def remove_excluded_image(self, img): # Remove excluded images # img is something like galleries/demo/tesla2_lg.jpg so it's the *source* path # and we should remove both the large and thumbnail *destination* paths img = os.path.relpath(img, self.kw['gallery_path']) output_folder = os.path.dirname( os.path.join( self.kw["output_folder"], self.site.path("gallery", os.path.dirname(img)))) img_path = os.path.join(output_folder, os.path.basename(img)) fname, ext = os.path.splitext(img_path) thumb_path = fname + '.thumbnail' + ext yield utils.apply_filters({ 'basename': '_render_galleries_clean', 'name': thumb_path, 'actions': [ (utils.remove_file, (thumb_path,)) ], 'clean': True, 'uptodate': [utils.config_changed(self.kw)], }, self.kw['filters']) yield utils.apply_filters({ 'basename': '_render_galleries_clean', 'name': img_path, 'actions': [ (utils.remove_file, (img_path,)) ], 'clean': True, 'uptodate': [utils.config_changed(self.kw)], }, self.kw['filters'])
def gen_tasks(self): """Generate Windows Live Tiles and notifications.""" kw = { "default_lang": self.site.config["DEFAULT_LANG"], "site_url": self.site.config["BASE_URL"], "output_folder": self.site.config["OUTPUT_FOLDER"], "show_untranslated_posts": self.site.config["SHOW_UNTRANSLATED_POSTS"], "windows_live_tiles": self.site.config["WINDOWS_LIVE_TILES"], } msapplication_assets = os.path.join(kw["output_folder"], "assets", "msapplication") if not os.path.exists(msapplication_assets): os.makedirs(msapplication_assets) self.site.scan_posts() yield self.group_task() deps = [] if kw["show_untranslated_posts"]: posts = self.site.posts[:5] else: posts = [x for x in self.site.posts if x.is_translation_available(kw["default_lang"])][:5] for post in posts: deps += post.deps(kw["default_lang"]) for i, post in zip(range(len(posts)), posts): notification_deps = post.deps(kw["default_lang"]) output_name = os.path.join(msapplication_assets, "tile_notification" + str(i + 1) + ".xml") titles = { "maintitle": post.title(kw["default_lang"]), "title1": posts[0].title(kw["default_lang"]), "title2": posts[1].title(kw["default_lang"]), "title3": posts[2].title(kw["default_lang"]) } yield { "basename": "windows_live_tiles", "name": os.path.normpath(output_name), "file_dep": notification_deps, "targets": [output_name], "actions": [(self.generate_notification_tile, (output_name, kw["default_lang"], kw["windows_live_tiles"]["tileimages"], titles, post.meta[kw["default_lang"]]["previewimage"]))], "task_dep": ["render_posts"], "clean": True, "uptodate": [utils.config_changed(kw)], } browserconfig_output_name = os.path.join(kw["output_folder"], "browserconfig.xml") yield { "basename": "windows_live_tiles", "name": os.path.normpath(browserconfig_output_name), "file_dep": deps, "targets": [browserconfig_output_name], "actions": [(self.generate_browserconfig, (browserconfig_output_name, kw["windows_live_tiles"], len(posts)))], "task_dep": ["render_posts"], "clean": True, "uptodate": [utils.config_changed(kw)], }
def remove_excluded_image(self, img, input_folder): # Remove excluded images # img is something like input_folder/demo/tesla2_lg.jpg so it's the *source* path # and we should remove both the large and thumbnail *destination* paths output_folder = os.path.dirname( os.path.join(self.kw["output_folder"], self.site.path("gallery", os.path.dirname(img))) ) img = os.path.relpath(img, input_folder) img_path = os.path.join(output_folder, os.path.basename(img)) fname, ext = os.path.splitext(img_path) thumb_path = fname + ".thumbnail" + ext yield utils.apply_filters( { "basename": "_render_galleries_clean", "name": thumb_path, "actions": [(utils.remove_file, (thumb_path,))], "clean": True, "uptodate": [utils.config_changed(self.kw, "nikola.plugins.task.galleries:clean_thumb")], }, self.kw["filters"], ) yield utils.apply_filters( { "basename": "_render_galleries_clean", "name": img_path, "actions": [(utils.remove_file, (img_path,))], "clean": True, "uptodate": [utils.config_changed(self.kw, "nikola.plugins.task.galleries:clean_file")], }, self.kw["filters"], )
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, "output_folder": self.site.config['OUTPUT_FOLDER'], "filters": self.site.config['FILTERS'], "code_color_scheme": self.site.config['CODE_COLOR_SCHEME'], } flag = True has_code_css = False tasks = {} code_css_path = os.path.join(kw['output_folder'], 'assets', 'css', 'code.css') 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 if task['targets'][0] == code_css_path: has_code_css = True tasks[task['name']] = task task['uptodate'] = [utils.config_changed(kw)] task['basename'] = self.name flag = False yield utils.apply_filters(task, kw['filters']) if flag: yield { 'basename': self.name, 'name': 'None', 'uptodate': [True], 'actions': [], } if not has_code_css: # Generate it 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 codecs.open(code_css_path, 'wb+', 'utf8') as outf: outf.write(formatter.get_style_defs('.code')) outf.write("table.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}") task = { 'basename': self.name, 'name': code_css_path, 'targets': [code_css_path], 'uptodate': [utils.config_changed(kw)], 'actions': [(create_code_css, [])], 'clean': True, } yield utils.apply_filters(task, kw['filters'])
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, "output_folder": self.site.config["OUTPUT_FOLDER"], "filters": self.site.config["FILTERS"], "code_color_scheme": self.site.config["CODE_COLOR_SCHEME"], } has_code_css = False tasks = {} code_css_path = os.path.join(kw["output_folder"], "assets", "css", "code.css") 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 if task["targets"][0] == code_css_path: has_code_css = True tasks[task["name"]] = task task["uptodate"] = [utils.config_changed(kw)] task["basename"] = self.name yield utils.apply_filters(task, kw["filters"]) if not has_code_css: # Generate it 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 codecs.open(code_css_path, "wb+", "utf8") as outf: outf.write(formatter.get_style_defs(["pre.code", "div.code pre"])) outf.write("\ntable.codetable { width: 100%;} td.linenos {text-align: right; width: 4em;}\n") task = { "basename": self.name, "name": code_css_path, "targets": [code_css_path], "uptodate": [utils.config_changed(kw)], "actions": [(create_code_css, [])], "clean": True, } yield utils.apply_filters(task, kw["filters"])
def create_target_images(self, img, input_path): """Copy images to output.""" gallery_name = os.path.dirname(img) output_gallery = os.path.dirname( os.path.join( self.kw["output_folder"], self.site.path("gallery_global", gallery_name))) # Do thumbnails and copy originals # img is "galleries/name/image_name.jpg" # img_name is "image_name.jpg" # fname, ext are "image_name", ".jpg" # thumb_path is # "output/GALLERY_PATH/name/image_name.thumbnail.jpg" img_name = os.path.basename(img) fname, ext = os.path.splitext(img_name) thumb_path = os.path.join( output_gallery, ".thumbnail".join([fname, ext])) # thumb_path is "output/GALLERY_PATH/name/image_name.jpg" orig_dest_path = os.path.join(output_gallery, img_name) yield utils.apply_filters({ 'basename': self.name, 'name': thumb_path, 'file_dep': [img], 'targets': [thumb_path], 'actions': [ (self.resize_image, (img, thumb_path, self.kw['thumbnail_size'], True, self.kw['preserve_exif_data'], self.kw['exif_whitelist'], self.kw['preserve_icc_profiles'])) ], 'clean': True, 'uptodate': [utils.config_changed({ 1: self.kw['thumbnail_size'] }, 'nikola.plugins.task.galleries:resize_thumb')], }, self.kw['filters']) yield utils.apply_filters({ 'basename': self.name, 'name': orig_dest_path, 'file_dep': [img], 'targets': [orig_dest_path], 'actions': [ (self.resize_image, (img, orig_dest_path, self.kw['max_image_size'], True, self.kw['preserve_exif_data'], self.kw['exif_whitelist'], self.kw['preserve_icc_profiles'])) ], 'clean': True, 'uptodate': [utils.config_changed({ 1: self.kw['max_image_size'] }, 'nikola.plugins.task.galleries:resize_max')], }, self.kw['filters'])
def create_target_images(self, img): gallery_name = os.path.relpath(os.path.dirname(img), self.kw['gallery_path']) output_gallery = os.path.dirname( os.path.join( self.kw["output_folder"], self.site.path("gallery", gallery_name))) # Do thumbnails and copy originals # img is "galleries/name/image_name.jpg" # img_name is "image_name.jpg" # fname, ext are "image_name", ".jpg" # thumb_path is # "output/GALLERY_PATH/name/image_name.thumbnail.jpg" img_name = os.path.basename(img) fname, ext = os.path.splitext(img_name) thumb_path = os.path.join( output_gallery, ".thumbnail".join([fname, ext])) # thumb_path is "output/GALLERY_PATH/name/image_name.jpg" orig_dest_path = os.path.join(output_gallery, img_name) yield utils.apply_filters({ 'basename': self.name, 'name': thumb_path, 'file_dep': [img], 'targets': [thumb_path], 'actions': [ (self.resize_image, (img, thumb_path, self.kw['thumbnail_size'])) ], 'clean': True, 'uptodate': [utils.config_changed({ 1: self.kw['thumbnail_size'] })], }, self.kw['filters']) yield utils.apply_filters({ 'basename': self.name, 'name': orig_dest_path, 'file_dep': [img], 'targets': [orig_dest_path], 'actions': [ (self.resize_image, (img, orig_dest_path, self.kw['max_image_size'])) ], 'clean': True, 'uptodate': [utils.config_changed({ 1: self.kw['max_image_size'] })], }, self.kw['filters'])
def gen_tasks(self): """Copy static files into the output folder.""" kw = { 'files_folders': self.site.config['FILES_FOLDERS'], 'output_folder': self.site.config['OUTPUT_FOLDER'], 'filters': self.site.config['FILTERS'], } flag = False for src in kw['files_folders']: dst = kw['output_folder'] filters = kw['filters'] real_dst = os.path.join(dst, kw['files_folders'][src]) for task in utils.copy_tree(src, real_dst, link_cutoff=dst): flag = True task['basename'] = self.name task['uptodate'] = task.get('uptodate', []) +\ [utils.config_changed(kw)] yield utils.apply_filters(task, filters) if not flag: yield { 'basename': self.name, 'actions': (), }
def gen_tasks(self): """Build HTML fragments from metadata and text.""" self.site.scan_posts() kw = { "translations": self.site.config["TRANSLATIONS"], "timeline": self.site.timeline, "default_lang": self.site.config["DEFAULT_LANG"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "demote_headers": self.site.config['DEMOTE_HEADERS'], } yield self.group_task() for lang in kw["translations"]: deps_dict = copy(kw) deps_dict.pop('timeline') for post in kw['timeline']: dest = post.translated_base_path(lang) file_dep = [p for p in post.fragment_deps(lang) if not p.startswith("####MAGIC####")] task = { 'basename': self.name, 'name': dest, 'file_dep': file_dep, 'targets': [dest], 'actions': [(post.compile, (lang, )), (update_deps, (post, lang, )), ], 'clean': True, 'uptodate': [ utils.config_changed(deps_dict, 'nikola.plugins.task.posts'), lambda p=post, l=lang: dependence_on_timeline(p, l) ] + post.fragment_deps_uptodate(lang), } yield task
def list_tags_page(self, kw): """a global "all your tags/categories" page for each language""" tags = list(self.site.posts_per_tag.keys()) categories = list(self.site.posts_per_category.keys()) # We want our tags to be sorted case insensitive tags.sort(key=lambda a: a.lower()) categories.sort(key=lambda a: a.lower()) if categories != [""]: has_categories = True else: has_categories = False template_name = "tags.tmpl" kw["tags"] = tags kw["categories"] = categories for lang in kw["translations"]: output_name = os.path.join(kw["output_folder"], self.site.path("tag_index", None, lang)) output_name = output_name context = {} if has_categories: context["title"] = kw["messages"][lang]["Tags and Categories"] else: context["title"] = kw["messages"][lang]["Tags"] context["items"] = [(tag, self.site.link("tag", tag, lang)) for tag in tags] if has_categories: context["cat_items"] = [(tag, self.site.link("category", tag, lang)) for tag in categories] else: context["cat_items"] = None context["permalink"] = self.site.link("tag_index", None, lang) context["description"] = None task = self.site.generic_post_list_renderer(lang, [], output_name, template_name, kw["filters"], context) task_cfg = {1: task["uptodate"][0].config, 2: kw} task["uptodate"] = [utils.config_changed(task_cfg)] task["basename"] = str(self.name) yield task
def author_rss(self, author, lang, posts, kw): """Create a RSS feed for a single author in a given language.""" kind = "author" # Render RSS output_name = os.path.normpath( os.path.join(kw['output_folder'], self.site.path(kind + "_rss", author, lang))) feed_url = urljoin(self.site.config['BASE_URL'], self.site.link(kind + "_rss", author, lang).lstrip('/')) deps = [] deps_uptodate = [] post_list = sorted(posts, key=lambda a: a.date) post_list.reverse() for post in post_list: deps += post.deps(lang) deps_uptodate += post.deps_uptodate(lang) task = { 'basename': str(self.name), 'name': output_name, 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, "{0} ({1})".format(kw["blog_title"](lang), self._get_title(author)), kw["site_url"], None, post_list, output_name, kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], feed_url, None, kw["feed_link_append_query"]))], 'clean': True, 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.authors:rss')] + deps_uptodate, 'task_dep': ['render_posts'], } return utils.apply_filters(task, kw['filters'])
def tag_page_as_list(self, tag, lang, post_list, kw, is_category): """We render a single flat link list with this tag's posts""" kind = "category" if is_category else "tag" template_name = "tag.tmpl" output_name = os.path.join(kw['output_folder'], self.site.path( kind, tag, lang)) context = {} context["lang"] = lang context["title"] = kw["messages"][lang]["Posts about %s"] % tag context["posts"] = post_list context["permalink"] = self.site.link(kind, tag, lang) context["tag"] = tag context["kind"] = kind context["description"] = None task = self.site.generic_post_list_renderer( lang, post_list, output_name, template_name, kw['filters'], context, ) task_cfg = {1: task['uptodate'][0].config, 2: kw} task['uptodate'] = [utils.config_changed(task_cfg)] task['basename'] = str(self.name) yield task
def gen_tasks(self): """Generate RSS feeds.""" kw = { "translations": self.site.config["TRANSLATIONS"], "filters": self.site.config["FILTERS"], "blog_title": self.site.config["BLOG_TITLE"], "site_url": self.site.config["SITE_URL"], "blog_description": self.site.config["BLOG_DESCRIPTION"], "output_folder": self.site.config["OUTPUT_FOLDER"], "rss_teasers": self.site.config["RSS_TEASERS"], } self.site.scan_posts() # TODO: timeline is global, kill it for lang in kw["translations"]: output_name = os.path.join(kw['output_folder'], self.site.path("rss", None, lang)) deps = [] posts = [x for x in self.site.timeline if x.use_in_feeds][:10] for post in posts: deps += post.deps(lang) yield { 'basename': 'render_rss', 'name': output_name, 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, kw["blog_title"], kw["site_url"], kw["blog_description"], posts, output_name, kw["rss_teasers"]))], 'clean': True, 'uptodate': [utils.config_changed(kw)], }
def _generate_subclassification_page(self, taxonomy, node, context, kw, lang): """Render a list of subclassifications.""" def get_subnode_data(subnode): return [ taxonomy.get_classification_friendly_name(subnode.classification_name, lang, only_last_component=True), self.site.link(taxonomy.classification_name, subnode.classification_name, lang), len(self._filter_list(self.site.posts_per_classification[taxonomy.classification_name][lang][subnode.classification_name], lang)) ] items = [get_subnode_data(subnode) for subnode in node.children] context = copy(context) context["lang"] = lang context["permalink"] = self.site.link(taxonomy.classification_name, node.classification_name, lang) if "pagekind" not in context: context["pagekind"] = ["list", "archive_page"] context["items"] = items task = self.site.generic_post_list_renderer( lang, [], os.path.join(kw['output_folder'], self.site.path(taxonomy.classification_name, node.classification_name, lang)), taxonomy.subcategories_list_template, kw['filters'], context, ) task_cfg = {1: kw, 2: items} task['uptodate'] = task['uptodate'] + [utils.config_changed(task_cfg, 'nikola.plugins.task.taxonomy')] task['basename'] = self.name return task
def tag_rss(self, tag, lang, posts, kw, is_category): """RSS for a single tag / language""" kind = "category" if is_category else "tag" #Render RSS output_name = os.path.normpath( os.path.join(kw['output_folder'], self.site.path(kind + "_rss", tag, lang))) feed_url = urljoin(self.site.config['BASE_URL'], self.site.link(kind + "_rss", tag, lang).lstrip('/')) deps = [] post_list = [self.site.global_data[post] for post in posts if self.site.global_data[post].use_in_feeds] post_list.sort(key=lambda a: a.date) post_list.reverse() for post in post_list: deps += post.deps(lang) return { 'basename': str(self.name), 'name': output_name, 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, "{0} ({1})".format(kw["blog_title"], tag), kw["site_url"], None, post_list, output_name, kw["rss_teasers"], kw['feed_length'], feed_url))], 'clean': True, 'uptodate': [utils.config_changed(kw)], 'task_dep': ['render_posts'], }
def _generate_classification_page_as_rss(self, taxonomy, classification, filtered_posts, title, description, kw, lang): """Create a RSS feed for a single classification in a given language.""" kind = taxonomy.classification_name # Render RSS output_name = os.path.normpath(os.path.join(self.site.config['OUTPUT_FOLDER'], self.site.path(kind + "_rss", classification, lang))) feed_url = urljoin(self.site.config['BASE_URL'], self.site.link(kind + "_rss", classification, lang).lstrip('/')) deps = [] deps_uptodate = [] for post in filtered_posts: deps += post.deps(lang) deps_uptodate += post.deps_uptodate(lang) blog_title = kw["blog_title"](lang) task = { 'basename': str(self.name), 'name': output_name, 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, "{0} ({1})".format(blog_title, title) if blog_title != title else blog_title, kw["site_url"], description, filtered_posts, output_name, kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], feed_url, _enclosure, kw["feed_links_append_query"]))], 'clean': True, 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.taxonomies:rss')] + deps_uptodate, 'task_dep': ['render_posts'], } return utils.apply_filters(task, kw['filters'])
def _prepare_task(self, kw, name, lang, posts, items, template_name, title, deps_translatable=None): # name: used to build permalink and destination # posts, items: posts or items; only one of them should be used, # the other be None # template_name: name of the template to use # title: the (translated) title for the generated page # deps_translatable: dependencies (None if not added) assert posts is not None or items is not None context = {} context["lang"] = lang context["title"] = title context["permalink"] = self.site.link("archive", name, lang) if posts is not None: context["posts"] = posts n = len(posts) else: context["items"] = items n = len(items) task = self.site.generic_post_list_renderer( lang, [], os.path.join(kw['output_folder'], self.site.path("archive", name, lang)), template_name, kw['filters'], context, ) task_cfg = {1: kw, 2: n} if deps_translatable is not None: task_cfg[3] = deps_translatable task['uptodate'] = task['uptodate'] + [config_changed(task_cfg, 'nikola.plugins.task.archive')] task['basename'] = self.name return task
def _render_classification_overview(self, classification_name, template, lang, context, kw): # Prepare rendering context["permalink"] = self.site.link("{}_index".format(classification_name), None, lang) if "pagekind" not in context: context["pagekind"] = ["list", "tags_page"] output_name = os.path.join(self.site.config['OUTPUT_FOLDER'], self.site.path('{}_index'.format(classification_name), None, lang)) blinker.signal('generate_classification_overview').send({ 'site': self.site, 'classification_name': classification_name, 'lang': lang, 'context': context, 'kw': kw, 'output_name': output_name, }) task = self.site.generic_post_list_renderer( lang, [], output_name, template, kw['filters'], context, ) task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.taxonomies:page')] task['basename'] = str(self.name) yield task
def task_generate_posts(self): """Generate post files for the blog entries.""" def gen_id(entry): h = hashlib.md5() h.update(entry.feed.name.encode('utf8')) h.update(entry.guid) return h.hexdigest() def generate_post(entry): unique_id = gen_id(entry) meta_path = os.path.join('posts', unique_id + '.meta') post_path = os.path.join('posts', unique_id + '.txt') with codecs.open(meta_path, 'wb+', 'utf8') as fd: fd.write('%s\n' % entry.title.replace('\n', ' ')) fd.write('%s\n' % unique_id) fd.write('%s\n' % entry.date.strftime('%Y/%m/%d %H:%M')) fd.write('\n') fd.write('%s\n' % entry.link) with codecs.open(post_path, 'wb+', 'utf8') as fd: fd.write('.. raw:: html\n\n') content = entry.content if not content: content = 'Sin contenido' for line in content.splitlines(): fd.write(' %s\n' % line) for entry in Entry.select().order_by(Entry.date.desc()): entry_id = gen_id(entry) yield { 'basename': self.name, 'targets': [os.path.join('posts', entry_id + '.meta'), os.path.join('posts', entry_id + '.txt')], 'name': entry_id, 'actions': [(generate_post, (entry,))], 'uptodate': [config_changed({1: entry})] }
def _generate_posts_task(self, kw, name, lang, posts, title, deps_translatable=None): posts = sorted(posts, key=lambda a: a.date) posts.reverse() if kw['archives_are_indexes']: def page_link(i, displayed_i, num_pages, force_addition, extension=None): feed = "_atom" if extension == ".atom" else "" return adjust_name_for_index_link(self.site.link("archive" + feed, name, lang), i, displayed_i, lang, self.site, force_addition, extension) def page_path(i, displayed_i, num_pages, force_addition, extension=None): feed = "_atom" if extension == ".atom" else "" return adjust_name_for_index_path(self.site.path("archive" + feed, name, lang), i, displayed_i, lang, self.site, force_addition, extension) uptodate = [] if deps_translatable is not None: uptodate += [config_changed(deps_translatable, 'nikola.plugins.task.archive')] yield self.site.generic_index_renderer( lang, posts, title, "archiveindex.tmpl", {"archive_name": name, "is_feed_stale": kw["is_feed_stale"]}, kw, str(self.name), page_link, page_path, uptodate) else: yield self._prepare_task(kw, name, lang, posts, None, "list_post.tmpl", title, deps_translatable)
def tag_page_as_list(self, tag, lang, post_list, kw, is_category): """Render a single flat link list with this tag's posts.""" kind = "category" if is_category else "tag" template_name = "tag.tmpl" output_name = os.path.join(kw['output_folder'], self.site.path( kind, tag, lang)) context = {} context["lang"] = lang title = self._get_title(tag, is_category) if is_category: context["category"] = tag context["category_path"] = self.site.parse_category_name(tag) context["tag"] = title context["title"] = self._get_indexes_title(tag, title, is_category, lang, kw["messages"]) context["posts"] = post_list context["permalink"] = self.site.link(kind, tag, lang) context["kind"] = kind context["description"] = self._get_description(tag, is_category, lang) if is_category: context["subcategories"] = self._get_subcategories(tag) context["pagekind"] = ["list", "tag_page"] task = self.site.generic_post_list_renderer( lang, post_list, output_name, template_name, kw['filters'], context, ) task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.tags:list')] task['basename'] = str(self.name) yield task if self.site.config['GENERATE_ATOM']: yield self.atom_feed_list(kind, tag, lang, post_list, context, kw)
def fragment_deps_uptodate(self, lang): """Return a list of file dependencies to build this post's fragment.""" deps = [] deps += self._get_dependencies(self._dependency_uptodate_fragment[lang]) deps += self._get_dependencies(self._dependency_uptodate_fragment[None]) deps.append(utils.config_changed({1: sorted(self.compiler.config_dependencies)}, 'nikola.post.Post.deps_uptodate:compiler:' + self.source_path)) return deps
def _create_authors_page(self, kw): """Create a global "all authors" page for each language.""" template_name = "authors.tmpl" kw = kw.copy() for lang in kw["translations"]: authors = natsort.natsorted([author for author in self._posts_per_author().keys()], alg=natsort.ns.F | natsort.ns.IC) has_authors = (authors != []) kw['authors'] = authors output_name = os.path.join( kw['output_folder'], self.site.path('author_index', None, lang)) context = {} if has_authors: context["title"] = kw["messages"][lang]["Authors"] context["items"] = [(author, self.site.link("author", author, lang)) for author in authors] context["description"] = context["title"] else: context["items"] = None context["permalink"] = self.site.link("author_index", None, lang) context["pagekind"] = ["list", "authors_page"] task = self.site.generic_post_list_renderer( lang, [], output_name, template_name, kw['filters'], context, ) task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.authors:page')] task['basename'] = str(self.name) yield task
def gen_tasks(self): """Build final pages from metadata and HTML fragments.""" kw = { "post_pages": self.site.config["post_pages"], "translations": self.site.config["TRANSLATIONS"], "filters": self.site.config["FILTERS"], } self.site.scan_posts() flag = False for lang in kw["translations"]: for post in self.site.timeline: for task in self.site.generic_page_renderer(lang, post, kw["filters"]): task['uptodate'] = [config_changed({ 1: task['uptodate'][0].config, 2: kw})] task['basename'] = self.name flag = True yield task if flag is False: # No page rendered, yield a dummy task yield { 'basename': self.name, 'name': 'None', 'uptodate': [True], 'actions': [], }
def gen_tasks(self): """Copy static files into the output folder.""" self.kw = { 'image_thumbnail_size': self.site.config['IMAGE_THUMBNAIL_SIZE'], 'image_thumbnail_format': self.site.config['IMAGE_THUMBNAIL_FORMAT'], 'max_image_size': self.site.config['MAX_IMAGE_SIZE'], 'image_folders': self.site.config['IMAGE_FOLDERS'], 'output_folder': self.site.config['OUTPUT_FOLDER'], 'filters': self.site.config['FILTERS'], 'preserve_exif_data': self.site.config['PRESERVE_EXIF_DATA'], 'exif_whitelist': self.site.config['EXIF_WHITELIST'], 'preserve_icc_profiles': self.site.config['PRESERVE_ICC_PROFILES'], } self.image_ext_list = self.image_ext_list_builtin self.image_ext_list.extend(self.site.config.get('EXTRA_IMAGE_EXTENSIONS', [])) yield self.group_task() for src in self.kw['image_folders']: dst = self.kw['output_folder'] filters = self.kw['filters'] real_dst = os.path.join(dst, self.kw['image_folders'][src]) for task in self.process_tree(src, real_dst): task['basename'] = self.name task['uptodate'] = [utils.config_changed(self.kw)] yield utils.apply_filters(task, filters)
def gen_tasks(self): """Generate redirections tasks.""" kw = { 'redirections': self.site.config['REDIRECTIONS'], 'output_folder': self.site.config['OUTPUT_FOLDER'], } if not kw['redirections']: # If there are no redirections, still needs to create a # dummy action so dependencies don't fail yield { 'basename': self.name, 'name': 'None', 'uptodate': [True], 'actions': [], } else: for src, dst in kw["redirections"]: src_path = os.path.join(kw["output_folder"], src) yield { 'basename': self.name, 'name': src_path, 'targets': [src_path], 'actions': [(create_redirect, (src_path, dst))], 'clean': True, 'uptodate': [utils.config_changed(kw)], }
def tag_rss(self, tag, lang, posts, kw, is_category): """RSS for a single tag / language""" kind = "category" if is_category else "tag" # Render RSS output_name = os.path.normpath( os.path.join(kw['output_folder'], self.site.path(kind + "_rss", tag, lang))) feed_url = urljoin(self.site.config['BASE_URL'], self.site.link(kind + "_rss", tag, lang).lstrip('/')) deps = [] deps_uptodate = [] post_list = sorted(posts, key=lambda a: a.date) post_list.reverse() for post in post_list: deps += post.deps(lang) deps_uptodate += post.deps_uptodate(lang) task = { 'basename': str(self.name), 'name': output_name, 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, "{0} ({1})".format(kw["blog_title"](lang), tag), kw["site_url"], None, post_list, output_name, kw["rss_teasers"], kw["rss_plain"], kw['feed_length'], feed_url))], 'clean': True, 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.tags:rss')] + deps_uptodate, 'task_dep': ['render_posts'], } return utils.apply_filters(task, kw['filters'])
def author_page_as_list(self, author, lang, post_list, kw): """Render a single flat link list with this author's posts.""" kind = "author" template_name = "author.tmpl" output_name = os.path.join(kw['output_folder'], self.site.path( kind, author, lang)) context = {} context["lang"] = lang title = self._get_title(author) context["author"] = title context["title"] = kw["messages"][lang]["Posts by %s"] % title context["posts"] = post_list context["permalink"] = self.site.link(kind, author, lang) context["kind"] = kind context["description"] = self._get_description(author, lang) context["pagekind"] = ["list", "author_page"] task = self.site.generic_post_list_renderer( lang, post_list, output_name, template_name, kw['filters'], context, ) task['uptodate'] = task['uptodate'] + [utils.config_changed(kw, 'nikola.plugins.task.authors:list')] task['basename'] = str(self.name) yield task
def tag_rss(self, tag, lang, posts, kw): """RSS for a single tag / language""" #Render RSS output_name = os.path.join(kw['output_folder'], self.site.path("tag_rss", tag, lang)) output_name = output_name.encode('utf8') deps = [] post_list = [self.site.global_data[post] for post in posts if self.site.global_data[post].use_in_feeds] post_list.sort(key=lambda a: a.date) post_list.reverse() for post in post_list: deps += post.deps(lang) return { 'basename': str(self.name), 'name': output_name, 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, "%s (%s)" % (kw["blog_title"], tag), kw["blog_url"], kw["blog_description"], post_list, output_name, kw["rss_teasers"]))], 'clean': True, 'uptodate': [utils.config_changed(kw)], }
def list_tags_page(self, kw): """a global "all your tags" page for each language""" tags = list(self.site.posts_per_tag.keys()) tags.sort() template_name = "tags.tmpl" kw['tags'] = tags for lang in kw["translations"]: output_name = os.path.join( kw['output_folder'], self.site.path('tag_index', None, lang)) output_name = output_name.encode('utf8') context = {} context["title"] = kw["messages"][lang]["Tags"] context["items"] = [(tag, self.site.link("tag", tag, lang)) for tag in tags] context["permalink"] = self.site.link("tag_index", None, lang) task = self.site.generic_post_list_renderer( lang, [], output_name, template_name, kw['filters'], context, ) task_cfg = {1: task['uptodate'][0].config, 2: kw} task['uptodate'] = [utils.config_changed(task_cfg)] yield task
def gen_tasks(self): """Copy static files into the output folder.""" self.kw = { 'image_thumbnail_size': self.site.config['IMAGE_THUMBNAIL_SIZE'], 'max_image_size': self.site.config['MAX_IMAGE_SIZE'], 'image_folders': self.site.config['IMAGE_FOLDERS'], 'output_folder': self.site.config['OUTPUT_FOLDER'], 'filters': self.site.config['FILTERS'], } self.image_ext_list = self.image_ext_list_builtin self.image_ext_list.extend( self.site.config.get('EXTRA_IMAGE_EXTENSIONS', [])) yield self.group_task() for src in self.kw['image_folders']: dst = self.kw['output_folder'] filters = self.kw['filters'] real_dst = os.path.join(dst, self.kw['image_folders'][src]) for task in self.process_tree(src, real_dst): task['basename'] = self.name task['uptodate'] = [utils.config_changed(self.kw)] yield utils.apply_filters(task, filters)
def tag_rss(self, tag, lang, posts, kw, is_category): """RSS for a single tag / language""" kind = "category" if is_category else "tag" #Render RSS output_name = os.path.normpath( os.path.join(kw['output_folder'], self.site.path(kind + "_rss", tag, lang))) feed_url = urljoin( self.site.config['BASE_URL'], self.site.link(kind + "_rss", tag, lang).lstrip('/')) deps = [] post_list = [ self.site.global_data[post] for post in posts if self.site.global_data[post].use_in_feeds ] post_list.sort(key=lambda a: a.date) post_list.reverse() for post in post_list: deps += post.deps(lang) return { 'basename': str(self.name), 'name': output_name, 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, "{0} ({1})".format(kw["blog_title"], tag), kw["site_url"], kw["blog_description"], post_list, output_name, kw["rss_teasers"], kw['feed_length'], feed_url))], 'clean': True, 'uptodate': [utils.config_changed(kw)], 'task_dep': ['render_posts'], }
def gen_tasks(self): """Render the tag cloud.""" self.site.scan_posts() yield self.group_task() # Tag cloud json file tag_cloud_data = {} for tag, posts in self.site.posts_per_tag.items(): if tag in self.site.config['HIDDEN_TAGS']: continue tag_posts = dict(posts=[{'title': post.meta[post.default_lang]['title'], 'date': post.date.strftime('%m/%d/%Y'), 'isodate': post.date.isoformat(), 'url': post.permalink(post.default_lang)} for post in reversed(sorted(self.site.timeline, key=lambda post: post.date)) if tag in post.alltags]) tag_cloud_data[tag] = [len(posts), self.site.link( 'tag', tag, self.site.config['DEFAULT_LANG']), tag_posts] output_name = os.path.join(self.site.config['OUTPUT_FOLDER'], 'assets', 'js', 'tag_cloud_data.json') def write_tag_data(data): """Write tag data into JSON file, for use in tag clouds.""" utils.makedirs(os.path.dirname(output_name)) with open(output_name, 'w+') as fd: json.dump(data, fd, sort_keys=True) task = { 'basename': str(self.name), 'name': str(output_name) } task['uptodate'] = [utils.config_changed(tag_cloud_data, 'nikola.plugins.task.tags:tagdata')] task['targets'] = [output_name] task['actions'] = [(write_tag_data, [tag_cloud_data])] task['clean'] = True yield utils.apply_filters(task, self.site.config['FILTERS'])
def _render_classification_overview(self, classification_name, template, lang, context, kw): # Prepare rendering context["permalink"] = self.site.link( "{}_index".format(classification_name), None, lang) if "pagekind" not in context: context["pagekind"] = ["list", "tags_page"] output_name = os.path.join( self.site.config['OUTPUT_FOLDER'], self.site.path('{}_index'.format(classification_name), None, lang)) blinker.signal('generate_classification_overview').send({ 'site': self.site, 'classification_name': classification_name, 'lang': lang, 'context': context, 'kw': kw, 'output_name': output_name, }) task = self.site.generic_post_list_renderer( lang, [], output_name, template, kw['filters'], context, ) task['uptodate'] = task['uptodate'] + [ utils.config_changed(kw, 'nikola.plugins.task.taxonomies:page') ] task['basename'] = str(self.name) yield task
def gen_tasks(self): """Generate CSS out of Sass sources.""" self.compiler_name = self.site.config['SASS_COMPILER'] self.output_folder = self.site.config['OUTPUT_FOLDER'] self.theme = self.site.config['THEME'] # Build targets and write CSS files destination_path = os.path.join(self.output_folder, 'assets', 'css', self.theme + '.css') sass_path = os.path.join('themes', self.theme, 'sass') target_path = os.path.join(sass_path, 'index.scss') sass_dir_size = '' def compile_sass(target_path, destination_path): try: compiled = sass.compile(filename=target_path) except OSError: utils.req_missing([self.compiler_name], 'build Sass files (and use this theme)', False, False) with open(destination_path, "w+") as outfile: outfile.write(compiled) if os.path.isfile(target_path): sass_dir_size = sum(f.stat().st_size for f in Path(sass_path).glob('**/*') if f.is_file()) yield { 'basename': self.name, 'name': destination_path, 'targets': [destination_path], 'actions': ((compile_sass, [target_path, destination_path]), ), 'uptodate': [utils.config_changed(str(sass_dir_size))], 'clean': True }
def generate_feed_task(self, lang, title, link, description, timeline, feed_url, output_name, primary_author=None): """Generate a task to create a feed.""" # Build dependency list deps = [] deps_uptodate = [] for post in timeline: deps += post.deps(lang) deps_uptodate += post.deps_uptodate(lang) task = { 'basename': str(self.name), 'name': str(output_name), 'targets': [output_name], 'file_dep': deps, 'task_dep': ['render_posts', 'render_taxonomies'], 'actions': [(self.generate_feed, (lang, title, link, description, timeline, feed_url, output_name, primary_author))], 'uptodate': [utils.config_changed(self.kw, 'jsonfeed:' + output_name)] + deps_uptodate, 'clean': True } yield utils.apply_filters(task, self.site.config['FILTERS'])
def gen_tasks(self): self.site.scan_posts() kw = { "translations": self.site.config["TRANSLATIONS"], "timeline": self.site.timeline, "default_lang": self.site.config["DEFAULT_LANG"], "show_untranslated_posts": self.site.config["SHOW_UNTRANSLATED_POSTS"], "demote_headers": self.site.config["DEMOTE_HEADERS"], } self.tl_changed = False yield self.group_task() def tl_ch(): self.tl_changed = True yield { "basename": self.name, "name": "timeline_changes", "actions": [tl_ch], "uptodate": [utils.config_changed({1: kw["timeline"]})], } for lang in kw["translations"]: deps_dict = copy(kw) deps_dict.pop("timeline") for post in kw["timeline"]: if not should_generate_pdf(self.site, post, lang): self.logger.info("not generating pdf for " + post.title()) continue yield utils.apply_filters( self.get_task(deps_dict, post, lang), { os.path.splitext(get_pdf_dest(self.site, post, lang))[-1]: self.get_metadata_filters(post, lang) }, )
def gen_tasks(self): """Build final pages from metadata and HTML fragments.""" kw = { "post_pages": self.site.config["post_pages"], "translations": self.site.config["TRANSLATIONS"], "filters": self.site.config["FILTERS"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "demote_headers": self.site.config['DEMOTE_HEADERS'], } self.site.scan_posts() yield self.group_task() index_paths = {} for lang in kw["translations"]: index_paths[lang] = False if not self.site.config["DISABLE_INDEXES"]: index_paths[lang] = os.path.normpath(os.path.join(self.site.config['OUTPUT_FOLDER'], self.site.path('index', '', lang=lang))) for lang in kw["translations"]: for post in self.site.timeline: if not kw["show_untranslated_posts"] and not post.is_translation_available(lang): continue if post.is_post: context = {'pagekind': ['post_page']} else: context = {'pagekind': ['story_page', 'page_page']} for task in self.site.generic_page_renderer(lang, post, kw["filters"], context): if task['name'] == index_paths[lang]: # Issue 3022 LOGGER.error( "Post {0!r}: output path ({1}) conflicts with the blog index ({2}). " "Please change INDEX_PATH or disable index generation.".format( post.source_path, task['name'], index_paths[lang])) task['uptodate'] = task['uptodate'] + [config_changed(kw, 'nikola.plugins.task.pages')] task['basename'] = self.name task['task_dep'] = ['render_posts'] yield task
def _create_authors_page(self, kw): """Create a global "all authors" page for each language.""" template_name = "authors.tmpl" kw = kw.copy() for lang in kw["translations"]: authors = natsort.natsorted( [author for author in self._posts_per_author().keys()], alg=natsort.ns.F | natsort.ns.IC) has_authors = (authors != []) kw['authors'] = authors output_name = os.path.join( kw['output_folder'], self.site.path('author_index', None, lang)) context = {} if has_authors: context["title"] = kw["messages"][lang]["Authors"] context["items"] = [(author, self.site.link("author", author, lang)) for author in authors] context["description"] = context["title"] else: context["items"] = None context["permalink"] = self.site.link("author_index", None, lang) context["pagekind"] = ["list", "authors_page"] task = self.site.generic_post_list_renderer( lang, [], output_name, template_name, kw['filters'], context, ) task['uptodate'] = task['uptodate'] + [ utils.config_changed(kw, 'nikola.plugins.task.authors:page') ] task['basename'] = str(self.name) yield task
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, "output_folder": self.site.config['OUTPUT_FOLDER'], "filters": self.site.config['FILTERS'], } tasks = {} 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)] task['basename'] = self.name yield utils.apply_filters(task, kw['filters'])
def gen_tasks(self): urls = [post.permalink() for post in self.site.posts] kw = { 'files_folders': self.site.config['FILES_FOLDERS'], 'output_folder': self.site.config['OUTPUT_FOLDER'], } output_filename = os.path.join( kw['output_folder'], 'assets/js/posts.json', ) output_index = os.path.join(kw['output_folder'], 'random/index.html') # Yield a task for Doit yield { 'basename': 'random_post', 'targets': (output_filename, output_index), 'actions': [ (create_json, (output_filename, urls)), (create_index, (output_index, )), ], 'uptodate': [utils.config_changed(kw)], }
def gen_tasks(self): """Copy static files into the output folder.""" kw = { 'files_folders': self.site.config['FILES_FOLDERS'], 'output_folder': self.site.config['OUTPUT_FOLDER'], 'filters': self.site.config['FILTERS'], } flag = False for src in kw['files_folders']: dst = kw['output_folder'] filters = kw['filters'] real_dst = os.path.join(dst, kw['files_folders'][src]) for task in utils.copy_tree(src, real_dst, link_cutoff=dst): flag = True task['basename'] = self.name task['uptodate'] = [utils.config_changed(kw)] yield utils.apply_filters(task, filters) if not flag: yield { 'basename': self.name, 'actions': (), }
def create_galleries(self): """Given a list of galleries, create the output folders.""" # gallery_path is "gallery/foo/name" for gallery_path, input_folder, _ in self.gallery_list: # have to use dirname because site.path returns .../index.html output_gallery = os.path.dirname( os.path.join(self.kw["output_folder"], self.site.path("gallery", gallery_path))) output_gallery = os.path.normpath(output_gallery) # Task to create gallery in output/ yield { 'basename': self.name, 'name': output_gallery, 'actions': [(utils.makedirs, (output_gallery, ))], 'targets': [output_gallery], 'clean': True, 'uptodate': [ utils.config_changed( self.kw.copy(), 'nikola.plugins.task.galleries:mkdir') ], }
def gen_tasks(self): """Build final pages from metadata and HTML fragments.""" kw = { "post_pages": self.site.config["post_pages"], "translations": self.site.config["TRANSLATIONS"], "filters": self.site.config["FILTERS"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "demote_headers": self.site.config['DEMOTE_HEADERS'], } self.site.scan_posts() yield self.group_task() for lang in kw["translations"]: for post in self.site.timeline: if not kw["show_untranslated_posts"] and not post.is_translation_available(lang): continue if post.is_post: context = {'pagekind': ['post_page']} else: context = {'pagekind': ['story_page']} for task in self.site.generic_page_renderer(lang, post, kw["filters"], context): task['uptodate'] = task['uptodate'] + [config_changed(kw, 'nikola.plugins.task.pages')] task['basename'] = self.name task['task_dep'] = ['render_posts'] yield task
def gen_tasks(self): self.logger = utils.get_logger('speechsynthesizednetcast', self.site.loghandlers) # Deps and config kw = { "translations": self.site.config['TRANSLATIONS'], "blog_title": self.site.config['BLOG_TITLE'], "blog_description": self.site.config['BLOG_DESCRIPTION'], "site_url": self.site.config['SITE_URL'], "output_folder": self.site.config['OUTPUT_FOLDER'], "cache_folder": self.site.config['CACHE_FOLDER'], "feed_length": self.site.config['FEED_LENGTH'], "default_lang": self.site.config['DEFAULT_LANG'], "audio_formats": self.default_audio_formats, "intro_text": self.default_text_intro, "outro_text": self.default_text_outro, } # Default configuration values if 'NETCAST_AUDIO_FORMATS' in self.site.config: kw['audio_formats'] = self.site.config['NETCAST_AUDIO_FORMATS'] if 'NETCAST_INTRO' in self.site.config: kw['intro_text'] = self.site.config['NETCAST_INTRO'] if 'NETCAST_OUTRO' in self.site.config: kw['outro_text'] = self.site.config['NETCAST_OUTRO'] self.test_required_programs([kw['audio_formats']]) self.site.scan_posts() yield self.group_task() for lang in kw['translations']: feed_deps = [] posts = [ x for x in self.site.posts if x.is_translation_available(lang) ][:10] for post in posts: post_recording_path = self.netcast_audio_path(lang=lang, post=post, format='flac', is_cache=True) yield { 'name': str(post_recording_path), 'basename': str(self.name), 'targets': [post_recording_path], 'file_dep': post.fragment_deps(lang), 'uptodate': [utils.config_changed(kw)], 'clean': True, 'actions': [(self.record_post, [post_recording_path, post, lang])] } for format in kw['audio_formats']: output_name = self.netcast_audio_path(lang=lang, post=post, format=format) yield { 'name': str(output_name), 'basename': str(self.name), 'targets': [output_name], 'file_dep': [post_recording_path, post.fragment_deps(lang)[0]], 'clean': True, 'actions': [(self.encode_post, [ output_name, post_recording_path, post, lang, format ])] } feed_deps.append(output_name) for format in kw['audio_formats']: output_name = self.netcast_feed_path(lang=lang, format=format) yield { 'name': str(output_name), 'basename': str(self.name), 'targets': [output_name], 'file_dep': feed_deps, # depends on all formats 'clean': True, 'actions': [(self.netcast_feed_renderer, [lang, posts, output_name, format])] }
def gen_tasks(self): self.site.scan_posts() kw = { "translations": self.site.config['TRANSLATIONS'], "index_display_post_count": self.site.config['INDEX_DISPLAY_POST_COUNT'], "messages": self.site.MESSAGES, "index_teasers": self.site.config['INDEX_TEASERS'], "output_folder": self.site.config['OUTPUT_FOLDER'], "filters": self.site.config['FILTERS'], "blog_title": self.site.config['BLOG_TITLE'], "content_footer": self.site.config['CONTENT_FOOTER'], } # TODO: timeline is global, get rid of it posts = [x for x in self.site.timeline if x.use_in_feeds] if not posts: yield { 'basename': 'render_mustache', 'actions': [], } return def write_file(path, post, lang): # Prev/Next links prev_link = False if post.prev_post: prev_link = post.prev_post.permalink(lang).replace( ".html", ".json") next_link = False if post.next_post: next_link = post.next_post.permalink(lang).replace( ".html", ".json") data = {} # Configuration for k, v in self.site.config.items(): if isinstance(v, (str, unicode_str)): # NOQA data[k] = v # Tag data tags = [] for tag in post.tags: tags.append({ 'name': tag, 'link': self.site.link("tag", tag, lang) }) data.update({ "tags": tags, "tags?": True if tags else False, }) # Template strings for k, v in kw["messages"][lang].items(): data["message_" + k] = v # Post data data.update({ "title": post.title(lang), "text": post.text(lang), "prev": prev_link, "next": next_link, "date": post.date.strftime(self.site.GLOBAL_CONTEXT['date_format']), }) # Comments context = dict(post=post, lang=LocaleBorg().current_lang) context.update(self.site.GLOBAL_CONTEXT) data["comment_html"] = self.site.template_system.render_template( 'mustache-comment-form.tmpl', None, context).strip() # Post translations translations = [] for langname in kw["translations"]: if langname == lang: continue translations.append({ 'name': kw["messages"][langname]["Read in English"], 'link': "javascript:load_data('%s');" % post.permalink(langname).replace(".html", ".json") }) data["translations"] = translations makedirs(os.path.dirname(path)) with codecs.open(path, 'wb+', 'utf8') as fd: fd.write(json.dumps(data)) for lang in kw["translations"]: for i, post in enumerate(posts): out_path = post.destination_path(lang, ".json") out_file = os.path.join(kw['output_folder'], out_path) task = { 'basename': 'render_mustache', 'name': out_file, 'file_dep': post.fragment_deps(lang), 'targets': [out_file], 'actions': [(write_file, (out_file, post, lang))], 'task_dep': ['render_posts'], 'uptodate': [ config_changed({ 1: post.text(lang), 2: post.prev_post, 3: post.next_post, 4: post.title(lang), }) ] } yield task if posts: first_post_data = posts[0].permalink( self.site.config["DEFAULT_LANG"]).replace(".html", ".json") # Copy mustache template src = os.path.join(os.path.dirname(__file__), 'mustache-template.html') dst = os.path.join(kw['output_folder'], 'mustache-template.html') yield { 'basename': 'render_mustache', 'name': dst, 'targets': [dst], 'file_dep': [src], 'actions': [(copy_file, (src, dst))], } # Copy mustache.html with the right starting file in it src = os.path.join(os.path.dirname(__file__), 'mustache.html') dst = os.path.join(kw['output_folder'], 'mustache.html') def copy_mustache(): with codecs.open(src, 'rb', 'utf8') as in_file: with codecs.open(dst, 'wb+', 'utf8') as out_file: data = in_file.read().replace('{{first_post_data}}', first_post_data) out_file.write(data) yield { 'basename': 'render_mustache', 'name': dst, 'targets': [dst], 'file_dep': [src], 'uptodate': [config_changed({1: first_post_data})], 'actions': [(copy_mustache, [])], }
def gen_tasks(self): """Build similarity data for each post.""" self.site.scan_posts() timeline = [ p for p in self.site.timeline if not (p.is_draft or p.is_private) ] kw = { "translations": self.site.translations, "output_folder": self.site.config["OUTPUT_FOLDER"], "similar_count": self.site.config.get('SIMILAR_COUNT', 10) } stopwords = {} for l in self.site.translations: stopwords[l] = get_stop_words(l) def split_text(text, lang="en"): words = text.lower().split() return [w for w in words if w not in stopwords[lang]] yield self.group_task() def tags_similarity(p1, p2): t1 = set(p1.tags) t2 = set(p2.tags) if not (t1 and t2): return 0 # Totally making this up return 2.0 * len(t1.intersection(t2)) / (len(t1) + len(t2)) def title_similarity(p1, p2): t1 = set(split_text(p1.title())) t2 = set(split_text(p2.title())) if not (t1 and t2): return 0 # Totally making this up return 2.0 * len(t1.intersection(t2)) / (len(t1) + len(t2)) indexes = {} dictionaries = {} lsis = {} def create_idx(indexes, dictionaries, lsis, lang): texts = [] for p in timeline: texts.append( split_text(p.text(strip_html=True, lang=lang), lang=lang)) dictionary = gensim.corpora.Dictionary(texts) corpus = [dictionary.doc2bow(text) for text in texts] lsi = gensim.models.LsiModel(corpus, id2word=dictionary, num_topics=2) indexes[lang] = gensim.similarities.MatrixSimilarity(lsi[corpus]) dictionaries[lang] = dictionary lsis[lang] = lsi def write_similar(path, post, lang, indexes=indexes, dictionaries=dictionaries, lsis=lsis): if lang not in dictionaries: create_idx(indexes, dictionaries, lsis, lang) doc = split_text(post.text(lang), lang) vec_bow = dictionaries[lang].doc2bow(doc) vec_lsi = lsis[lang][vec_bow] body_sims = indexes[lang][vec_lsi] tag_sims = [tags_similarity(post, p) for p in timeline] title_sims = [title_similarity(post, p) for p in timeline] full_sims = [ tag_sims[i] + title_sims[i] + body_sims[i] * 1.5 for i in range(len(timeline)) ] full_sims = sorted(enumerate(full_sims), key=lambda item: -item[1]) idx = timeline.index(post) related = [(timeline[s[0]], s[1], tag_sims[s[0]], title_sims[s[0]], body_sims[s[0]]) for s in full_sims[:kw['similar_count'] + 1] if s[0] != idx] data = [] for p, score, tag, title, body in related: data.append({ 'url': '/' + p.destination_path(sep='/'), 'title': p.title(), 'score': score, 'detailed_score': [tag, title, float(body)], }) with open(path, 'w+') as outf: json.dump(data, outf) for lang in self.site.translations: file_dep = [p.translated_source_path(lang) for p in timeline] uptodate = utils.config_changed({1: kw}, 'similarity') for i, post in enumerate(timeline): out_name = os.path.join( kw['output_folder'], post.destination_path(lang=lang)) + '.related.json' task = { 'basename': self.name, 'name': out_name, 'targets': [out_name], 'actions': [(write_similar, (out_name, post, lang))], 'file_dep': file_dep, 'uptodate': [uptodate], 'clean': True, } yield task
def gen_tasks(self): """Generate RSS feeds.""" kw = { "translations": self.site.config["TRANSLATIONS"], "filters": self.site.config["FILTERS"], "blog_title": self.site.config["BLOG_TITLE"], "site_url": self.site.config["SITE_URL"], "base_url": self.site.config["BASE_URL"], "blog_description": self.site.config["BLOG_DESCRIPTION"], "output_folder": self.site.config["OUTPUT_FOLDER"], "feed_teasers": self.site.config["FEED_TEASERS"], "feed_plain": self.site.config["FEED_PLAIN"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "feed_length": self.site.config['FEED_LENGTH'], "feed_previewimage": self.site.config["FEED_PREVIEWIMAGE"], "tzinfo": self.site.tzinfo, "feed_read_more_link": self.site.config["FEED_READ_MORE_LINK"], "feed_links_append_query": self.site.config["FEED_LINKS_APPEND_QUERY"], } self.site.scan_posts() # Check for any changes in the state of use_in_feeds for any post. # Issue #934 kw['use_in_feeds_status'] = ''.join( ['T' if x.use_in_feeds else 'F' for x in self.site.timeline]) yield self.group_task() for lang in kw["translations"]: output_name = os.path.join(kw['output_folder'], self.site.path("rss", None, lang)) deps = [] deps_uptodate = [] if kw["show_untranslated_posts"]: posts = self.site.posts[:kw['feed_length']] else: posts = [ x for x in self.site.posts if x.is_translation_available(lang) ][:kw['feed_length']] for post in posts: deps += post.deps(lang) deps_uptodate += post.deps_uptodate(lang) feed_url = urljoin(self.site.config['BASE_URL'], self.site.link("rss", None, lang).lstrip('/')) task = { 'basename': 'generate_rss', 'name': os.path.normpath(output_name), 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, kw["blog_title"](lang), kw["site_url"], kw["blog_description"](lang), posts, output_name, kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], feed_url, _enclosure, kw["feed_links_append_query"]))], 'task_dep': ['render_posts'], 'clean': True, 'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.rss')] + deps_uptodate, } yield utils.apply_filters(task, kw['filters'])
def gen_tasks(self): """Build HTML fragments from metadata and text.""" self.site.scan_posts() kw = { "translations": self.site.config["TRANSLATIONS"], "timeline": self.site.timeline, "default_lang": self.site.config["DEFAULT_LANG"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "demote_headers": self.site.config['DEMOTE_HEADERS'], } self.tl_changed = False yield self.group_task() def tl_ch(): self.tl_changed = True yield { 'basename': self.name, 'name': 'timeline_changes', 'actions': [tl_ch], 'uptodate': [utils.config_changed({1: kw['timeline']})], } for lang in kw["translations"]: deps_dict = copy(kw) deps_dict.pop('timeline') for post in kw['timeline']: if not post.is_translation_available( lang ) and not self.site.config['SHOW_UNTRANSLATED_POSTS']: continue # Extra config dependencies picked from config for p in post.fragment_deps(lang): if p.startswith('####MAGIC####CONFIG:'): k = p.split('####MAGIC####CONFIG:', 1)[-1] deps_dict[k] = self.site.config.get(k) dest = post.translated_base_path(lang) file_dep = [ p for p in post.fragment_deps(lang) if not p.startswith("####MAGIC####") ] extra_targets = post.compiler.get_extra_targets( post, lang, dest) task = { 'basename': self.name, 'name': dest, 'file_dep': file_dep, 'targets': [dest] + extra_targets, 'actions': [ (post.compile, (lang, )), (update_deps, ( post, lang, )), ], 'clean': True, 'uptodate': [ utils.config_changed( deps_dict, 'nikola.plugins.task.posts'), lambda p= post, l=lang: self.dependence_on_timeline(p, l) ] + post.fragment_deps_uptodate(lang), 'task_dep': ['render_posts:timeline_changes'] } # Apply filters specified in the metadata ff = [x.strip() for x in post.meta('filters', lang).split(',')] flist = [] for i, f in enumerate(ff): if not f: continue _f = self.site.filters.get(f) if _f is not None: # A registered filter flist.append(_f) else: flist.append(f) yield utils.apply_filters(task, {os.path.splitext(dest)[-1]: flist})
def gen_tasks(self): """Render the tag pages and feeds.""" kw = { "translations": self.site.config["TRANSLATIONS"], "blog_title": self.site.config["BLOG_TITLE"], "site_url": self.site.config["SITE_URL"], "base_url": self.site.config["BASE_URL"], "messages": self.site.MESSAGES, "output_folder": self.site.config['OUTPUT_FOLDER'], "filters": self.site.config['FILTERS'], 'tag_path': self.site.config['TAG_PATH'], "tag_pages_are_indexes": self.site.config['TAG_PAGES_ARE_INDEXES'], "tag_pages_descriptions": self.site.config['TAG_PAGES_DESCRIPTIONS'], 'category_path': self.site.config['CATEGORY_PATH'], 'category_prefix': self.site.config['CATEGORY_PREFIX'], "category_pages_are_indexes": self.site.config['CATEGORY_PAGES_ARE_INDEXES'], "category_pages_descriptions": self.site.config['CATEGORY_PAGES_DESCRIPTIONS'], "generate_rss": self.site.config['GENERATE_RSS'], "rss_teasers": self.site.config["RSS_TEASERS"], "rss_plain": self.site.config["RSS_PLAIN"], "show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'], "feed_length": self.site.config['FEED_LENGTH'], "taglist_minimum_post_count": self.site.config['TAGLIST_MINIMUM_POSTS'], "tzinfo": self.site.tzinfo, "pretty_urls": self.site.config['PRETTY_URLS'], "strip_indexes": self.site.config['STRIP_INDEXES'], "index_file": self.site.config['INDEX_FILE'], } self.site.scan_posts() yield self.group_task() yield self.list_tags_page(kw) if not self.site.posts_per_tag and not self.site.posts_per_category: return if kw['category_path'] == kw['tag_path']: tags = { self.slugify_name(tag): tag for tag in self.site.posts_per_tag.keys() } categories = { kw['category_prefix'] + self.slugify_name(category): category for category in self.site.posts_per_category.keys() } intersect = set(tags.keys()) & set(categories.keys()) if len(intersect) > 0: for slug in intersect: utils.LOGGER.error( "Category '{0}' and tag '{1}' both have the same slug '{2}'!" .format(categories[slug], tags[slug], slug)) sys.exit(1) tag_list = list(self.site.posts_per_tag.items()) cat_list = list(self.site.posts_per_category.items()) def render_lists(tag, posts, is_category=True): post_list = sorted(posts, key=lambda a: a.date) post_list.reverse() for lang in kw["translations"]: if kw["show_untranslated_posts"]: filtered_posts = post_list else: filtered_posts = [ x for x in post_list if x.is_translation_available(lang) ] if kw["generate_rss"]: yield self.tag_rss(tag, lang, filtered_posts, kw, is_category) # Render HTML if kw['category_pages_are_indexes'] if is_category else kw[ 'tag_pages_are_indexes']: yield self.tag_page_as_index(tag, lang, filtered_posts, kw, is_category) else: yield self.tag_page_as_list(tag, lang, filtered_posts, kw, is_category) for tag, posts in tag_list: for task in render_lists(tag, posts, False): yield task for tag, posts in cat_list: if tag == '': # This is uncategorized posts continue for task in render_lists(tag, posts, True): yield task # Tag cloud json file tag_cloud_data = {} for tag, posts in self.site.posts_per_tag.items(): tag_posts = dict( posts=[{ 'title': post.meta[post.default_lang]['title'], 'date': post.date.strftime('%m/%d/%Y'), 'isodate': post.date.isoformat(), 'url': post.permalink(post.default_lang) } for post in reversed( sorted(self.site.timeline, key=lambda post: post.date)) if tag in post.alltags]) tag_cloud_data[tag] = [ len(posts), self.site.link('tag', tag, self.site.config['DEFAULT_LANG']), tag_posts ] output_name = os.path.join(kw['output_folder'], 'assets', 'js', 'tag_cloud_data.json') def write_tag_data(data): utils.makedirs(os.path.dirname(output_name)) with open(output_name, 'w+') as fd: json.dump(data, fd) if self.site.config['WRITE_TAG_CLOUD']: task = {'basename': str(self.name), 'name': str(output_name)} task['uptodate'] = [ utils.config_changed(tag_cloud_data, 'nikola.plugins.task.tags:tagdata') ] task['targets'] = [output_name] task['actions'] = [(write_tag_data, [tag_cloud_data])] task['clean'] = True yield utils.apply_filters(task, kw['filters'])
def _create_tags_page(self, kw, include_tags=True, include_categories=True): """a global "all your tags/categories" page for each language""" tags = list([ tag for tag in self.site.posts_per_tag.keys() if len(self.site.posts_per_tag[tag]) >= kw["taglist_minimum_post_count"] ]) categories = list(self.site.posts_per_category.keys()) # We want our tags to be sorted case insensitive tags.sort(key=lambda a: a.lower()) categories.sort(key=lambda a: a.lower()) has_tags = (tags != ['']) and include_tags has_categories = (categories != ['']) and include_categories template_name = "tags.tmpl" kw = kw.copy() if include_tags: kw['tags'] = tags if include_categories: kw['categories'] = categories for lang in kw["translations"]: output_name = os.path.join( kw['output_folder'], self.site.path('tag_index' if has_tags else 'category_index', None, lang)) output_name = output_name context = {} if has_categories and has_tags: context["title"] = kw["messages"][lang]["Tags and Categories"] elif has_categories: context["title"] = kw["messages"][lang]["Categories"] else: context["title"] = kw["messages"][lang]["Tags"] if has_tags: context["items"] = [(tag, self.site.link("tag", tag, lang)) for tag in tags] else: context["items"] = None if has_categories: context["cat_items"] = [(tag, self.site.link("category", tag, lang)) for tag in categories] else: context["cat_items"] = None context["permalink"] = self.site.link( "tag_index" if has_tags else "category_index", None, lang) context["description"] = context["title"] task = self.site.generic_post_list_renderer( lang, [], output_name, template_name, kw['filters'], context, ) task['uptodate'] = task['uptodate'] + [ utils.config_changed(kw, 'nikola.plugins.task.tags:page') ] task['basename'] = str(self.name) yield task
def gen_tasks(self): """Generate Windows Live Tiles and notifications.""" kw = { "default_lang": self.site.config["DEFAULT_LANG"], "site_url": self.site.config["BASE_URL"], "output_folder": self.site.config["OUTPUT_FOLDER"], "show_untranslated_posts": self.site.config["SHOW_UNTRANSLATED_POSTS"], "windows_live_tiles": self.site.config["WINDOWS_LIVE_TILES"], } msapplication_assets = os.path.join(kw["output_folder"], "assets", "msapplication") if not os.path.exists(msapplication_assets): os.makedirs(msapplication_assets) self.site.scan_posts() yield self.group_task() deps = [] if kw["show_untranslated_posts"]: posts = self.site.posts[:5] else: posts = [ x for x in self.site.posts if x.is_translation_available(kw["default_lang"]) ][:5] for post in posts: deps += post.deps(kw["default_lang"]) for i, post in zip(range(len(posts)), posts): notification_deps = post.deps(kw["default_lang"]) output_name = os.path.join( msapplication_assets, "tile_notification" + str(i + 1) + ".xml") titles = { "maintitle": post.title(kw["default_lang"]), "title1": posts[0].title(kw["default_lang"]), "title2": posts[1].title(kw["default_lang"]), "title3": posts[2].title(kw["default_lang"]) } yield { "basename": "windows_live_tiles", "name": os.path.normpath(output_name), "file_dep": notification_deps, "targets": [output_name], "actions": [(self.generate_notification_tile, (output_name, kw["default_lang"], kw["windows_live_tiles"]["tileimages"], titles, post.meta[kw["default_lang"]]["previewimage"]))], "task_dep": ["render_posts"], "clean": True, "uptodate": [utils.config_changed(kw)], } browserconfig_output_name = os.path.join(kw["output_folder"], "browserconfig.xml") yield { "basename": "windows_live_tiles", "name": os.path.normpath(browserconfig_output_name), "file_dep": deps, "targets": [browserconfig_output_name], "actions": [(self.generate_browserconfig, (browserconfig_output_name, kw["windows_live_tiles"], len(posts)))], "task_dep": ["render_posts"], "clean": True, "uptodate": [utils.config_changed(kw)], }
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'])
def gen_tasks(self): """Render the tag pages and feeds.""" kw = { "translations": self.site.config["TRANSLATIONS"], "blog_title": self.site.config["BLOG_TITLE"], "blog_url": self.site.config["BLOG_URL"], "blog_description": self.site.config["BLOG_DESCRIPTION"], "messages": self.site.MESSAGES, "output_folder": self.site.config['OUTPUT_FOLDER'], "filters": self.site.config['FILTERS'], "tag_pages_are_indexes": self.site.config['TAG_PAGES_ARE_INDEXES'], "index_display_post_count": self.site.config['INDEX_DISPLAY_POST_COUNT'], "index_teasers": self.site.config['INDEX_TEASERS'], } if not self.site.posts_per_tag: yield { 'basename': self.name, 'actions': [], } return def page_name(tagname, i, lang): """Given tag, n, returns a page name.""" name = self.site.path("tag", tag, lang) if i: name = name.replace('.html', '-%s.html' % i) return name for tag, posts in self.site.posts_per_tag.items(): post_list = [self.site.global_data[post] for post in posts] post_list.sort(cmp=lambda a, b: cmp(a.date, b.date)) post_list.reverse() for lang in kw["translations"]: #Render RSS output_name = os.path.join( kw['output_folder'], self.site.path("tag_rss", tag, lang)) deps = [] post_list = [ self.site.global_data[post] for post in posts if self.site.global_data[post].use_in_feeds ] post_list.sort(cmp=lambda a, b: cmp(a.date, b.date)) post_list.reverse() for post in post_list: deps += post.deps(lang) yield { 'name': output_name.encode('utf8'), 'file_dep': deps, 'targets': [output_name], 'actions': [(utils.generic_rss_renderer, (lang, "%s (%s)" % (kw["blog_title"], tag), kw["blog_url"], kw["blog_description"], post_list, output_name))], 'clean': True, 'uptodate': [utils.config_changed(kw)], 'basename': self.name } # Render HTML if kw['tag_pages_are_indexes']: # We render a sort of index page collection using only # this tag's posts. # FIXME: deduplicate this with render_indexes template_name = "index.tmpl" # Split in smaller lists lists = [] while post_list: lists.append( post_list[:kw["index_display_post_count"]]) post_list = post_list[kw["index_display_post_count"]:] num_pages = len(lists) for i, post_list in enumerate(lists): context = {} # On a tag page, the feeds include the tag's feeds rss_link = \ """<link rel="alternate" type="application/rss+xml" """\ """type="application/rss+xml" title="RSS for tag """\ """%s (%s)" href="%s">""" % \ (tag, lang, self.site.link("tag_rss", tag, lang)) context['rss_link'] = rss_link output_name = os.path.join(kw['output_folder'], page_name(tag, i, lang)) context["title"] = kw["messages"][lang][ u"Posts about %s:"] % tag context["prevlink"] = None context["nextlink"] = None context['index_teasers'] = kw['index_teasers'] if i > 1: context["prevlink"] = os.path.basename( page_name(tag, i - 1, lang)) if i == 1: context["prevlink"] = os.path.basename( page_name(tag, 0, lang)) if i < num_pages - 1: context["nextlink"] = os.path.basename( page_name(tag, i + 1, lang)) context["permalink"] = self.site.link("tag", tag, lang) context["tag"] = tag for task in self.site.generic_post_list_renderer( lang, post_list, output_name, template_name, kw['filters'], context, ): task['uptodate'] = task.get('updtodate', []) +\ [utils.config_changed(kw)] task['basename'] = self.name yield task else: # We render a single flat link list with this tag's posts template_name = "tag.tmpl" output_name = os.path.join( kw['output_folder'], self.site.path("tag", tag, lang)) context = {} context["lang"] = lang context["title"] = kw["messages"][lang][ u"Posts about %s:"] % tag context["items"] = [ ("[%s] %s" % (post.date, post.title(lang)), post.permalink(lang)) for post in post_list ] context["permalink"] = self.site.link("tag", tag, lang) context["tag"] = tag for task in self.site.generic_post_list_renderer( lang, post_list, output_name, template_name, kw['filters'], context, ): task['uptodate'] = task.get('updtodate', []) +\ [utils.config_changed(kw)] task['basename'] = self.name yield task # And global "all your tags" page tags = self.site.posts_per_tag.keys() tags.sort() template_name = "tags.tmpl" kw['tags'] = tags for lang in kw["translations"]: output_name = os.path.join(kw['output_folder'], self.site.path('tag_index', None, lang)) context = {} context["title"] = kw["messages"][lang][u"Tags"] context["items"] = [(tag, self.site.link("tag", tag, lang)) for tag in tags] context["permalink"] = self.site.link("tag_index", None, lang) for task in self.site.generic_post_list_renderer( lang, [], output_name, template_name, kw['filters'], context, ): task['uptodate'] = task.get('updtodate', []) +\ [utils.config_changed(kw)] yield task
def gen_tasks(self): """Render image galleries.""" self.image_ext_list = self.image_ext_list_builtin self.image_ext_list.extend( self.site.config.get('EXTRA_IMAGE_EXTENSIONS', [])) for k, v in self.site.GLOBAL_CONTEXT['template_hooks'].items(): self.kw['||template_hooks|{0}||'.format(k)] = v.calculate_deps() self.site.scan_posts() yield self.group_task() template_name = "gallery.tmpl" # Create all output folders for task in self.create_galleries(): yield task # For each gallery: for gallery, input_folder, output_folder in self.gallery_list: # Create subfolder list folder_list = [ (x, x.split(os.sep)[-2]) for x in glob.glob(os.path.join(gallery, '*') + os.sep) ] # Parse index into a post (with translations) post = self.parse_index(gallery, input_folder, output_folder) # Create image list, filter exclusions image_list = self.get_image_list(gallery) # Create thumbnails and large images in destination for image in image_list: for task in self.create_target_images(image, input_folder): yield task # Remove excluded images for image in self.get_excluded_images(gallery): for task in self.remove_excluded_image(image, input_folder): yield task for lang in self.kw['translations']: # save navigation links as dependencies self.kw['navigation_links|{0}'.format(lang)] = self.kw[ 'global_context']['navigation_links'](lang) # Create index.html for each language for lang in self.kw['translations']: dst = os.path.join(self.kw['output_folder'], self.site.path("gallery", gallery, lang)) dst = os.path.normpath(dst) for k in self.site._GLOBAL_CONTEXT_TRANSLATABLE: self.kw[k] = self.site.GLOBAL_CONTEXT[k](lang) context = {} # Do we have a metadata file? meta_path, order, captions, img_metadata = self.find_metadata( gallery, lang) context['meta_path'] = meta_path context['order'] = order context['captions'] = captions context["lang"] = lang if post: context["title"] = post.title(lang) else: context["title"] = os.path.basename(gallery) context["description"] = None image_name_list = [os.path.basename(p) for p in image_list] if captions: img_titles = [] for fn in image_name_list: if fn in captions: img_titles.append(captions[fn]) else: if self.kw['use_filename_as_title']: img_titles.append(fn) else: img_titles.append('') self.logger.debug( "Image {0} found in gallery but not listed in {1}" .format(fn, context['meta_path'])) elif self.kw['use_filename_as_title']: img_titles = [] for fn in image_name_list: name_without_ext = os.path.splitext( os.path.basename(fn))[0] img_titles.append( utils.unslugify(name_without_ext, lang)) else: img_titles = [''] * len(image_name_list) thumbs = [ '.thumbnail'.join(os.path.splitext(p)) for p in image_list ] thumbs = [ os.path.join(self.kw['output_folder'], output_folder, os.path.relpath(t, input_folder)) for t in thumbs ] dst_img_list = [ os.path.join(output_folder, os.path.relpath(t, input_folder)) for t in image_list ] dest_img_list = [ os.path.join(self.kw['output_folder'], t) for t in dst_img_list ] folders = [] # Generate friendly gallery names for path, folder in folder_list: fpost = self.parse_index(path, input_folder, output_folder) if fpost: ft = fpost.title(lang) or folder else: ft = folder if not folder.endswith('/'): folder += '/' folders.append((folder, ft)) context["gallery_path"] = gallery context["folders"] = natsort.natsorted(folders, alg=natsort.ns.F | natsort.ns.IC) context["crumbs"] = utils.get_crumbs(gallery, index_folder=self, lang=lang) context["permalink"] = self.site.link("gallery", gallery, lang) context["enable_comments"] = self.kw['comments_in_galleries'] context["thumbnail_size"] = self.kw["thumbnail_size"] context["pagekind"] = ["gallery_front"] if post: yield { 'basename': self.name, 'name': post.translated_base_path(lang), 'targets': [post.translated_base_path(lang)], 'file_dep': post.fragment_deps(lang), 'actions': [(post.compile, [lang])], 'uptodate': [ utils.config_changed( self.kw.copy(), 'nikola.plugins.task.galleries:post') ] + post.fragment_deps_uptodate(lang) } context['post'] = post else: context['post'] = None file_dep = self.site.template_system.template_deps( template_name) + image_list + thumbs file_dep_dest = self.site.template_system.template_deps( template_name) + dest_img_list + thumbs if post: file_dep += [ post.translated_base_path(l) for l in self.kw['translations'] ] file_dep_dest += [ post.translated_base_path(l) for l in self.kw['translations'] ] context["pagekind"] = ["gallery_page"] yield utils.apply_filters( { 'basename': self.name, 'name': dst, 'file_dep': file_dep, 'targets': [dst], 'actions': [(self.render_gallery_index, (template_name, dst, context.copy(), dest_img_list, img_titles, thumbs, img_metadata))], 'clean': True, 'uptodate': [ utils.config_changed( { 1: self.kw.copy(), 2: self.site.config["COMMENTS_IN_GALLERIES"], 3: context.copy(), }, 'nikola.plugins.task.galleries:gallery') ], }, self.kw['filters']) # RSS for the gallery if self.kw["generate_rss"]: rss_dst = os.path.join( self.kw['output_folder'], self.site.path("gallery_rss", gallery, lang)) rss_dst = os.path.normpath(rss_dst) yield utils.apply_filters( { 'basename': self.name, 'name': rss_dst, 'file_dep': file_dep_dest, 'targets': [rss_dst], 'actions': [(self.gallery_rss, (image_list, dst_img_list, img_titles, lang, self.site.link("gallery_rss", gallery, lang), rss_dst, context['title']))], 'clean': True, 'uptodate': [ utils.config_changed({ 1: self.kw.copy(), }, 'nikola.plugins.task.galleries:rss') ], }, self.kw['filters'])
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'])
def create_target_images(self, img, input_path): """Copy images to output.""" gallery_name = os.path.dirname(img) output_gallery = os.path.dirname( os.path.join(self.kw["output_folder"], self.site.path("gallery_global", gallery_name))) # Do thumbnails and copy originals # img is "galleries/name/image_name.jpg" # img_name is "image_name.jpg" # fname, ext are "image_name", ".jpg" # thumb_path is # "output/GALLERY_PATH/name/image_name.thumbnail.jpg" img_name = os.path.basename(img) fname, ext = os.path.splitext(img_name) thumb_path = os.path.join(output_gallery, ".thumbnail".join([fname, ext])) # thumb_path is "output/GALLERY_PATH/name/image_name.jpg" orig_dest_path = os.path.join(output_gallery, img_name) yield utils.apply_filters( { 'basename': self.name, 'name': thumb_path, 'file_dep': [img], 'targets': [thumb_path], 'actions': [(self.resize_image, (img, thumb_path, self.kw['thumbnail_size'], True, self.kw['preserve_exif_data'], self.kw['exif_whitelist'], self.kw['preserve_icc_profiles']))], 'clean': True, 'uptodate': [ utils.config_changed( {1: self.kw['thumbnail_size']}, 'nikola.plugins.task.galleries:resize_thumb') ], }, self.kw['filters']) yield utils.apply_filters( { 'basename': self.name, 'name': orig_dest_path, 'file_dep': [img], 'targets': [orig_dest_path], 'actions': [(self.resize_image, (img, orig_dest_path, self.kw['max_image_size'], True, self.kw['preserve_exif_data'], self.kw['exif_whitelist'], self.kw['preserve_icc_profiles']))], 'clean': True, 'uptodate': [ utils.config_changed( {1: self.kw['max_image_size']}, 'nikola.plugins.task.galleries:resize_max') ], }, self.kw['filters'])
def gen_tasks(self): """Render image galleries.""" kw = { 'thumbnail_size': self.site.config['THUMBNAIL_SIZE'], 'max_image_size': self.site.config['MAX_IMAGE_SIZE'], 'output_folder': self.site.config['OUTPUT_FOLDER'], 'cache_folder': self.site.config['CACHE_FOLDER'], 'default_lang': self.site.config['DEFAULT_LANG'], 'blog_description': self.site.config['BLOG_DESCRIPTION'], 'use_filename_as_title': self.site.config['USE_FILENAME_AS_TITLE'], 'gallery_path': self.site.config['GALLERY_PATH'] } # FIXME: lots of work is done even when images don't change, # which should be moved into the task. template_name = "gallery.tmpl" gallery_list = [] for root, dirs, files in os.walk(kw['gallery_path']): gallery_list.append(root) if not gallery_list: yield { 'basename': str('render_galleries'), 'actions': [], } return # gallery_path is "gallery/name" for gallery_path in gallery_list: # gallery_name is "name" splitted = gallery_path.split(os.sep)[1:] if not splitted: gallery_name = '' else: gallery_name = os.path.join(*splitted) # Task to create gallery in output/ # output_gallery is "output/GALLERY_PATH/name" output_gallery = os.path.dirname( os.path.join(kw["output_folder"], self.site.path("gallery", gallery_name, None))) output_name = os.path.join(output_gallery, self.site.config['INDEX_FILE']) if not os.path.isdir(output_gallery): yield { 'basename': str('render_galleries'), 'name': output_gallery, 'actions': [(os.makedirs, (output_gallery, ))], 'targets': [output_gallery], 'clean': True, 'uptodate': [utils.config_changed(kw)], } # Gather image_list contains "gallery/name/image_name.jpg" image_list = glob.glob(gallery_path + "/*jpg") +\ glob.glob(gallery_path + "/*JPG") +\ glob.glob(gallery_path + "/*png") +\ glob.glob(gallery_path + "/*PNG") # Filter ignored images try: exclude_path = os.path.join(gallery_path, "exclude.meta") try: f = open(exclude_path, 'r') excluded_image_name_list = f.read().split() except IOError: excluded_image_name_list = [] excluded_image_list = [ "{0}/{1}".format(gallery_path, i) for i in excluded_image_name_list ] image_set = set(image_list) - set(excluded_image_list) image_list = list(image_set) except IOError: pass # List of sub-galleries folder_list = [ x.split(os.sep)[-2] for x in glob.glob(os.path.join(gallery_path, '*') + os.sep) ] crumbs = utils.get_crumbs(gallery_path) image_list = [x for x in image_list if "thumbnail" not in x] # Sort by date image_list.sort(key=lambda a: self.image_date(a)) image_name_list = [os.path.basename(x) for x in image_list] # Do thumbnails and copy originals thumbs = [] for img, img_name in list(zip(image_list, image_name_list)): # img is "galleries/name/image_name.jpg" # img_name is "image_name.jpg" # fname, ext are "image_name", ".jpg" fname, ext = os.path.splitext(img_name) # thumb_path is # "output/GALLERY_PATH/name/image_name.thumbnail.jpg" thumb_path = os.path.join(output_gallery, ".thumbnail".join([fname, ext])) # thumb_path is "output/GALLERY_PATH/name/image_name.jpg" orig_dest_path = os.path.join(output_gallery, img_name) thumbs.append(os.path.basename(thumb_path)) yield { 'basename': str('render_galleries'), 'name': thumb_path, 'file_dep': [img], 'targets': [thumb_path], 'actions': [(self.resize_image, (img, thumb_path, kw['thumbnail_size']))], 'clean': True, 'uptodate': [utils.config_changed(kw)], } yield { 'basename': str('render_galleries'), 'name': orig_dest_path, 'file_dep': [img], 'targets': [orig_dest_path], 'actions': [(self.resize_image, (img, orig_dest_path, kw['max_image_size']))], 'clean': True, 'uptodate': [utils.config_changed(kw)], } # Remove excluded images if excluded_image_name_list: for img, img_name in zip(excluded_image_list, excluded_image_name_list): # img_name is "image_name.jpg" # fname, ext are "image_name", ".jpg" fname, ext = os.path.splitext(img_name) excluded_thumb_dest_path = os.path.join( output_gallery, ".thumbnail".join([fname, ext])) excluded_dest_path = os.path.join(output_gallery, img_name) yield { 'basename': str('render_galleries_clean'), 'name': excluded_thumb_dest_path, 'file_dep': [exclude_path], #'targets': [excluded_thumb_dest_path], 'actions': [(utils.remove_file, (excluded_thumb_dest_path, ))], 'clean': True, 'uptodate': [utils.config_changed(kw)], } yield { 'basename': str('render_galleries_clean'), 'name': excluded_dest_path, 'file_dep': [exclude_path], #'targets': [excluded_dest_path], 'actions': [(utils.remove_file, (excluded_dest_path, ))], 'clean': True, 'uptodate': [utils.config_changed(kw)], } # Use galleries/name/index.txt to generate a blurb for # the gallery, if it exists. index_path = os.path.join(gallery_path, "index.txt") cache_dir = os.path.join(kw["cache_folder"], 'galleries') if not os.path.isdir(cache_dir): os.makedirs(cache_dir) index_dst_path = os.path.join( cache_dir, str( hashlib.sha224(index_path.encode('utf-8')).hexdigest() + '.html')) if os.path.exists(index_path): compile_html = self.site.get_compiler(index_path).compile_html yield { 'basename': str('render_galleries'), 'name': index_dst_path, 'file_dep': [index_path], 'targets': [index_dst_path], 'actions': [(compile_html, [index_path, index_dst_path, True])], 'clean': True, 'uptodate': [utils.config_changed(kw)], } context = {} context["lang"] = kw["default_lang"] context["title"] = os.path.basename(gallery_path) context["description"] = kw["blog_description"] if kw['use_filename_as_title']: img_titles = [ 'id="{0}" alt="{1}" title="{2}"'.format( fn[:-4], fn[:-4], utils.unslugify(fn[:-4])) for fn in image_name_list ] else: img_titles = [''] * len(image_name_list) # In the future, remove images from context, use photo_array context["images"] = list(zip(image_name_list, thumbs, img_titles)) context["folders"] = folder_list context["crumbs"] = crumbs context["permalink"] = self.site.link("gallery", gallery_name, None) context["enable_comments"] = ( self.site.config["COMMENTS_IN_GALLERIES"]) context["thumbnail_size"] = kw["thumbnail_size"] file_dep = self.site.template_system.template_deps( template_name) + image_list yield { 'basename': str('render_galleries'), 'name': output_name, 'file_dep': file_dep, 'targets': [output_name], 'actions': [(self.render_gallery_index, (template_name, output_name, context, index_dst_path, image_name_list, thumbs, file_dep, kw))], 'clean': True, 'uptodate': [ utils.config_changed({ 1: kw, 2: self.site.config['GLOBAL_CONTEXT'], 3: self.site.config["COMMENTS_IN_GALLERIES"], 4: context, }) ], }
def gen_tasks(self): """Generate a sitemap.""" kw = { "base_url": self.site.config["BASE_URL"], "site_url": self.site.config["SITE_URL"], "output_folder": self.site.config["OUTPUT_FOLDER"], "strip_indexes": self.site.config["STRIP_INDEXES"], "index_file": self.site.config["INDEX_FILE"], "sitemap_include_fileless_dirs": self.site.config["SITEMAP_INCLUDE_FILELESS_DIRS"], "mapped_extensions": self.site.config.get( 'MAPPED_EXTENSIONS', ['.atom', '.html', '.htm', '.php', '.xml', '.rss']), "robots_exclusions": self.site.config["ROBOTS_EXCLUSIONS"], "filters": self.site.config["FILTERS"], "translations": self.site.config["TRANSLATIONS"], "tzinfo": self.site.config['__tzinfo__'], "sitemap_plugin_revision": 1, } output = kw['output_folder'] base_url = kw['base_url'] mapped_exts = kw['mapped_extensions'] output_path = kw['output_folder'] sitemapindex_path = os.path.join(output_path, "sitemapindex.xml") sitemap_path = os.path.join(output_path, "sitemap.xml") base_path = get_base_path(kw['base_url']) sitemapindex = {} urlset = {} def scan_locs(): """Scan site locations.""" for root, dirs, files in os.walk(output, followlinks=True): if not dirs and not files and not kw[ 'sitemap_include_fileless_dirs']: continue # Totally empty, not on sitemap path = os.path.relpath(root, output) # ignore the current directory. if path == '.': path = syspath = '' else: syspath = path + os.sep path = path.replace(os.sep, '/') + '/' lastmod = self.get_lastmod(root) loc = urljoin(base_url, base_path + path) if kw['index_file'] in files and kw[ 'strip_indexes']: # ignore folders when not stripping urls post = self.site.post_per_file.get(syspath + kw['index_file']) if post and (post.is_draft or post.is_private or post.publish_later): continue alternates = [] if post: for lang in post.translated_to: alt_url = post.permalink(lang=lang, absolute=True) if encodelink(loc) == alt_url: continue alternates.append( alternates_format.format(lang, alt_url)) urlset[loc] = loc_format.format(encodelink(loc), lastmod, ''.join(alternates)) for fname in files: if kw['strip_indexes'] and fname == kw['index_file']: continue # We already mapped the folder if os.path.splitext(fname)[-1] in mapped_exts: real_path = os.path.join(root, fname) path = os.path.relpath(real_path, output) if path.endswith( kw['index_file']) and kw['strip_indexes']: # ignore index files when stripping urls continue if not robot_fetch(path): continue # read in binary mode to make ancient files work fh = open(real_path, 'rb') filehead = fh.read(1024) fh.close() if path.endswith('.html') or path.endswith( '.htm') or path.endswith('.php'): # Ignores "html" files without doctype if b'<!doctype html' not in filehead.lower(): continue # Ignores "html" files with noindex robot directives robots_directives = [ b'<meta content=noindex name=robots', b'<meta content=none name=robots', b'<meta name=robots content=noindex', b'<meta name=robots content=none' ] lowquothead = filehead.lower().decode( 'utf-8', 'ignore').replace('"', '').encode('utf-8') if any([ robot_directive in lowquothead for robot_directive in robots_directives ]): continue # put Atom and RSS in sitemapindex[] instead of in urlset[], # sitemap_path is included after it is generated if path.endswith('.xml') or path.endswith( '.atom') or path.endswith('.rss'): known_elm_roots = (b'<feed', b'<rss', b'<urlset') if any([ elm_root in filehead.lower() for elm_root in known_elm_roots ]) and path != sitemap_path: path = path.replace(os.sep, '/') lastmod = self.get_lastmod(real_path) loc = urljoin(base_url, base_path + path) sitemapindex[loc] = sitemap_format.format( encodelink(loc), lastmod) continue else: continue # ignores all XML files except those presumed to be RSS post = self.site.post_per_file.get(syspath) if post and (post.is_draft or post.is_private or post.publish_later): continue path = path.replace(os.sep, '/') lastmod = self.get_lastmod(real_path) loc = urljoin(base_url, base_path + path) alternates = [] if post: for lang in post.translated_to: alt_url = post.permalink(lang=lang, absolute=True) if encodelink(loc) == alt_url: continue alternates.append( alternates_format.format(lang, alt_url)) urlset[loc] = loc_format.format( encodelink(loc), lastmod, '\n'.join(alternates)) def robot_fetch(path): """Check if robots can fetch a file.""" for rule in kw["robots_exclusions"]: robot = robotparser.RobotFileParser() robot.parse(["User-Agent: *", "Disallow: {0}".format(rule)]) if not robot.can_fetch("*", '/' + path): return False # not robot food return True def write_sitemap(): """Write sitemap to file.""" # Have to rescan, because files may have been added between # task dep scanning and task execution with io.open(sitemap_path, 'w+', encoding='utf8') as outf: outf.write(urlset_header) for k in sorted(urlset.keys()): outf.write(urlset[k]) outf.write(urlset_footer) sitemap_url = urljoin(base_url, base_path + "sitemap.xml") sitemapindex[sitemap_url] = sitemap_format.format( sitemap_url, self.get_lastmod(sitemap_path)) def write_sitemapindex(): """Write sitemap index.""" with io.open(sitemapindex_path, 'w+', encoding='utf8') as outf: outf.write(sitemapindex_header) for k in sorted(sitemapindex.keys()): outf.write(sitemapindex[k]) outf.write(sitemapindex_footer) def scan_locs_task(): """Yield a task to calculate the dependencies of the sitemap. Other tasks can depend on this output, instead of having to scan locations. """ scan_locs() # Generate a list of file dependencies for the actual generation # task, so rebuilds are triggered. (Issue #1032) output = kw["output_folder"] file_dep = [] for i in urlset.keys(): p = os.path.join(output, urlparse(i).path.replace(base_path, '', 1)) if not p.endswith('sitemap.xml') and not os.path.isdir(p): file_dep.append(p) if os.path.isdir(p) and os.path.exists( os.path.join(p, 'index.html')): file_dep.append(p + 'index.html') for i in sitemapindex.keys(): p = os.path.join(output, urlparse(i).path.replace(base_path, '', 1)) if not p.endswith('sitemap.xml') and not os.path.isdir(p): file_dep.append(p) if os.path.isdir(p) and os.path.exists( os.path.join(p, 'index.html')): file_dep.append(p + 'index.html') return {'file_dep': file_dep} yield { "basename": "_scan_locs", "name": "sitemap", "actions": [(scan_locs_task)] } yield self.group_task() yield apply_filters( { "basename": "sitemap", "name": sitemap_path, "targets": [sitemap_path], "actions": [(write_sitemap, )], "uptodate": [config_changed(kw, 'nikola.plugins.task.sitemap:write')], "clean": True, "task_dep": ["render_site"], "calc_dep": ["_scan_locs:sitemap"], }, kw['filters']) yield apply_filters( { "basename": "sitemap", "name": sitemapindex_path, "targets": [sitemapindex_path], "actions": [(write_sitemapindex, )], "uptodate": [ config_changed(kw, 'nikola.plugins.task.sitemap:write_index') ], "clean": True, "file_dep": [sitemap_path] }, kw['filters'])
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 }