Пример #1
0
def get_data(theme):
    data = {}
    data['chain'] = utils.get_theme_chain(theme, DIR)
    data['name'] = theme
    readme = utils.get_asset_path('README.md', data['chain'], _themes_dir=DIR)
    conf_sample = utils.get_asset_path('conf.py.sample', data['chain'], _themes_dir=DIR)
    if readme:
        data['readme'] = io.open(readme, 'r', encoding='utf-8').read()
    else:
        data['readme'] = 'No README.md file available.'

    if conf_sample:
        data['confpy'] = pygments.highlight(
            io.open(conf_sample, 'r', encoding='utf-8').read(),
            PythonLexer(), HtmlFormatter(cssclass='code'))
    else:
        data['confpy'] = None

    data['bootswatch'] = ('bootstrap' in data['chain'] or
        'bootstrap-jinja' in data['chain'] or
        'bootstrap3-jinja' in data['chain'] or
        'bootstrap3' in data['chain']) and \
        'bootstrap3-gradients' not in data['chain']
    data['engine'] = utils.get_template_engine(data['chain'], DIR)
    data['chain'] = data['chain'][::-1]

    data['allver'] = []
    for v in ALL_VERSIONS_SUPPORTED:
        if glob.glob('v{0}/*'.format(v)):
            data['allver'].append(v)

    return data
Пример #2
0
def get_data(theme):
    data = {}
    data['chain'] = utils.get_theme_chain(theme)
    data['name'] = theme
    readme = utils.get_asset_path('README.md', data['chain'])
    conf_sample = utils.get_asset_path('conf.py.sample', data['chain'])
    if readme:
        data['readme'] = codecs.open(readme, 'r', 'utf8').read()
    else:
        data['readme'] = 'No README.md file available.'

    if conf_sample:
        data['confpy'] = pygments.highlight(
            codecs.open(conf_sample, 'r', 'utf8').read(),
            PythonLexer(), HtmlFormatter(cssclass='code'))
    else:
        data['confpy'] = None

    data['bootswatch'] = ('bootstrap' in data['chain'] or
        'bootstrap-jinja' in data['chain'] or
        'bootstrap3-jinja' in data['chain'] or
        'bootstrap3' in data['chain']) and \
        'bootstrap3-gradients' not in data['chain']
    data['engine'] = utils.get_template_engine(data['chain'])
    data['chain'] = data['chain'][::-1]
    return data
def build_demo(theme, themes_dir, demo_source, demo_destination):
    demo_destination = os.path.abspath(demo_destination)
    if os.path.isdir(demo_source):
        shutil.rmtree(demo_source)
    if os.path.isdir(demo_destination):
        shutil.rmtree(demo_destination)
    sys.stderr.write('=> Building {}...\n'.format(theme))
    subprocess.check_call(["nikola", "init", "-qd", demo_source], stdout=subprocess.PIPE)
    os.symlink(os.path.abspath(themes_dir), os.path.abspath("/".join([demo_source, "themes"])))

    conf_path = "/".join([demo_source, "conf.py"])
    book_path = "/".join([demo_source, "templates", "book.tmpl"])
    lorem_path = "/".join([demo_source, "posts", "lorem-ipsum.rst"])
    # Get custom required settings from the theme
    themes = utils.get_theme_chain(theme, themes_dirs=[themes_dir, 'themes'])
    engine_path = utils.get_asset_path('engine', themes)
    extra_conf_path = utils.get_asset_path('conf.py.sample', themes)
    extra_conf = ''
    if extra_conf_path:
        extra_conf = io.open(extra_conf_path, 'r', encoding="utf-8").read()
    if engine_path:
        engine = io.open(engine_path, 'r', encoding="utf-8").read().strip()
        if engine == 'jinja':
            shutil.copy('book-jinja.tmpl', book_path)

    with io.open(conf_path, "a", encoding="utf-8") as conf:
        conf.write(u"\n\nTHEME = '{0}'\nUSE_BUNDLES = False\nOUTPUT_FOLDER = '{1}'\nSOCIAL_BUTTONS_CODE = ''\nUSE_BASE_TAG = False\n\n{2}\n".format(theme, demo_destination, extra_conf))

    shutil.copy(LOREM_BASE, lorem_path)

    with cd(demo_source):
        subprocess.check_call(["nikola", "build"], stdout=subprocess.PIPE)

    sys.stderr.write('=> Done building {}\n'.format(theme))
Пример #4
0
def is_asset_duplicated(path, themes):
    # First get the path for the asset with whole theme chain
    p1 = utils.get_asset_path(path, themes)
    # Get the path for asset with truncated theme chain
    p2 = utils.get_asset_path(path, themes[1:])
    # Compare
    if p1 and p2:
        return filecmp.cmp(p1, p2, False), p1, p2
    else:
        return False, p1, p2
Пример #5
0
def is_asset_duplicated(path, themes):
    # First get the path for the asset with whole theme chain
    p1 = utils.get_asset_path(path, themes, _themes_dir="v7")
    # Get the path for asset with truncated theme chain
    p2 = utils.get_asset_path(path, themes[1:], _themes_dir="v7")
    # README.md is ok to duplicate
    if "README.md" in path:
        return False, p1, p2
    # Compare
    if p1 and p2:
        return filecmp.cmp(p1, p2, False), p1, p2
    else:
        return False, p1, p2
Пример #6
0
def is_asset_duplicated(path, themes):
    # First get the path for the asset with whole theme chain
    p1 = utils.get_asset_path(path, themes)
    # Get the path for asset with truncated theme chain
    p2 = utils.get_asset_path(path, themes[1:])
    # Ignore some files that may be duplicated
    if any(i in path for i in ('README.md', 'engine', 'AUTHORS.txt', 'conf.py.sample')):
        return False, p1, p2
    # Compare
    if p1 and p2:
        return filecmp.cmp(p1, p2, False), p1, p2
    else:
        return False, p1, p2
Пример #7
0
    def new_theme(self, name, engine, parent):
        """Create a new theme."""
        base = 'themes'
        themedir = os.path.join(base, name)
        LOGGER.info("Creating theme {0} with parent {1} and engine {2} in {3}".format(name, parent, engine, themedir))
        if not os.path.exists(base):
            os.mkdir(base)
            LOGGER.info("Created directory {0}".format(base))

        # Check if engine and parent match
        engine_file = utils.get_asset_path('engine', utils.get_theme_chain(parent, self.site.themes_dirs))
        with io.open(engine_file, 'r', encoding='utf-8') as fh:
            parent_engine = fh.read().strip()

        if parent_engine != engine:
            LOGGER.error("Cannot use engine {0} because parent theme '{1}' uses {2}".format(engine, parent, parent_engine))
            return 2

        # Create theme
        if not os.path.exists(themedir):
            os.mkdir(themedir)
            LOGGER.info("Created directory {0}".format(themedir))
        else:
            LOGGER.error("Theme already exists")
            return 2

        with io.open(os.path.join(themedir, 'parent'), 'w', encoding='utf-8') as fh:
            fh.write(parent + '\n')
            LOGGER.info("Created file {0}".format(os.path.join(themedir, 'parent')))
        with io.open(os.path.join(themedir, 'engine'), 'w', encoding='utf-8') as fh:
            fh.write(engine + '\n')
            LOGGER.info("Created file {0}".format(os.path.join(themedir, 'engine')))

        LOGGER.info("Theme {0} created successfully.".format(themedir))
        LOGGER.notice('Remember to set THEME="{0}" in conf.py to use this theme.'.format(name))
Пример #8
0
def sanity_check(theme=None):
    if theme is None:  # Check them all
        for theme in theme_list():
            sanity_check(theme)
        return
    themes = utils.get_theme_chain(theme, themes_dirs=['v7'])
    themes_bn = [os.path.basename(i) for i in themes]
    engine = utils.get_template_engine(themes)

    # Inheritance checks

    # All themes must inherit from base
    if themes_bn[-1] != 'base':
        error("theme {0} doesn't inherit from base".format(theme))
    # All jinja themes must inherit from base-jinja
    if engine == "jinja" and "base-jinja" not in themes_bn:
        error("theme {0} is jinja-based and doesn't inherit from base-jinja".format(theme))
    # All mako themes must NOT inherit from base-jinja
    if engine == "mako" and "base-jinja" in themes_bn:
        error("theme {0} is mako-based and inherits from base-jinja".format(theme))

    # Detect exact asset duplication in theme chain
    for root, dirs, files in os.walk("v7/"+theme):
        for f in files:
            path = "/".join([root, f])
            asset = path.split("/", 2)[-1]
            r, p1, p2 = is_asset_duplicated(asset, themes)
            if r:
                error("duplicated asset: {0} {1}".format(p1, p2))

    # Detect deprecated names and anonymous namespaces
    for root, dirs, files in os.walk("v7/"+theme+"/templates"):
        for f in files:
            path = "/".join([root, f])
            with io.open(path, "r", encoding="utf-8") as inf:
                data = inf.read()
            for k, exceptions in blacklist:
                if k in data and f not in exceptions:
                    error("theme '{0}' contains deprecated name '{1}' in {2}".format(theme, k, path))

    # Ensure the theme has a README.md
    if utils.get_asset_path('README.md', themes) is None:
        error("theme '{0}' has no README.md".format(theme))

    # Ensure the theme has a meta file
    if utils.get_asset_path(theme + '.theme', themes) is None:
        warning("theme '{0}' has no {0}.theme meta file".format(theme))
Пример #9
0
    def gen_tasks(self):
        """Generate CSS out of LESS sources."""

        kw = {
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'themes': self.site.THEMES,
        }

        # Find where in the theme chain we define the LESS targets
        # There can be many *.less in the folder, but we only will build
        # the ones listed in less/targets
        targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
        try:
            with codecs.open(targets_path, "rb", "utf-8") as inf:
                targets = [x.strip() for x in inf.readlines()]
        except Exception:
            targets = []

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
            for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
                task['basename'] = 'prepare_less_sources'
                yield task

        # Build targets and write CSS files
        base_path = utils.get_theme_path(self.site.THEMES[0])
        dst_dir = os.path.join(self.site.config['OUTPUT_FOLDER'], 'assets', 'css')
        # Make everything depend on all sources, rough but enough
        deps = glob.glob(os.path.join(
            base_path,
            self.sources_folder,
            "*{0}".format(self.sources_ext)))

        def compile_target(target, dst):
            utils.makedirs(dst_dir)
            src = os.path.join(kw['cache_folder'], self.sources_folder, target)
            try:
                compiled = subprocess.check_output([self.compiler_name, src])
            except OSError:
                utils.req_missing([self.compiler_name],
                                  'build LESS files (and use this theme)',
                                  False, False)
            with open(dst, "wb+") as outf:
                outf.write(compiled)

        yield self.group_task()

        for target in targets:
            dst = os.path.join(dst_dir, target.replace(self.sources_ext, ".css"))
            yield {
                'basename': self.name,
                'name': dst,
                'targets': [dst],
                'file_dep': deps,
                'task_dep': ['prepare_less_sources'],
                'actions': ((compile_target, [target, dst]), ),
                'uptodate': [utils.config_changed(kw)],
                'clean': True
            }
Пример #10
0
    def gen_tasks(self):
        """Bundle assets using WebAssets."""

        kw = {
            'filters': self.site.config['FILTERS'],
            'output_folder': self.site.config['OUTPUT_FOLDER'],
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'theme_bundles': get_theme_bundles(self.site.THEMES),
            'themes': self.site.THEMES,
            'files_folders': self.site.config['FILES_FOLDERS'],
            'code_color_scheme': self.site.config['CODE_COLOR_SCHEME'],
        }

        def build_bundle(output, inputs):
            out_dir = os.path.join(kw['output_folder'],
                                   os.path.dirname(output))
            inputs = [i for i in inputs if os.path.isfile(
                os.path.join(out_dir, i))]
            cache_dir = os.path.join(kw['cache_folder'], 'webassets')
            utils.makedirs(cache_dir)
            env = webassets.Environment(out_dir, os.path.dirname(output),
                                        cache=cache_dir)
            if inputs:
                bundle = webassets.Bundle(*inputs, output=os.path.basename(output))
                env.register(output, bundle)
                # This generates the file
                env[output].urls()
            else:
                with open(os.path.join(out_dir, os.path.basename(output)), 'wb+'):
                    pass  # Create empty file

        yield self.group_task()
        if (webassets is not None and self.site.config['USE_BUNDLES'] is not
                False):
            for name, _files in kw['theme_bundles'].items():
                output_path = os.path.join(kw['output_folder'], name)
                dname = os.path.dirname(name)
                files = []
                for fname in _files:
                    # paths are relative to dirname
                    files.append(os.path.join(dname, fname))
                file_dep = [os.path.join(kw['output_folder'], fname)
                            for fname in files if
                            utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS'])]
                task = {
                    'file_dep': list(file_dep),
                    'task_dep': ['copy_assets'],
                    'basename': str(self.name),
                    'name': str(output_path),
                    'actions': [(build_bundle, (name, files))],
                    'targets': [output_path],
                    'uptodate': [
                        utils.config_changed({
                            1: kw,
                            2: file_dep
                        })],
                    'clean': True,
                }
                yield utils.apply_filters(task, kw['filters'])
Пример #11
0
    def gen_tasks(self):
        """Bundle assets using WebAssets."""

        kw = {
            'filters': self.site.config['FILTERS'],
            'output_folder': self.site.config['OUTPUT_FOLDER'],
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'theme_bundles': get_theme_bundles(self.site.THEMES),
            'themes': self.site.THEMES,
            'files_folders': self.site.config['FILES_FOLDERS'],
            'code_color_scheme': self.site.config['CODE_COLOR_SCHEME'],
        }

        def build_bundle(output, inputs):
            out_dir = os.path.join(kw['output_folder'],
                                   os.path.dirname(output))
            inputs = [i for i in inputs if os.path.isfile(
                os.path.join(out_dir, i))]
            cache_dir = os.path.join(kw['cache_folder'], 'webassets')
            if not os.path.isdir(cache_dir):
                os.makedirs(cache_dir)
            env = webassets.Environment(out_dir, os.path.dirname(output),
                                        cache=cache_dir)
            bundle = webassets.Bundle(*inputs, output=os.path.basename(output))
            env.register(output, bundle)
            # This generates the file
            env[output].urls()

        flag = False
        if (webassets is not None and self.site.config['USE_BUNDLES'] is not
                False):
            for name, files in kw['theme_bundles'].items():
                output_path = os.path.join(kw['output_folder'], name)
                dname = os.path.dirname(name)
                file_dep = [utils.get_asset_path(
                    os.path.join(dname, fname), kw['themes'],
                    kw['files_folders'])
                    for fname in files
                ]
                file_dep = filter(None, file_dep)  # removes missing files
                task = {
                    'file_dep': file_dep,
                    'basename': str(self.name),
                    'name': str(output_path),
                    'actions': [(build_bundle, (name, files))],
                    'targets': [output_path],
                    'uptodate': [utils.config_changed(kw)],
                    'clean': True,
                }
                flag = True
                yield utils.apply_filters(task, kw['filters'])
        if flag is False:  # No page rendered, yield a dummy task
            yield {
                'basename': self.name,
                'uptodate': [True],
                'name': 'None',
                'actions': [],
            }
Пример #12
0
def test_get_asset_path():
    assert get_asset_path('assets/css/nikola_rst.css',
                          get_theme_chain('bootstrap4', ['themes'])).replace(
        '\\', '/').endswith('nikola/data/themes/base/assets/css/nikola_rst.css')
    assert get_asset_path('assets/css/theme.css',
                          get_theme_chain('bootstrap4', ['themes'])).replace(
        '\\', '/').endswith(
        'nikola/data/themes/bootstrap4/assets/css/theme.css')
    assert get_asset_path(
        'nikola.py', get_theme_chain('bootstrap4', ['themes']),
        {'nikola': ''}).replace(
        '\\', '/').endswith('nikola/nikola.py')
    assert get_asset_path('nikola.py', get_theme_chain(
        'bootstrap4', ['themes']), {'nikola': 'nikola'}) is None
    assert get_asset_path(
        'nikola/nikola.py', get_theme_chain('bootstrap4', ['themes']),
        {'nikola': 'nikola'}).replace(
        '\\', '/').endswith('nikola/nikola.py')
Пример #13
0
    def gen_tasks(self):
        """Generate CSS out of LESS sources."""

        kw = {"cache_folder": self.site.config["CACHE_FOLDER"], "themes": self.site.THEMES}

        # Find where in the theme chain we define the LESS targets
        # There can be many *.less in the folder, but we only will build
        # the ones listed in less/targets
        targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
        try:
            with codecs.open(targets_path, "rb", "utf-8") as inf:
                targets = [x.strip() for x in inf.readlines()]
        except Exception:
            targets = []

        for theme_name in kw["themes"]:
            src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
            for task in utils.copy_tree(src, os.path.join(kw["cache_folder"], self.sources_folder)):
                # task['basename'] = self.name
                yield task

        # Build targets and write CSS files
        base_path = utils.get_theme_path(self.site.THEMES[0])
        dst_dir = os.path.join(self.site.config["OUTPUT_FOLDER"], "assets", "css")
        # Make everything depend on all sources, rough but enough
        deps = glob.glob(os.path.join(base_path, self.sources_folder, "*{0}".format(self.sources_ext)))

        def compile_target(target, dst):
            if not os.path.isdir(dst_dir):
                os.makedirs(dst_dir)
            src = os.path.join(kw["cache_folder"], self.sources_folder, target)
            compiled = subprocess.check_output([self.compiler_name, src])
            with open(dst, "wb+") as outf:
                outf.write(compiled)

        for target in targets:
            dst = os.path.join(dst_dir, target.replace(self.sources_ext, ".css"))
            yield {
                "basename": self.name,
                "name": dst,
                "targets": [dst],
                "file_dep": deps,
                "actions": ((compile_target, [target, dst]),),
                "uptodate": [utils.config_changed(kw)],
                "clean": True,
            }

        if not targets:
            yield {"basename": self.name, "actions": []}
Пример #14
0
    def gen_tasks(self):
        """Bundle assets using WebAssets."""

        kw = {
            "filters": self.site.config["FILTERS"],
            "output_folder": self.site.config["OUTPUT_FOLDER"],
            "cache_folder": self.site.config["CACHE_FOLDER"],
            "theme_bundles": get_theme_bundles(self.site.THEMES),
            "themes": self.site.THEMES,
            "files_folders": self.site.config["FILES_FOLDERS"],
            "code_color_scheme": self.site.config["CODE_COLOR_SCHEME"],
        }

        def build_bundle(output, inputs):
            out_dir = os.path.join(kw["output_folder"], os.path.dirname(output))
            inputs = [i for i in inputs if os.path.isfile(os.path.join(out_dir, i))]
            cache_dir = os.path.join(kw["cache_folder"], "webassets")
            if not os.path.isdir(cache_dir):
                os.makedirs(cache_dir)
            env = webassets.Environment(out_dir, os.path.dirname(output), cache=cache_dir)
            bundle = webassets.Bundle(*inputs, output=os.path.basename(output))
            env.register(output, bundle)
            # This generates the file
            env[output].urls()

        flag = False
        if webassets is not None and self.site.config["USE_BUNDLES"] is not False:
            for name, files in kw["theme_bundles"].items():
                output_path = os.path.join(kw["output_folder"], name)
                dname = os.path.dirname(name)
                file_dep = [
                    utils.get_asset_path(os.path.join(dname, fname), kw["themes"], kw["files_folders"])
                    for fname in files
                ]
                file_dep = filter(None, file_dep)  # removes missing files
                task = {
                    "file_dep": file_dep,
                    "basename": str(self.name),
                    "name": str(output_path),
                    "actions": [(build_bundle, (name, files))],
                    "targets": [output_path],
                    "uptodate": [utils.config_changed(kw)],
                }
                flag = True
                yield utils.apply_filters(task, kw["filters"])
        if flag is False:  # No page rendered, yield a dummy task
            yield {"basename": self.name, "uptodate": [True], "name": "None", "actions": []}
Пример #15
0
def sanity_check(theme=None):
    if theme is None:  # Check them all
        for theme in theme_list():
            sanity_check(theme)
        return
    themes = utils.get_theme_chain(theme, themes_dirs=['v7'])
    themes_bn = [os.path.basename(i) for i in themes]
    engine = utils.get_template_engine(themes)

    # Inheritance checks

    # All themes must inherit from base
    if themes_bn[-1] != 'base':
        error("theme {0} doesn't inherit from base".format(theme))
    # All jinja themes must inherit from base-jinja
    if engine == "jinja" and "base-jinja" not in themes_bn:
        error("theme {0} is jinja-based and doesn't inherit from base-jinja".
              format(theme))
    # All mako themes must NOT inherit from base-jinja
    if engine == "mako" and "base-jinja" in themes_bn:
        error("theme {0} is mako-based and inherits from base-jinja".format(
            theme))

    # Detect exact asset duplication in theme chain
    for root, dirs, files in os.walk("v7/" + theme):
        for f in files:
            path = "/".join([root, f])
            asset = path.split("/", 2)[-1]
            r, p1, p2 = is_asset_duplicated(asset, themes)
            if r:
                error("duplicated asset: {0} {1}".format(p1, p2))

    # Detect deprecated names and anonymous namespaces
    for root, dirs, files in os.walk("v7/" + theme + "/templates"):
        for f in files:
            path = "/".join([root, f])
            with io.open(path, "r", encoding="utf-8") as inf:
                data = inf.read()
            for k, exceptions in blacklist:
                if k in data and f not in exceptions:
                    error("theme '{0}' contains deprecated name '{1}' in {2}".
                          format(theme, k, path))

    # Ensure the theme has a README.md
    if utils.get_asset_path('README.md', themes) is None:
        error("theme '{0}' has no README.md".format(theme))
Пример #16
0
    def new_theme(self, name, engine, parent):
        """Create a new theme."""
        base = 'themes'
        themedir = os.path.join(base, name)
        LOGGER.info(
            "Creating theme {0} with parent {1} and engine {2} in {3}".format(
                name, parent, engine, themedir))
        if not os.path.exists(base):
            os.mkdir(base)
            LOGGER.info("Created directory {0}".format(base))

        # Check if engine and parent match
        engine_file = utils.get_asset_path(
            'engine', utils.get_theme_chain(parent, self.site.themes_dirs))
        with io.open(engine_file, 'r', encoding='utf-8') as fh:
            parent_engine = fh.read().strip()

        if parent_engine != engine:
            LOGGER.error(
                "Cannot use engine {0} because parent theme '{1}' uses {2}".
                format(engine, parent, parent_engine))
            return 2

        # Create theme
        if not os.path.exists(themedir):
            os.mkdir(themedir)
            LOGGER.info("Created directory {0}".format(themedir))
        else:
            LOGGER.error("Theme already exists")
            return 2

        with io.open(os.path.join(themedir, 'parent'), 'w',
                     encoding='utf-8') as fh:
            fh.write(parent + '\n')
            LOGGER.info("Created file {0}".format(
                os.path.join(themedir, 'parent')))
        with io.open(os.path.join(themedir, 'engine'), 'w',
                     encoding='utf-8') as fh:
            fh.write(engine + '\n')
            LOGGER.info("Created file {0}".format(
                os.path.join(themedir, 'engine')))

        LOGGER.info("Theme {0} created successfully.".format(themedir))
        LOGGER.notice(
            'Remember to set THEME="{0}" in conf.py to use this theme.'.format(
                name))
Пример #17
0
    def gen_tasks(self):
        """Generate a robots.txt file."""
        kw = {
            "base_url": self.site.config["BASE_URL"],
            "site_url": self.site.config["SITE_URL"],
            "output_folder": self.site.config["OUTPUT_FOLDER"],
            "files_folders": self.site.config["FILES_FOLDERS"],
            "robots_exclusions": self.site.config["ROBOTS_EXCLUSIONS"],
            "filters": self.site.config["FILTERS"],
        }

        sitemapindex_url = urljoin(kw["base_url"], "sitemapindex.xml")
        robots_path = os.path.join(kw["output_folder"], "robots.txt")

        def write_robots():
            if kw["site_url"] != urljoin(kw["site_url"], "/"):
                utils.LOGGER.warn("robots.txt not ending up in server root, will be useless")

            with io.open(robots_path, "w+", encoding="utf8") as outf:
                outf.write("Sitemap: {0}\n\n".format(sitemapindex_url))
                if kw["robots_exclusions"]:
                    outf.write("User-Agent: *\n")
                    for loc in kw["robots_exclusions"]:
                        outf.write("Disallow: {0}\n".format(loc))

        yield self.group_task()

        if not utils.get_asset_path("robots.txt", [], files_folders=kw["files_folders"], output_dir=False):
            yield utils.apply_filters(
                {
                    "basename": self.name,
                    "name": robots_path,
                    "targets": [robots_path],
                    "actions": [(write_robots)],
                    "uptodate": [utils.config_changed(kw, "nikola.plugins.task.robots")],
                    "clean": True,
                    "task_dep": ["sitemap"],
                },
                kw["filters"],
            )
        elif kw["robots_exclusions"]:
            utils.LOGGER.warn(
                "Did not generate robots.txt as one already exists in FILES_FOLDERS. ROBOTS_EXCLUSIONS will not have any affect on the copied file."
            )
        else:
            utils.LOGGER.debug("Did not generate robots.txt as one already exists in FILES_FOLDERS.")
Пример #18
0
    def gen_tasks(self):
        """Generate a robots.txt."""
        kw = {
            "base_url": self.site.config["BASE_URL"],
            "site_url": self.site.config["SITE_URL"],
            "output_folder": self.site.config["OUTPUT_FOLDER"],
            "files_folders": self.site.config['FILES_FOLDERS'],
            "robots_exclusions": self.site.config["ROBOTS_EXCLUSIONS"]
        }

        if kw["site_url"] != urljoin(kw["site_url"], "/"):
            utils.LOGGER.warn(
                'robots.txt not ending up in server root, will be useless')

        sitemapindex_url = urljoin(kw["base_url"], "sitemapindex.xml")
        robots_path = os.path.join(kw['output_folder'], "robots.txt")

        def write_robots():
            with codecs.open(robots_path, 'wb+', 'utf8') as outf:
                outf.write("Sitemap: {0}\n\n".format(sitemapindex_url))
                if kw["robots_exclusions"]:
                    outf.write("User-Agent: *\n")
                    for loc in kw["robots_exclusions"]:
                        outf.write("Disallow: {0}\n".format(loc))

        yield self.group_task()

        if not utils.get_asset_path("robots.txt", [],
                                    files_folders=kw["files_folders"]):
            yield {
                "basename": self.name,
                "name": robots_path,
                "targets": [robots_path],
                "actions": [(write_robots)],
                "uptodate": [utils.config_changed(kw)],
                "clean": True,
                "task_dep": ["sitemap"]
            }
        elif kw["robots_exclusions"]:
            utils.LOGGER.warn(
                'Did not generate robots.txt as one already exists in FILES_FOLDERS. ROBOTS_EXCLUSIONS will not have any affect on the copied fie.'
            )
        else:
            utils.LOGGER.debug(
                'Did not generate robots.txt as one already exists in FILES_FOLDERS.'
            )
Пример #19
0
def get_data(theme):
    data = {}
    data['name'] = theme
    readme = utils.get_asset_path('README', [theme])
    if readme:
        data['readme'] = open(readme).read()
    else:
        data['readme'] = 'No readme file available'
    data['chain'] = utils.get_theme_chain(theme)
    data['bootswatch'] = ('bootstrap' in data['chain'] or
        'bootstrap-jinja' in data['chain'] or
        'bootstrap3-jinja' in data['chain'] or
        'bootstrap3' in data['chain']) and \
        'bootstrap3-gradients' not in data['chain']
    data['engine'] = utils.get_template_engine(data['chain'])
    data['chain'] = data['chain'][::-1]
    return data
Пример #20
0
def init_theme(theme):
    t_path = "/".join(["sites", theme])
    o_path = os.path.abspath("/".join(["output", "v6", theme]))
    if os.path.isdir(t_path):
        shutil.rmtree(t_path)
    if os.path.isdir(o_path):
        shutil.rmtree(o_path)
    subprocess.check_call(["nikola", "init", "--demo", t_path], stdout=subprocess.PIPE)
    os.symlink(os.path.abspath("themes"), os.path.abspath("/".join([t_path, "themes"])))

    conf_path = "/".join([t_path,"conf.py"])
    # Get custom required settings from the theme
    themes = utils.get_theme_chain(theme)
    extra_conf_path = utils.get_asset_path('conf.py.sample', themes)
    extra_conf = ''
    if extra_conf_path:
        extra_conf = open(extra_conf_path, 'r').read()

    with codecs.open(conf_path, "a", "utf-8") as conf:
        conf.write("\n\n{2}\n\nTHEME = '{0}'\n\nOUTPUT_FOLDER = '{1}'\n\nSOCIAL_BUTTONS_CODE = ''\n".format(theme, o_path, extra_conf))
Пример #21
0
    def gen_tasks(self):
        """Generate a robots.txt."""
        kw = {
            "base_url": self.site.config["BASE_URL"],
            "site_url": self.site.config["SITE_URL"],
            "output_folder": self.site.config["OUTPUT_FOLDER"],
            "files_folders": self.site.config['FILES_FOLDERS'],
            "robots_exclusions": self.site.config["ROBOTS_EXCLUSIONS"]
        }

        sitemapindex_url = urljoin(kw["base_url"], "sitemapindex.xml")
        robots_path = os.path.join(kw['output_folder'], "robots.txt")

        def write_robots():
            if kw["site_url"] != urljoin(kw["site_url"], "/"):
                utils.LOGGER.warn('robots.txt not ending up in server root, will be useless')

            with codecs.open(robots_path, 'wb+', 'utf8') as outf:
                outf.write("Sitemap: {0}\n\n".format(sitemapindex_url))
                if kw["robots_exclusions"]:
                    outf.write("User-Agent: *\n")
                    for loc in kw["robots_exclusions"]:
                        outf.write("Disallow: {0}\n".format(loc))

        yield self.group_task()

        if not utils.get_asset_path("robots.txt", [], files_folders=kw["files_folders"]):
            yield {
                "basename": self.name,
                "name": robots_path,
                "targets": [robots_path],
                "actions": [(write_robots)],
                "uptodate": [utils.config_changed(kw)],
                "clean": True,
                "task_dep": ["sitemap"]
            }
        elif kw["robots_exclusions"]:
            utils.LOGGER.warn('Did not generate robots.txt as one already exists in FILES_FOLDERS. ROBOTS_EXCLUSIONS will not have any affect on the copied fie.')
        else:
            utils.LOGGER.debug('Did not generate robots.txt as one already exists in FILES_FOLDERS.')
Пример #22
0
def init_theme(theme):
    t_path = "/".join(["sites", theme])
    o_path = os.path.abspath("/".join(["output", "v7", theme]))
    if os.path.isdir(t_path):
        shutil.rmtree(t_path)
    if os.path.isdir(o_path):
        shutil.rmtree(o_path)
    subprocess.check_call(["nikola", "init", "-qd", t_path], stdout=subprocess.PIPE)
    os.symlink(os.path.abspath("v7"), os.path.abspath("/".join([t_path, "themes"])))

    conf_path = "/".join([t_path, "conf.py"])
    # Get custom required settings from the theme
    themes = utils.get_theme_chain(theme, _themes_dir="v7")
    extra_conf_path = utils.get_asset_path("conf.py.sample", themes, _themes_dir="v7")
    extra_conf = ""
    if extra_conf_path:
        extra_conf = io.open(extra_conf_path, "r", encoding="utf-8").read()

    with io.open(conf_path, "a", encoding="utf-8") as conf:
        conf.write(
            "\n\n{2}\n\nTHEME = '{0}'\n\nUSE_BUNDLES = False\n\nOUTPUT_FOLDER = '{1}'\n\nSOCIAL_BUTTONS_CODE = ''\nUSE_BASE_TAG = False\n".format(
                theme, o_path, extra_conf
            )
        )
Пример #23
0
def init_theme(theme):
    t_path = "/".join(["sites", theme])
    o_path = os.path.abspath("/".join(["output", "v7", theme]))
    if os.path.isdir(t_path):
        shutil.rmtree(t_path)
    if os.path.isdir(o_path):
        shutil.rmtree(o_path)
    subprocess.check_call(["nikola", "init", "-qd", t_path],
                          stdout=subprocess.PIPE)
    os.symlink(os.path.abspath("v7"),
               os.path.abspath("/".join([t_path, "themes"])))

    conf_path = "/".join([t_path, "conf.py"])
    # Get custom required settings from the theme
    themes = utils.get_theme_chain(theme, themes_dirs=['v7', 'themes'])
    extra_conf_path = utils.get_asset_path('conf.py.sample', themes)
    extra_conf = ''
    if extra_conf_path:
        extra_conf = io.open(extra_conf_path, 'r', encoding="utf-8").read()

    with io.open(conf_path, "a", encoding="utf-8") as conf:
        conf.write(
            u"\n\n{2}\n\nTHEME = '{0}'\n\nUSE_BUNDLES = False\n\nOUTPUT_FOLDER = '{1}'\n\nSOCIAL_BUTTONS_CODE = ''\nUSE_BASE_TAG = False\n"
            .format(theme, o_path, extra_conf))
Пример #24
0
    def gen_tasks(self):
        """Generate CSS out of LESS sources."""
        self.compiler_name = self.site.config['LESS_COMPILER']
        self.compiler_options = self.site.config['LESS_OPTIONS']

        kw = {
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'themes': self.site.THEMES,
        }
        tasks = {}

        # Find where in the theme chain we define the LESS targets
        # There can be many *.less in the folder, but we only will build
        # the ones listed in less/targets
        if os.path.isfile(os.path.join(self.sources_folder, "targets")):
            targets_path = os.path.join(self.sources_folder, "targets")
        else:
            targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
        try:
            with codecs.open(targets_path, "rb", "utf-8") as inf:
                targets = [x.strip() for x in inf.readlines()]
        except Exception:
            targets = []

        for task in utils.copy_tree(self.sources_folder, os.path.join(kw['cache_folder'], self.sources_folder)):
            if task['name'] in tasks:
                continue
            task['basename'] = 'prepare_less_sources'
            tasks[task['name']] = task
            yield task

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
            for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
                task['basename'] = 'prepare_less_sources'
                yield task

        # Build targets and write CSS files
        base_path = utils.get_theme_path(self.site.THEMES[0])
        dst_dir = os.path.join(self.site.config['OUTPUT_FOLDER'], 'assets', 'css')
        # Make everything depend on all sources, rough but enough
        deps = []
        if os.path.isfile(os.path.join(self.sources_folder, "targets")):
            deps += glob.glob(os.path.join(kw['cache_folder'], self.sources_folder,
                                           '*{0}'.format(self.sources_ext)))
        else:
            deps += glob.glob(os.path.join(base_path, self.sources_folder,
                                           '*{0}'.format(self.sources_ext)))

        def compile_target(target, dst):
            utils.makedirs(dst_dir)
            src = os.path.join(kw['cache_folder'], self.sources_folder, target)
            run_in_shell = sys.platform == 'win32'
            try:
                compiled = subprocess.check_output([self.compiler_name] + self.compiler_options + [src], shell=run_in_shell)
            except OSError:
                utils.req_missing([self.compiler_name],
                                  'build LESS files (and use this theme)',
                                  False, False)
            with open(dst, "wb+") as outf:
                outf.write(compiled)

        yield self.group_task()

        for target in targets:
            dst = os.path.join(dst_dir, target.replace(self.sources_ext, ".css"))
            yield {
                'basename': self.name,
                'name': dst,
                'targets': [dst],
                'file_dep': deps,
                'task_dep': ['prepare_less_sources'],
                'actions': ((compile_target, [target, dst]), ),
                'uptodate': [utils.config_changed(kw)],
                'clean': True
            }
Пример #25
0
    def gen_tasks(self):
        """Bundle assets."""
        kw = {
            'filters': self.site.config['FILTERS'],
            'output_folder': self.site.config['OUTPUT_FOLDER'],
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'theme_bundles': get_theme_bundles(self.site.THEMES),
            'themes': self.site.THEMES,
            'files_folders': self.site.config['FILES_FOLDERS'],
            'code_color_scheme': self.site.config['CODE_COLOR_SCHEME'],
        }

        def build_bundle(output, inputs):
            out_dir = os.path.join(kw['output_folder'],
                                   os.path.dirname(output))
            inputs = [
                os.path.join(out_dir, os.path.relpath(i, out_dir))
                for i in inputs if os.path.isfile(i)
            ]
            with open(os.path.join(out_dir, os.path.basename(output)),
                      'wb+') as out_fh:
                for i in inputs:
                    with open(i, 'rb') as in_fh:
                        shutil.copyfileobj(in_fh, out_fh)
                    out_fh.write(b'\n')

        yield self.group_task()

        if self.site.config['USE_BUNDLES']:
            for name, _files in kw['theme_bundles'].items():
                output_path = os.path.join(kw['output_folder'], name)
                dname = os.path.dirname(name)
                files = []
                for fname in _files:
                    # paths are relative to dirname
                    files.append(os.path.join(dname, fname))
                file_dep = [
                    os.path.join(kw['output_folder'], fname) for fname in files
                    if utils.get_asset_path(fname,
                                            self.site.THEMES,
                                            self.site.config['FILES_FOLDERS'],
                                            output_dir=kw['output_folder'])
                    or fname == os.path.join('assets', 'css', 'code.css')
                ]
                # code.css will be generated by us if it does not exist in
                # FILES_FOLDERS or theme assets.  It is guaranteed that the
                # generation will happen before this task.
                task = {
                    'file_dep':
                    list(file_dep),
                    'task_dep': ['copy_assets', 'copy_files'],
                    'basename':
                    str(self.name),
                    'name':
                    str(output_path),
                    'actions': [(build_bundle, (name, file_dep))],
                    'targets': [output_path],
                    'uptodate': [
                        utils.config_changed({
                            1: kw,
                            2: file_dep
                        }, 'nikola.plugins.task.bundles')
                    ],
                    'clean':
                    True,
                }
                yield utils.apply_filters(task, kw['filters'])
Пример #26
0
    def gen_tasks(self):
        """Generate CSS out of Sass sources."""
        self.logger = utils.get_logger('build_sass', self.site.loghandlers)
        self.compiler_name = self.site.config['SASS_COMPILER']
        self.compiler_options = self.site.config['SASS_OPTIONS']

        kw = {
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'themes': self.site.THEMES,
        }
        tasks = {}

        # Find where in the theme chain we define the Sass targets
        # There can be many *.sass/*.scss in the folder, but we only
        # will build the ones listed in sass/targets
        if os.path.isfile(os.path.join(self.sources_folder, "targets")):
            targets_path = os.path.join(self.sources_folder, "targets")
        else:
            targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
        try:
            with codecs.open(targets_path, "rb", "utf-8") as inf:
                targets = [x.strip() for x in inf.readlines()]
        except Exception:
            targets = []

        for task in utils.copy_tree(self.sources_folder, os.path.join(kw['cache_folder'], self.sources_folder)):
            if task['name'] in tasks:
                continue
            task['basename'] = 'prepare_sass_sources'
            tasks[task['name']] = task
            yield task

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
            for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
                if task['name'] in tasks:
                    continue
                task['basename'] = 'prepare_sass_sources'
                tasks[task['name']] = task
                yield task

        # Build targets and write CSS files
        base_path = utils.get_theme_path(self.site.THEMES[0])
        dst_dir = os.path.join(self.site.config['OUTPUT_FOLDER'], 'assets', 'css')
        # Make everything depend on all sources, rough but enough
        deps = []
        for ext in self.sources_ext:
            if os.path.isfile(os.path.join(self.sources_folder, "targets")):
                deps += glob.glob(os.path.join(kw['cache_folder'], self.sources_folder,
                                               '*{0}'.format(ext)))
            else:
                deps += glob.glob(os.path.join(base_path, self.sources_folder,
                                               '*{0}'.format(ext)))

        def compile_target(target, dst):
            utils.makedirs(dst_dir)
            run_in_shell = sys.platform == 'win32'
            src = os.path.join(kw['cache_folder'], self.sources_folder, target)
            try:
                compiled = subprocess.check_output([self.compiler_name] + self.compiler_options + [src], shell=run_in_shell)
            except OSError:
                utils.req_missing([self.compiler_name],
                                  'build Sass files (and use this theme)',
                                  False, False)
            with open(dst, "wb+") as outf:
                outf.write(compiled)

        yield self.group_task()

        # We can have file conflicts.  This is a way to prevent them.
        # I orignally wanted to use sets and their cannot-have-duplicates
        # magic, but I decided not to do this so we can show the user
        # what files were problematic.
        # If we didn’t do this, there would be a cryptic message from doit
        # instead.
        seennames = {}
        for target in targets:
            base = os.path.splitext(target)[0]
            dst = os.path.join(dst_dir, base + ".css")

            if base in seennames:
                self.logger.error(
                    'Duplicate filenames for Sass compiled files: {0} and '
                    '{1} (both compile to {2})'.format(
                        seennames[base], target, base + ".css"))
            else:
                seennames.update({base: target})

            yield {
                'basename': self.name,
                'name': dst,
                'targets': [dst],
                'file_dep': deps,
                'task_dep': ['prepare_sass_sources'],
                'actions': ((compile_target, [target, dst]), ),
                'uptodate': [utils.config_changed(kw)],
                'clean': True
            }
Пример #27
0
    def gen_tasks(self):
        """Create tasks to copy the assets of the whole theme chain.

        If a file is present on two themes, use the version
        from the "youngest" theme.
        """
        kw = {
            "themes": self.site.THEMES,
            "files_folders": self.site.config['FILES_FOLDERS'],
            "output_folder": self.site.config['OUTPUT_FOLDER'],
            "filters": self.site.config['FILTERS'],
            "code_color_scheme": self.site.config['CODE_COLOR_SCHEME'],
            "code.css_selectors": 'pre.code',
            "code.css_head": '/* code.css file generated by Nikola */\n',
            "code.css_close": "\ntable.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}\n",
        }
        tasks = {}
        code_css_path = os.path.join(kw['output_folder'], 'assets', 'css', 'code.css')
        code_css_input = utils.get_asset_path('assets/css/code.css',
                                              themes=kw['themes'],
                                              files_folders=kw['files_folders'], output_dir=None)
        yield self.group_task()

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), 'assets')
            dst = os.path.join(kw['output_folder'], 'assets')
            for task in utils.copy_tree(src, dst):
                if task['name'] in tasks:
                    continue
                tasks[task['name']] = task
                task['uptodate'] = [utils.config_changed(kw, 'nikola.plugins.task.copy_assets')]
                task['basename'] = self.name
                if code_css_input:
                    if 'file_dep' not in task:
                        task['file_dep'] = []
                    task['file_dep'].append(code_css_input)
                yield utils.apply_filters(task, kw['filters'])

        # Check whether or not there is a code.css file around.
        if not code_css_input and kw['code_color_scheme']:
            def create_code_css():
                from pygments.formatters import get_formatter_by_name
                formatter = get_formatter_by_name('html', style=kw["code_color_scheme"])
                utils.makedirs(os.path.dirname(code_css_path))
                with io.open(code_css_path, 'w+', encoding='utf8') as outf:
                    outf.write(kw["code.css_head"])
                    outf.write(formatter.get_style_defs(kw["code.css_selectors"]))
                    outf.write(kw["code.css_close"])

            if os.path.exists(code_css_path):
                with io.open(code_css_path, 'r', encoding='utf-8') as fh:
                    testcontents = fh.read(len(kw["code.css_head"])) == kw["code.css_head"]
            else:
                testcontents = False

            task = {
                'basename': self.name,
                'name': code_css_path,
                'targets': [code_css_path],
                'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.copy_assets'), testcontents],
                'actions': [(create_code_css, [])],
                'clean': True,
            }
            yield utils.apply_filters(task, kw['filters'])
Пример #28
0
    def _execute(self, options, args):
        """Given a swatch name and a parent theme, creates a custom theme."""
        name = options['name']
        swatch = options['swatch']
        if not swatch:
            LOGGER.error('The -s option is mandatory')
            return 1
        parent = options['parent']
        version = '4'

        # Check which Bootstrap version to use
        themes = utils.get_theme_chain(parent, self.site.themes_dirs)
        if _check_for_theme('bootstrap', themes) or _check_for_theme('bootstrap-jinja', themes):
            version = '2'
        elif _check_for_theme('bootstrap3', themes) or _check_for_theme('bootstrap3-jinja', themes):
            version = '3'
        elif _check_for_theme('bootstrap4', themes) or _check_for_theme('bootstrap4-jinja', themes):
            version = '4'
        elif not _check_for_theme('bootstrap4', themes) and not _check_for_theme('bootstrap4-jinja', themes):
            LOGGER.warning(
                '"subtheme" only makes sense for themes that use bootstrap')
        elif _check_for_theme('bootstrap3-gradients', themes) or _check_for_theme('bootstrap3-gradients-jinja', themes):
            LOGGER.warning(
                '"subtheme" doesn\'t work well with the bootstrap3-gradients family')

        LOGGER.info("Creating '{0}' theme from '{1}' and '{2}'".format(
            name, swatch, parent))
        utils.makedirs(os.path.join('themes', name, 'assets', 'css'))
        for fname in ('bootstrap.min.css', 'bootstrap.css'):
            if swatch in [
                    'bubblegum', 'business-tycoon', 'charming', 'daydream',
                    'executive-suite', 'good-news', 'growth', 'harbor', 'hello-world',
                    'neon-glow', 'pleasant', 'retro', 'vibrant-sea', 'wizardry']:  # Hackerthemes
                LOGGER.info(
                    'Hackertheme-based subthemes often require you use a custom font for full effect.')
                if version != '4':
                    LOGGER.error(
                        'The hackertheme subthemes are only available for Bootstrap 4.')
                    return 1
                if fname == 'bootstrap.css':
                    url = 'https://raw.githubusercontent.com/HackerThemes/theme-machine/master/dist/{swatch}/css/bootstrap4-{swatch}.css'.format(
                        swatch=swatch)
                else:
                    url = 'https://raw.githubusercontent.com/HackerThemes/theme-machine/master/dist/{swatch}/css/bootstrap4-{swatch}.min.css'.format(
                        swatch=swatch)
            else:  # Bootswatch
                url = 'https://bootswatch.com'
                if version:
                    url += '/' + version
                url = '/'.join((url, swatch, fname))
            LOGGER.info("Downloading: " + url)
            r = requests.get(url)
            if r.status_code > 299:
                LOGGER.error('Error {} getting {}', r.status_code, url)
                return 1
            data = r.text

            with open(os.path.join('themes', name, 'assets', 'css', fname),
                      'w+') as output:
                output.write(data)

        with open(os.path.join('themes', name, '%s.theme' % name), 'w+') as output:
            parent_theme_data_path = utils.get_asset_path(
                '%s.theme' % parent, themes)
            cp = configparser.ConfigParser()
            cp.read(parent_theme_data_path)
            cp['Theme']['parent'] = parent
            cp['Family'] = {'family': cp['Family']['family']}
            cp.write(output)

        LOGGER.info(
            'Theme created.  Change the THEME setting to "{0}" to use it.'.format(name))
Пример #29
0
    def gen_tasks(self):
        """Generate CSS out of Sass sources."""
        self.logger = utils.get_logger('build_sass', self.site.loghandlers)

        kw = {
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'themes': self.site.THEMES,
        }

        # Find where in the theme chain we define the Sass targets
        # There can be many *.sass/*.scss in the folder, but we only
        # will build the ones listed in sass/targets
        targets_path = utils.get_asset_path(
            os.path.join(self.sources_folder, "targets"), self.site.THEMES)
        try:
            with codecs.open(targets_path, "rb", "utf-8") as inf:
                targets = [x.strip() for x in inf.readlines()]
        except Exception:
            targets = []

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name),
                               self.sources_folder)
            for task in utils.copy_tree(
                    src, os.path.join(kw['cache_folder'],
                                      self.sources_folder)):
                task['basename'] = 'prepare_sass_sources'
                yield task

        # Build targets and write CSS files
        base_path = utils.get_theme_path(self.site.THEMES[0])
        dst_dir = os.path.join(self.site.config['OUTPUT_FOLDER'], 'assets',
                               'css')
        # Make everything depend on all sources, rough but enough
        deps = glob.glob(
            os.path.join(base_path, self.sources_folder,
                         *("*{0}".format(ext) for ext in self.sources_ext)))

        def compile_target(target, dst):
            utils.makedirs(dst_dir)
            src = os.path.join(kw['cache_folder'], self.sources_folder, target)
            compiled = subprocess.check_output([self.compiler_name, src])
            with open(dst, "wb+") as outf:
                outf.write(compiled)

        yield self.group_task()

        # We can have file conflicts.  This is a way to prevent them.
        # I orignally wanted to use sets and their cannot-have-duplicates
        # magic, but I decided not to do this so we can show the user
        # what files were problematic.
        # If we didn’t do this, there would be a cryptic message from doit
        # instead.
        seennames = {}
        for target in targets:
            base = os.path.splitext(target)[0]
            dst = os.path.join(dst_dir, base + ".css")

            if base in seennames:
                self.logger.error(
                    'Duplicate filenames for SASS compiled files: {0} and '
                    '{1} (both compile to {2})'.format(seennames[base], target,
                                                       base + ".css"))
            else:
                seennames.update({base: target})

            yield {
                'basename': self.name,
                'name': dst,
                'targets': [dst],
                'file_dep': deps,
                'task_dep': ['prepare_sass_sources'],
                'actions': ((compile_target, [target, dst]), ),
                'uptodate': [utils.config_changed(kw)],
                'clean': True
            }
Пример #30
0
    def gen_tasks(self):
        """Generate CSS out of Sass sources."""
        self.logger = utils.get_logger('build_sass', utils.STDERR_HANDLER)
        self.compiler_name = self.site.config['SASS_COMPILER']
        self.compiler_options = self.site.config['SASS_OPTIONS']

        kw = {
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'themes': self.site.THEMES,
        }
        tasks = {}

        # Find where in the theme chain we define the Sass targets
        # There can be many *.sass/*.scss in the folder, but we only
        # will build the ones listed in sass/targets
        if os.path.isfile(os.path.join(self.sources_folder, "targets")):
            targets_path = os.path.join(self.sources_folder, "targets")
        else:
            targets_path = utils.get_asset_path(
                os.path.join(self.sources_folder, "targets"), self.site.THEMES)
        try:
            with codecs.open(targets_path, "rb", "utf-8") as inf:
                targets = [x.strip() for x in inf.readlines()]
        except Exception:
            targets = []

        for task in utils.copy_tree(
                self.sources_folder,
                os.path.join(kw['cache_folder'], self.sources_folder)):
            if task['name'] in tasks:
                continue
            task['basename'] = 'prepare_sass_sources'
            tasks[task['name']] = task
            yield task

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name),
                               self.sources_folder)
            for task in utils.copy_tree(
                    src, os.path.join(kw['cache_folder'],
                                      self.sources_folder)):
                if task['name'] in tasks:
                    continue
                task['basename'] = 'prepare_sass_sources'
                tasks[task['name']] = task
                yield task

        # Build targets and write CSS files
        dst_dir = os.path.join(self.site.config['OUTPUT_FOLDER'], 'assets',
                               'css')
        # Make everything depend on all sources, rough but enough
        deps = []
        for task in tasks.keys():
            for ext in self.sources_ext:
                if task.endswith(ext):
                    deps.append(task)

        def compile_target(target, dst):
            utils.makedirs(dst_dir)
            run_in_shell = sys.platform == 'win32'
            src = os.path.join(kw['cache_folder'], self.sources_folder, target)
            try:
                compiled = subprocess.check_output(
                    [self.compiler_name] + self.compiler_options + [src],
                    shell=run_in_shell)
            except OSError:
                utils.req_missing([self.compiler_name],
                                  'build Sass files (and use this theme)',
                                  False, False)
            with open(dst, "wb+") as outf:
                outf.write(compiled)

        yield self.group_task()

        # We can have file conflicts.  This is a way to prevent them.
        # I orignally wanted to use sets and their cannot-have-duplicates
        # magic, but I decided not to do this so we can show the user
        # what files were problematic.
        # If we didn’t do this, there would be a cryptic message from doit
        # instead.
        seennames = {}
        for target in targets:
            base = os.path.splitext(target)[0]
            dst = os.path.join(dst_dir, base + ".css")

            if base in seennames:
                self.logger.error(
                    'Duplicate filenames for Sass compiled files: {0} and '
                    '{1} (both compile to {2})'.format(seennames[base], target,
                                                       base + ".css"))
            else:
                seennames.update({base: target})

            yield {
                'basename': self.name,
                'name': dst,
                'targets': [dst],
                'file_dep': deps,
                'task_dep': ['prepare_sass_sources'],
                'actions': ((compile_target, [target, dst]), ),
                'uptodate': [utils.config_changed(kw)],
                'clean': True
            }
Пример #31
0
    def gen_tasks(self):
        """Create tasks to copy the assets of the whole theme chain.

        If a file is present on two themes, use the version
        from the "youngest" theme.
        """
        kw = {
            "themes": self.site.THEMES,
            "translations": self.site.translations,
            "files_folders": self.site.config['FILES_FOLDERS'],
            "output_folder": self.site.config['OUTPUT_FOLDER'],
            "filters": self.site.config['FILTERS'],
            "code_color_scheme": self.site.config['CODE_COLOR_SCHEME'],
            "code.css_selectors": ['pre.code', '.highlight pre'],
            "code.css_head": '/* code.css file generated by Nikola */\n',
            "code.css_close": "\ntable.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}\n",
        }
        tasks = {}
        code_css_path = os.path.join(kw['output_folder'], 'assets', 'css', 'code.css')
        code_css_input = utils.get_asset_path('assets/css/code.css',
                                              themes=kw['themes'],
                                              files_folders=kw['files_folders'], output_dir=None)
        yield self.group_task()

        main_theme = utils.get_theme_path(kw['themes'][0])
        theme_ini = utils.parse_theme_meta(main_theme)
        if theme_ini:
            ignored_assets = theme_ini.get("Nikola", "ignored_assets", fallback='').split(',')
            ignore_colorbox_i18n = theme_ini.get("Nikola", "ignore_colorbox_i18n", fallback="unused")
        else:
            ignored_assets = []
            ignore_colorbox_i18n = "unused"

        if ignore_colorbox_i18n == "unused":
            # Check what colorbox languages we need so we can ignore the rest
            needed_colorbox_languages = [LEGAL_VALUES['COLORBOX_LOCALES'][i] for i in kw['translations']]
            needed_colorbox_languages = [i for i in needed_colorbox_languages if i]  # remove '' for en
            # ignored_filenames is passed to copy_tree to avoid creating
            # directories. Since ignored_assets are full paths, and copy_tree
            #  works on single filenames, we can’t use that here.
            if not needed_colorbox_languages:
                ignored_filenames = set(["colorbox-i18n"])
            else:
                ignored_filenames = set()

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), 'assets')
            dst = os.path.join(kw['output_folder'], 'assets')
            for task in utils.copy_tree(src, dst, ignored_filenames=ignored_filenames):
                asset_name = os.path.relpath(task['name'], dst)
                if task['name'] in tasks or asset_name in ignored_assets:
                    continue
                elif asset_name.startswith(_COLORBOX_PREFIX):
                    if ignore_colorbox_i18n == "all" or ignore_colorbox_i18n is True:
                        continue

                    colorbox_lang = asset_name[_COLORBOX_SLICE]
                    if ignore_colorbox_i18n == "unused" and colorbox_lang not in needed_colorbox_languages:
                        continue
                tasks[task['name']] = task
                task['uptodate'] = [utils.config_changed(kw, 'nikola.plugins.task.copy_assets')]
                task['basename'] = self.name
                if code_css_input:
                    if 'file_dep' not in task:
                        task['file_dep'] = []
                    task['file_dep'].append(code_css_input)
                yield utils.apply_filters(task, kw['filters'])

        # Check whether or not there is a code.css file around.
        if not code_css_input and kw['code_color_scheme']:
            def create_code_css():
                from pygments.formatters import get_formatter_by_name
                formatter = get_formatter_by_name('html', style=kw["code_color_scheme"])
                utils.makedirs(os.path.dirname(code_css_path))
                with io.open(code_css_path, 'w+', encoding='utf8') as outf:
                    outf.write(kw["code.css_head"])
                    outf.write(formatter.get_style_defs(kw["code.css_selectors"]))
                    outf.write(kw["code.css_close"])

            if os.path.exists(code_css_path):
                with io.open(code_css_path, 'r', encoding='utf-8') as fh:
                    testcontents = fh.read(len(kw["code.css_head"])) == kw["code.css_head"]
            else:
                testcontents = False

            task = {
                'basename': self.name,
                'name': code_css_path,
                'targets': [code_css_path],
                'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.copy_assets'), testcontents],
                'actions': [(create_code_css, [])],
                'clean': True,
            }
            yield utils.apply_filters(task, kw['filters'])
Пример #32
0
    def _execute(self, options, args):
        """Given a swatch name and a parent theme, creates a custom theme."""
        name = options['name']
        swatch = options['swatch']
        if not swatch:
            LOGGER.error('The -s option is mandatory')
            return 1
        parent = options['parent']
        version = '4'

        # Check which Bootstrap version to use
        themes = utils.get_theme_chain(parent, self.site.themes_dirs)
        if _check_for_theme('bootstrap', themes) or _check_for_theme('bootstrap-jinja', themes):
            version = '2'
        elif _check_for_theme('bootstrap3', themes) or _check_for_theme('bootstrap3-jinja', themes):
            version = '3'
        elif _check_for_theme('bootstrap4', themes) or _check_for_theme('bootstrap4-jinja', themes):
            version = '4'
        elif not _check_for_theme('bootstrap4', themes) and not _check_for_theme('bootstrap4-jinja', themes):
            LOGGER.warn(
                '"subtheme" only makes sense for themes that use bootstrap')
        elif _check_for_theme('bootstrap3-gradients', themes) or _check_for_theme('bootstrap3-gradients-jinja', themes):
            LOGGER.warn(
                '"subtheme" doesn\'t work well with the bootstrap3-gradients family')

        LOGGER.info("Creating '{0}' theme from '{1}' and '{2}'".format(
            name, swatch, parent))
        utils.makedirs(os.path.join('themes', name, 'assets', 'css'))
        for fname in ('bootstrap.min.css', 'bootstrap.css'):
            if swatch in [
                    'bubblegum', 'business-tycoon', 'charming', 'daydream',
                    'executive-suite', 'good-news', 'growth', 'harbor', 'hello-world',
                    'neon-glow', 'pleasant', 'retro', 'vibrant-sea', 'wizardry']:  # Hackerthemes
                LOGGER.info(
                    'Hackertheme-based subthemes often require you use a custom font for full effect.')
                if version != '4':
                    LOGGER.error(
                        'The hackertheme subthemes are only available for Bootstrap 4.')
                    return 1
                if fname == 'bootstrap.css':
                    url = 'https://raw.githubusercontent.com/HackerThemes/theme-machine/master/dist/{swatch}/css/bootstrap4-{swatch}.css'.format(
                        swatch=swatch)
                else:
                    url = 'https://raw.githubusercontent.com/HackerThemes/theme-machine/master/dist/{swatch}/css/bootstrap4-{swatch}.min.css'.format(
                        swatch=swatch)
            else:  # Bootswatch
                url = 'https://bootswatch.com'
                if version:
                    url += '/' + version
                url = '/'.join((url, swatch, fname))
            LOGGER.info("Downloading: " + url)
            r = requests.get(url)
            if r.status_code > 299:
                LOGGER.error('Error {} getting {}', r.status_code, url)
                return 1
            data = r.text

            with open(os.path.join('themes', name, 'assets', 'css', fname),
                      'w+') as output:
                output.write(data)

        with open(os.path.join('themes', name, '%s.theme' % name), 'w+') as output:
            parent_theme_data_path = utils.get_asset_path(
                '%s.theme' % parent, themes)
            cp = configparser.ConfigParser()
            cp.read(parent_theme_data_path)
            cp['Theme']['parent'] = parent
            cp['Family'] = {'family': cp['Family']['family']}
            cp.write(output)

        LOGGER.notice(
            'Theme created.  Change the THEME setting to "{0}" to use it.'.format(name))
Пример #33
0
    def gen_tasks(self):
        """Create tasks to copy the assets of the whole theme chain.

        If a file is present on two themes, use the version
        from the "youngest" theme.
        """
        kw = {
            "themes": self.site.THEMES,
            "files_folders": self.site.config['FILES_FOLDERS'],
            "output_folder": self.site.config['OUTPUT_FOLDER'],
            "filters": self.site.config['FILTERS'],
            "code_color_scheme": self.site.config['CODE_COLOR_SCHEME'],
            "code.css_selectors": 'pre.code',
            "code.css_head": '/* code.css file generated by Nikola */\n',
            "code.css_close": "\ntable.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}\n",
        }
        tasks = {}
        code_css_path = os.path.join(kw['output_folder'], 'assets', 'css', 'code.css')
        code_css_input = utils.get_asset_path('assets/css/code.css',
                                              themes=kw['themes'],
                                              files_folders=kw['files_folders'], output_dir=None)
        yield self.group_task()

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), 'assets')
            dst = os.path.join(kw['output_folder'], 'assets')
            for task in utils.copy_tree(src, dst):
                if task['name'] in tasks:
                    continue
                tasks[task['name']] = task
                task['uptodate'] = [utils.config_changed(kw, 'nikola.plugins.task.copy_assets')]
                task['basename'] = self.name
                if code_css_input:
                    if 'file_dep' not in task:
                        task['file_dep'] = []
                    task['file_dep'].append(code_css_input)
                yield utils.apply_filters(task, kw['filters'])

        # Check whether or not there is a code.css file around.
        if not code_css_input and kw['code_color_scheme']:
            def create_code_css():
                from pygments.formatters import get_formatter_by_name
                formatter = get_formatter_by_name('html', style=kw["code_color_scheme"])
                utils.makedirs(os.path.dirname(code_css_path))
                with io.open(code_css_path, 'w+', encoding='utf8') as outf:
                    outf.write(kw["code.css_head"])
                    outf.write(formatter.get_style_defs(kw["code.css_selectors"]))
                    outf.write(kw["code.css_close"])

            if os.path.exists(code_css_path):
                with io.open(code_css_path, 'r', encoding='utf-8') as fh:
                    testcontents = fh.read(len(kw["code.css_head"])) == kw["code.css_head"]
            else:
                testcontents = False

            task = {
                'basename': self.name,
                'name': code_css_path,
                'targets': [code_css_path],
                'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.copy_assets'), testcontents],
                'actions': [(create_code_css, [])],
                'clean': True,
            }
            yield utils.apply_filters(task, kw['filters'])
Пример #34
0
    def gen_tasks(self):
        """Create tasks to copy the assets of the whole theme chain.

        If a file is present on two themes, use the version
        from the "youngest" theme.
        """
        kw = {
            "themes":
            self.site.THEMES,
            "translations":
            self.site.translations,
            "files_folders":
            self.site.config['FILES_FOLDERS'],
            "output_folder":
            self.site.config['OUTPUT_FOLDER'],
            "filters":
            self.site.config['FILTERS'],
            "code_color_scheme":
            self.site.config['CODE_COLOR_SCHEME'],
            "code.css_selectors":
            ['pre.code', '.code .codetable', '.highlight pre'],
            "code.css_wrappers": ['.highlight', '.code'],
            "code.css_head":
            '/* code.css file generated by Nikola */\n',
            "code.css_close":
            ("\ntable.codetable, table.highlighttable { width: 100%;}\n"
             ".codetable td.linenos, td.linenos { text-align: right; width: 3.5em; "
             "padding-right: 0.5em; background: rgba(127, 127, 127, 0.2) }\n"
             ".codetable td.code, td.code { padding-left: 0.5em; }\n"),
        }
        tasks = {}
        code_css_path = os.path.join(kw['output_folder'], 'css', 'code.css')
        code_css_input = utils.get_asset_path(
            'assets/css/code.css',
            themes=kw['themes'],
            files_folders=kw['files_folders'],
            output_dir=None)
        yield self.group_task()

        main_theme = utils.get_theme_path(kw['themes'][0])
        theme_ini = utils.parse_theme_meta(main_theme)
        if theme_ini:
            ignored_assets = theme_ini.get("Nikola",
                                           "ignored_assets",
                                           fallback='').split(',')
            ignored_assets = [
                os.path.normpath(asset_name.strip())
                for asset_name in ignored_assets
            ]
        else:
            ignored_assets = []

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), 'assets')
            dst = kw['output_folder']
            for task in utils.copy_tree(src, dst):
                asset_name = os.path.relpath(task['name'], dst)
                if task['name'] in tasks or asset_name in ignored_assets:
                    continue
                tasks[task['name']] = task
                task['uptodate'] = [
                    utils.config_changed(kw, 'nikola.plugins.task.copy_assets')
                ]
                task['basename'] = self.name
                if code_css_input:
                    if 'file_dep' not in task:
                        task['file_dep'] = []
                    task['file_dep'].append(code_css_input)
                yield utils.apply_filters(task, kw['filters'])

        # Check whether or not there is a code.css file around.
        if not code_css_input and kw['code_color_scheme']:

            def create_code_css():
                formatter = BetterHtmlFormatter(style=kw["code_color_scheme"])
                utils.makedirs(os.path.dirname(code_css_path))
                with io.open(code_css_path, 'w+', encoding='utf-8') as outf:
                    outf.write(kw["code.css_head"])
                    outf.write(
                        formatter.get_style_defs(kw["code.css_selectors"],
                                                 kw["code.css_wrappers"]))
                    outf.write(kw["code.css_close"])

            if os.path.exists(code_css_path):
                with io.open(code_css_path, 'r', encoding='utf-8-sig') as fh:
                    testcontents = fh.read(len(
                        kw["code.css_head"])) == kw["code.css_head"]
            else:
                testcontents = False

            task = {
                'basename':
                self.name,
                'name':
                code_css_path,
                'targets': [code_css_path],
                'uptodate': [
                    utils.config_changed(kw,
                                         'nikola.plugins.task.copy_assets'),
                    testcontents
                ],
                'actions': [(create_code_css, [])],
                'clean':
                True,
            }
            yield utils.apply_filters(task, kw['filters'])
Пример #35
0
    def gen_tasks(self):
        """Create tasks to copy the assets of the whole theme chain.

        If a file is present on two themes, use the version
        from the "youngest" theme.
        """
        kw = {
            "themes":
            self.site.THEMES,
            "translations":
            self.site.translations,
            "files_folders":
            self.site.config['FILES_FOLDERS'],
            "output_folder":
            self.site.config['OUTPUT_FOLDER'],
            "filters":
            self.site.config['FILTERS'],
            "code_color_scheme":
            self.site.config['CODE_COLOR_SCHEME'],
            "code.css_selectors": ['pre.code', '.highlight pre'],
            "code.css_head":
            '/* code.css file generated by Nikola */\n',
            "code.css_close":
            "\ntable.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}\n",
        }
        tasks = {}
        code_css_path = os.path.join(kw['output_folder'], 'assets', 'css',
                                     'code.css')
        code_css_input = utils.get_asset_path(
            'assets/css/code.css',
            themes=kw['themes'],
            files_folders=kw['files_folders'],
            output_dir=None)
        yield self.group_task()

        main_theme = utils.get_theme_path(kw['themes'][0])
        theme_ini = utils.parse_theme_meta(main_theme)
        if theme_ini:
            ignored_assets = theme_ini.get("Nikola",
                                           "ignored_assets",
                                           fallback='').split(',')
            ignore_colorbox_i18n = theme_ini.get("Nikola",
                                                 "ignore_colorbox_i18n",
                                                 fallback="unused")
        else:
            ignored_assets = []
            ignore_colorbox_i18n = "unused"

        if ignore_colorbox_i18n == "unused":
            # Check what colorbox languages we need so we can ignore the rest
            needed_colorbox_languages = [
                LEGAL_VALUES['COLORBOX_LOCALES'][i] for i in kw['translations']
            ]
            needed_colorbox_languages = [
                i for i in needed_colorbox_languages if i
            ]  # remove '' for en
            # ignored_filenames is passed to copy_tree to avoid creating
            # directories. Since ignored_assets are full paths, and copy_tree
            #  works on single filenames, we can’t use that here.
            if not needed_colorbox_languages:
                ignored_filenames = set(["colorbox-i18n"])
            else:
                ignored_filenames = set()

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), 'assets')
            dst = os.path.join(kw['output_folder'], 'assets')
            for task in utils.copy_tree(src,
                                        dst,
                                        ignored_filenames=ignored_filenames):
                asset_name = os.path.relpath(task['name'], dst)
                if task['name'] in tasks or asset_name in ignored_assets:
                    continue
                elif asset_name.startswith(_COLORBOX_PREFIX):
                    if ignore_colorbox_i18n == "all" or ignore_colorbox_i18n is True:
                        continue

                    colorbox_lang = asset_name[_COLORBOX_SLICE]
                    if ignore_colorbox_i18n == "unused" and colorbox_lang not in needed_colorbox_languages:
                        continue
                tasks[task['name']] = task
                task['uptodate'] = [
                    utils.config_changed(kw, 'nikola.plugins.task.copy_assets')
                ]
                task['basename'] = self.name
                if code_css_input:
                    if 'file_dep' not in task:
                        task['file_dep'] = []
                    task['file_dep'].append(code_css_input)
                yield utils.apply_filters(task, kw['filters'])

        # Check whether or not there is a code.css file around.
        if not code_css_input and kw['code_color_scheme']:

            def create_code_css():
                from pygments.formatters import get_formatter_by_name
                formatter = get_formatter_by_name(
                    'html', style=kw["code_color_scheme"])
                utils.makedirs(os.path.dirname(code_css_path))
                with io.open(code_css_path, 'w+', encoding='utf8') as outf:
                    outf.write(kw["code.css_head"])
                    outf.write(
                        formatter.get_style_defs(kw["code.css_selectors"]))
                    outf.write(kw["code.css_close"])

            if os.path.exists(code_css_path):
                with io.open(code_css_path, 'r', encoding='utf-8') as fh:
                    testcontents = fh.read(len(
                        kw["code.css_head"])) == kw["code.css_head"]
            else:
                testcontents = False

            task = {
                'basename':
                self.name,
                'name':
                code_css_path,
                'targets': [code_css_path],
                'uptodate': [
                    utils.config_changed(kw,
                                         'nikola.plugins.task.copy_assets'),
                    testcontents
                ],
                'actions': [(create_code_css, [])],
                'clean':
                True,
            }
            yield utils.apply_filters(task, kw['filters'])
Пример #36
0
    def gen_tasks(self):
        """Bundle assets using WebAssets."""
        kw = {
            'filters': self.site.config['FILTERS'],
            'output_folder': self.site.config['OUTPUT_FOLDER'],
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'theme_bundles': get_theme_bundles(self.site.THEMES),
            'themes': self.site.THEMES,
            'files_folders': self.site.config['FILES_FOLDERS'],
            'code_color_scheme': self.site.config['CODE_COLOR_SCHEME'],
        }

        def build_bundle(output, inputs):
            out_dir = os.path.join(kw['output_folder'],
                                   os.path.dirname(output))
            inputs = [os.path.relpath(i, out_dir) for i in inputs if os.path.isfile(i)]
            cache_dir = os.path.join(kw['cache_folder'], 'webassets')
            utils.makedirs(cache_dir)
            env = webassets.Environment(out_dir, os.path.dirname(output),
                                        cache=cache_dir)
            if inputs:
                bundle = webassets.Bundle(*inputs, output=os.path.basename(output))
                env.register(output, bundle)
                # This generates the file
                try:
                    env[output].build(force=True)
                except Exception as e:
                    self.logger.error("Failed to build bundles.")
                    self.logger.exception(e)
                    self.logger.notice("Try running ``nikola clean`` and building again.")
            else:
                with open(os.path.join(out_dir, os.path.basename(output)), 'wb+'):
                    pass  # Create empty file

        yield self.group_task()
        if (webassets is not None and self.site.config['USE_BUNDLES'] is not
                False):
            for name, _files in kw['theme_bundles'].items():
                output_path = os.path.join(kw['output_folder'], name)
                dname = os.path.dirname(name)
                files = []
                for fname in _files:
                    # paths are relative to dirname
                    files.append(os.path.join(dname, fname))
                file_dep = [os.path.join(kw['output_folder'], fname)
                            for fname in files if
                            utils.get_asset_path(
                                fname,
                                self.site.THEMES,
                                self.site.config['FILES_FOLDERS'],
                                output_dir=kw['output_folder']) or fname == os.path.join('assets', 'css', 'code.css')]
                # code.css will be generated by us if it does not exist in
                # FILES_FOLDERS or theme assets.  It is guaranteed that the
                # generation will happen before this task.
                task = {
                    'file_dep': list(file_dep),
                    'task_dep': ['copy_assets', 'copy_files'],
                    'basename': str(self.name),
                    'name': str(output_path),
                    'actions': [(build_bundle, (name, file_dep))],
                    'targets': [output_path],
                    'uptodate': [
                        utils.config_changed({
                            1: kw,
                            2: file_dep
                        }, 'nikola.plugins.task.bundles')],
                    'clean': True,
                }
                yield utils.apply_filters(task, kw['filters'])
Пример #37
0
    def gen_tasks(self):
        """Bundle assets using WebAssets."""
        kw = {
            'filters': self.site.config['FILTERS'],
            'output_folder': self.site.config['OUTPUT_FOLDER'],
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'theme_bundles': get_theme_bundles(self.site.THEMES),
            'themes': self.site.THEMES,
            'files_folders': self.site.config['FILES_FOLDERS'],
            'code_color_scheme': self.site.config['CODE_COLOR_SCHEME'],
        }

        def build_bundle(output, inputs):
            out_dir = os.path.join(kw['output_folder'],
                                   os.path.dirname(output))
            inputs = [
                os.path.relpath(i, out_dir) for i in inputs
                if os.path.isfile(i)
            ]
            cache_dir = os.path.join(kw['cache_folder'], 'webassets')
            utils.makedirs(cache_dir)
            env = webassets.Environment(out_dir,
                                        os.path.dirname(output),
                                        cache=cache_dir)
            if inputs:
                bundle = webassets.Bundle(*inputs,
                                          output=os.path.basename(output))
                env.register(output, bundle)
                # This generates the file
                try:
                    env[output].build(force=True)
                except Exception as e:
                    self.logger.error("Failed to build bundles.")
                    self.logger.exception(e)
                    self.logger.notice(
                        "Try running ``nikola clean`` and building again.")
            else:
                with open(os.path.join(out_dir, os.path.basename(output)),
                          'wb+'):
                    pass  # Create empty file

        yield self.group_task()
        if (webassets is not None
                and self.site.config['USE_BUNDLES'] is not False):
            for name, _files in kw['theme_bundles'].items():
                output_path = os.path.join(kw['output_folder'], name)
                dname = os.path.dirname(name)
                files = []
                for fname in _files:
                    # paths are relative to dirname
                    files.append(os.path.join(dname, fname))
                file_dep = [
                    os.path.join(kw['output_folder'], fname) for fname in files
                    if utils.get_asset_path(fname,
                                            self.site.THEMES,
                                            self.site.config['FILES_FOLDERS'],
                                            output_dir=kw['output_folder'])
                    or fname == os.path.join('assets', 'css', 'code.css')
                ]
                # code.css will be generated by us if it does not exist in
                # FILES_FOLDERS or theme assets.  It is guaranteed that the
                # generation will happen before this task.
                task = {
                    'file_dep':
                    list(file_dep),
                    'task_dep': ['copy_assets', 'copy_files'],
                    'basename':
                    str(self.name),
                    'name':
                    str(output_path),
                    'actions': [(build_bundle, (name, file_dep))],
                    'targets': [output_path],
                    'uptodate': [
                        utils.config_changed({
                            1: kw,
                            2: file_dep
                        }, 'nikola.plugins.task.bundles')
                    ],
                    'clean':
                    True,
                }
                yield utils.apply_filters(task, kw['filters'])
Пример #38
0
    def gen_tasks(self):
        """Generate CSS out of LESS sources."""
        self.compiler_name = self.site.config['LESS_COMPILER']
        self.compiler_options = self.site.config['LESS_OPTIONS']

        kw = {
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'themes': self.site.THEMES,
        }
        tasks = {}

        # Find where in the theme chain we define the LESS targets
        # There can be many *.less in the folder, but we only will build
        # the ones listed in less/targets
        if os.path.isfile(os.path.join(self.sources_folder, "targets")):
            targets_path = os.path.join(self.sources_folder, "targets")
        else:
            targets_path = utils.get_asset_path(os.path.join(self.sources_folder, "targets"), self.site.THEMES)
        try:
            with codecs.open(targets_path, "rb", "utf-8") as inf:
                targets = [x.strip() for x in inf.readlines()]
        except Exception:
            targets = []

        for task in utils.copy_tree(self.sources_folder, os.path.join(kw['cache_folder'], self.sources_folder)):
            if task['name'] in tasks:
                continue
            task['basename'] = 'prepare_less_sources'
            tasks[task['name']] = task
            yield task

        for theme_name in kw['themes']:
            src = os.path.join(utils.get_theme_path(theme_name), self.sources_folder)
            for task in utils.copy_tree(src, os.path.join(kw['cache_folder'], self.sources_folder)):
                if task['name'] in tasks:
                    continue
                task['basename'] = 'prepare_less_sources'
                tasks[task['name']] = task
                yield task

        # Build targets and write CSS files
        dst_dir = os.path.join(self.site.config['OUTPUT_FOLDER'], 'assets', 'css')
        # Make everything depend on all sources, rough but enough
        deps = []
        for task in tasks.keys():
            if task.endswith(self.sources_ext):
                deps.append(task)

        def compile_target(target, dst):
            utils.makedirs(dst_dir)
            src = os.path.join(kw['cache_folder'], self.sources_folder, target)
            run_in_shell = sys.platform == 'win32'
            try:
                compiled = subprocess.check_output([self.compiler_name] + self.compiler_options + [src], shell=run_in_shell)
            except OSError:
                utils.req_missing([self.compiler_name],
                                  'build LESS files (and use this theme)',
                                  False, False)
            with open(dst, "wb+") as outf:
                outf.write(compiled)

        yield self.group_task()

        for target in targets:
            dst = os.path.join(dst_dir, target.replace(self.sources_ext, ".css"))
            yield {
                'basename': self.name,
                'name': dst,
                'targets': [dst],
                'file_dep': deps,
                'task_dep': ['prepare_less_sources'],
                'actions': ((compile_target, [target, dst]), ),
                'uptodate': [utils.config_changed(kw)],
                'clean': True
            }
Пример #39
0
    def gen_tasks(self):
        """Bundle assets."""
        kw = {
            'filters': self.site.config['FILTERS'],
            'output_folder': self.site.config['OUTPUT_FOLDER'],
            'cache_folder': self.site.config['CACHE_FOLDER'],
            'theme_bundles': get_theme_bundles(self.site.THEMES),
            'themes': self.site.THEMES,
            'files_folders': self.site.config['FILES_FOLDERS'],
            'code_color_scheme': self.site.config['CODE_COLOR_SCHEME'],
        }

        def build_bundle(output, inputs):
            out_dir = os.path.join(kw['output_folder'],
                                   os.path.dirname(output))
            inputs = [
                os.path.join(
                    out_dir,
                    os.path.relpath(i, out_dir))
                for i in inputs if os.path.isfile(i)
            ]
            with open(os.path.join(out_dir, os.path.basename(output)), 'wb+') as out_fh:
                for i in inputs:
                    with open(i, 'rb') as in_fh:
                        shutil.copyfileobj(in_fh, out_fh)
                    out_fh.write(b'\n')

        yield self.group_task()

        if self.site.config['USE_BUNDLES']:
            for name, _files in kw['theme_bundles'].items():
                output_path = os.path.join(kw['output_folder'], name)
                dname = os.path.dirname(name)
                files = []
                for fname in _files:
                    # paths are relative to dirname
                    files.append(os.path.join(dname, fname))
                file_dep = [os.path.join(kw['output_folder'], fname)
                            for fname in files if
                            utils.get_asset_path(
                                fname,
                                self.site.THEMES,
                                self.site.config['FILES_FOLDERS'],
                                output_dir=kw['output_folder']) or fname == os.path.join('assets', 'css', 'code.css')]
                # code.css will be generated by us if it does not exist in
                # FILES_FOLDERS or theme assets.  It is guaranteed that the
                # generation will happen before this task.
                task = {
                    'file_dep': list(file_dep),
                    'task_dep': ['copy_assets', 'copy_files'],
                    'basename': str(self.name),
                    'name': str(output_path),
                    'actions': [(build_bundle, (name, file_dep))],
                    'targets': [output_path],
                    'uptodate': [
                        utils.config_changed({
                            1: kw,
                            2: file_dep
                        }, 'nikola.plugins.task.bundles')],
                    'clean': True,
                }
                yield utils.apply_filters(task, kw['filters'])