diff options
| author | 2013-02-13 18:35:39 -0300 | |
|---|---|---|
| committer | 2013-02-13 18:35:39 -0300 | |
| commit | a40930043121a4b60de8526d58417761a54ab718 (patch) | |
| tree | 383c5cf8e320761ee942619282fe51be625179a7 /nikola/nikola.py | |
| parent | 9c5708cc92af894e414bc76ee35ec2230de5d288 (diff) | |
Imported Upstream version 5.2upstream/5.2
Diffstat (limited to 'nikola/nikola.py')
| -rw-r--r-- | nikola/nikola.py | 307 |
1 files changed, 193 insertions, 114 deletions
diff --git a/nikola/nikola.py b/nikola/nikola.py index 4ce6f61..e10c84e 100644 --- a/nikola/nikola.py +++ b/nikola/nikola.py @@ -32,7 +32,7 @@ import sys try: from urlparse import urlparse, urlsplit, urljoin except ImportError: - from urllib.parse import urlparse, urlsplit, urljoin + from urllib.parse import urlparse, urlsplit, urljoin # NOQA import lxml.html from yapsy.PluginManager import PluginManager @@ -79,39 +79,70 @@ class Nikola(object): # This is the default config # TODO: fill it self.config = { + 'ADD_THIS_BUTTONS': True, + 'ANALYTICS': '', 'ARCHIVE_PATH': "", 'ARCHIVE_FILENAME': "archive.html", - 'DEFAULT_LANG': "en", - 'OUTPUT_FOLDER': 'output', 'CACHE_FOLDER': 'cache', + 'COMMENTS_IN_GALLERIES': False, + 'COMMENTS_IN_STORIES': False, + 'CONTENT_FOOTER': '', + 'DATE_FORMAT': '%Y-%m-%d %H:%M', + 'DEFAULT_LANG': "en", + 'DEPLOY_COMMANDS': [], + 'DISQUS_FORUM': 'nikolademo', + 'FAVICONS': {}, + 'FILE_METADATA_REGEXP': None, 'FILES_FOLDERS': {'files': ''}, - 'LISTINGS_FOLDER': 'listings', - 'ADD_THIS_BUTTONS': True, + 'FILTERS': {}, + 'GALLERY_PATH': 'galleries', 'INDEX_DISPLAY_POST_COUNT': 10, 'INDEX_TEASERS': False, - 'MAX_IMAGE_SIZE': 1280, - 'USE_FILENAME_AS_TITLE': True, - 'SLUG_TAG_PATH': False, 'INDEXES_TITLE': "", 'INDEXES_PAGES': "", - 'FILTERS': {}, - 'USE_BUNDLES': True, - 'TAG_PAGES_ARE_INDEXES': False, - 'THEME': 'default', + 'INDEX_PATH': '', + 'LICENSE': '', + 'LISTINGS_FOLDER': 'listings', + 'MAX_IMAGE_SIZE': 1280, + 'OUTPUT_FOLDER': 'output', 'post_compilers': { - "rest": ['.txt', '.rst'], - "markdown": ['.md', '.mdown', '.markdown'], - "html": ['.html', '.htm'], + "rest": ('.txt', '.rst'), + "markdown": ('.md', '.mdown', '.markdown'), + "textile": ('.textile',), + "txt2tags": ('.t2t',), + "bbcode": ('.bb',), + "wiki": ('.wiki',), + "ipynb": ('.ipynb',), + "html": ('.html', '.htm') }, + 'POST_PAGES': ( + ("posts/*.txt", "posts", "post.tmpl", True), + ("stories/*.txt", "stories", "story.tmpl", False), + ), + 'REDIRECTIONS': [], + 'RSS_LINK': None, + 'RSS_PATH': '', + 'RSS_TEASERS': True, + 'SEARCH_FORM': '', + 'SLUG_TAG_PATH': True, + 'STORY_INDEX': False, + 'TAG_PATH': 'categories', + 'TAG_PAGES_ARE_INDEXES': False, + 'THEME': 'site', + 'THUMBNAIL_SIZE': 180, + 'USE_FILENAME_AS_TITLE': True, + 'USE_BUNDLES': True, } + self.config.update(config) self.config['TRANSLATIONS'] = self.config.get('TRANSLATIONS', - {self.config['DEFAULT_LANG']: ''}) + {self.config['DEFAULT_' + 'LANG']: ''}) self.THEMES = utils.get_theme_chain(self.config['THEME']) self.MESSAGES = utils.load_messages(self.THEMES, - self.config['TRANSLATIONS']) + self.config['TRANSLATIONS']) self.plugin_manager = PluginManager(categories_filter={ "Command": Command, @@ -124,7 +155,7 @@ class Nikola(object): self.plugin_manager.setPluginPlaces([ str(os.path.join(os.path.dirname(__file__), 'plugins')), str(os.path.join(os.getcwd(), 'plugins')), - ]) + ]) self.plugin_manager.collectPlugins() self.commands = {} @@ -145,20 +176,40 @@ class Nikola(object): pluginInfo.plugin_object.set_site(self) # set global_context for template rendering - self.GLOBAL_CONTEXT = self.config.get('GLOBAL_CONTEXT', {}) + self.GLOBAL_CONTEXT = { + } + self.GLOBAL_CONTEXT['messages'] = self.MESSAGES self.GLOBAL_CONTEXT['_link'] = self.link self.GLOBAL_CONTEXT['rel_link'] = self.rel_link self.GLOBAL_CONTEXT['abs_link'] = self.abs_link self.GLOBAL_CONTEXT['exists'] = self.file_exists + self.GLOBAL_CONTEXT['add_this_buttons'] = self.config[ 'ADD_THIS_BUTTONS'] self.GLOBAL_CONTEXT['index_display_post_count'] = self.config[ 'INDEX_DISPLAY_POST_COUNT'] self.GLOBAL_CONTEXT['use_bundles'] = self.config['USE_BUNDLES'] + self.GLOBAL_CONTEXT['favicons'] = self.config['FAVICONS'] if 'date_format' not in self.GLOBAL_CONTEXT: self.GLOBAL_CONTEXT['date_format'] = '%Y-%m-%d %H:%M' + self.GLOBAL_CONTEXT['blog_author'] = self.config.get('BLOG_AUTHOR') + self.GLOBAL_CONTEXT['blog_title'] = self.config.get('BLOG_TITLE') + self.GLOBAL_CONTEXT['blog_url'] = self.config.get('BLOG_URL') + self.GLOBAL_CONTEXT['blog_desc'] = self.config.get('BLOG_DESCRIPTION') + self.GLOBAL_CONTEXT['analytics'] = self.config.get('ANALYTICS') + self.GLOBAL_CONTEXT['translations'] = self.config.get('TRANSLATIONS') + self.GLOBAL_CONTEXT['license'] = self.config.get('LICENSE') + self.GLOBAL_CONTEXT['search_form'] = self.config.get('SEARCH_FORM') + self.GLOBAL_CONTEXT['disqus_forum'] = self.config.get('DISQUS_FORUM') + self.GLOBAL_CONTEXT['content_footer'] = self.config.get('CONTENT_FOOTER') + self.GLOBAL_CONTEXT['rss_path'] = self.config.get('RSS_PATH') + self.GLOBAL_CONTEXT['rss_link'] = self.config.get('RSS_LINK') + self.GLOBAL_CONTEXT['sidebar_links'] = self.config.get('SIDEBAR_LINKS') + + self.GLOBAL_CONTEXT.update(self.config.get('GLOBAL_CONTEXT', {})) + # check if custom css exist and is not empty for files_path in list(self.config['FILES_FOLDERS'].keys()): custom_css_path = os.path.join(files_path, 'assets/css/custom.css') @@ -173,8 +224,8 @@ class Nikola(object): pi = self.plugin_manager.getPluginByName( template_sys_name, "TemplateSystem") if pi is None: - sys.stderr.write("Error loading %s template system plugin\n" - % template_sys_name) + sys.stderr.write("Error loading %s template system plugin\n" % + template_sys_name) sys.exit(1) self.template_system = pi.plugin_object lookup_dirs = [os.path.join(utils.get_theme_path(name), "templates") @@ -228,13 +279,16 @@ class Nikola(object): def render_template(self, template_name, output_name, context): local_context = {} local_context["template_name"] = template_name - local_context.update(self.config['GLOBAL_CONTEXT']) + local_context.update(self.GLOBAL_CONTEXT) local_context.update(context) data = self.template_system.render_template( template_name, None, local_context) - assert output_name.startswith(self.config["OUTPUT_FOLDER"]) - url_part = output_name[len(self.config["OUTPUT_FOLDER"]) + 1:] + assert isinstance(output_name, bytes) + assert output_name.startswith( + self.config["OUTPUT_FOLDER"].encode('utf8')) + url_part = output_name.decode('utf8')[len(self.config["OUTPUT_FOLDER"]) + + 1:] # This is to support windows paths url_part = "/".join(url_part.split(os.sep)) @@ -250,7 +304,7 @@ class Nikola(object): if dst_url.netloc: if dst_url.scheme == 'link': # Magic link dst = self.link(dst_url.netloc, dst_url.path.lstrip('/'), - context['lang']) + context['lang']) else: return dst @@ -273,7 +327,7 @@ class Nikola(object): break # Now i is the longest common prefix result = '/'.join(['..'] * (len(src_elems) - i - 1) + - dst_elems[i:]) + dst_elems[i:]) if not result: result = "." @@ -309,6 +363,7 @@ class Nikola(object): * rss (name is ignored) * gallery (name is the gallery name) * listing (name is the source code file name) + * post_path (name is 1st element in a post_pages tuple) The returned value is always a path relative to output, like "categories/whatever.html" @@ -324,38 +379,49 @@ class Nikola(object): if kind == "tag_index": path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['TAG_PATH'], 'index.html'] if _f] + self.config['TAG_PATH'], 'index.html'] if _f] elif kind == "tag": if self.config['SLUG_TAG_PATH']: name = utils.slugify(name) path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['TAG_PATH'], name + ".html"] if _f] + self.config['TAG_PATH'], name + ".html"] if + _f] elif kind == "tag_rss": if self.config['SLUG_TAG_PATH']: name = utils.slugify(name) path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['TAG_PATH'], name + ".xml"] if _f] + self.config['TAG_PATH'], name + ".xml"] if + _f] elif kind == "index": - if name > 0: + if name not in [None, 0]: path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['INDEX_PATH'], 'index-%s.html' % name] if _f] + self.config['INDEX_PATH'], + 'index-%s.html' % name] if _f] else: path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['INDEX_PATH'], 'index.html'] if _f] + self.config['INDEX_PATH'], 'index.html'] + if _f] + elif kind == "post_path": + path = [_f for _f in [self.config['TRANSLATIONS'][lang], + os.path.dirname(name), "index.html"] if _f] elif kind == "rss": path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['RSS_PATH'], 'rss.xml'] if _f] + self.config['RSS_PATH'], 'rss.xml'] if _f] elif kind == "archive": if name: path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['ARCHIVE_PATH'], name, 'index.html'] if _f] + self.config['ARCHIVE_PATH'], name, + 'index.html'] if _f] else: path = [_f for _f in [self.config['TRANSLATIONS'][lang], - self.config['ARCHIVE_PATH'], self.config['ARCHIVE_FILENAME']] if _f] + self.config['ARCHIVE_PATH'], + self.config['ARCHIVE_FILENAME']] if _f] elif kind == "gallery": - path = [_f for _f in [self.config['GALLERY_PATH'], name, 'index.html'] if _f] + path = [_f for _f in [self.config['GALLERY_PATH'], name, + 'index.html'] if _f] elif kind == "listing": - path = [_f for _f in [self.config['LISTINGS_FOLDER'], name + '.html'] if _f] + path = [_f for _f in [self.config['LISTINGS_FOLDER'], name + + '.html'] if _f] if is_link: return '/' + ('/'.join(path)) else: @@ -426,36 +492,48 @@ class Nikola(object): def scan_posts(self): """Scan all the posts.""" if not self._scanned: - print("Scanning posts ") + print("Scanning posts", end='') targets = set([]) - for wildcard, destination, _, use_in_feeds in \ + for wildcard, destination, template_name, use_in_feeds in \ self.config['post_pages']: - print (".") - for base_path in glob.glob(wildcard): - post = Post( - base_path, - self.config['CACHE_FOLDER'], - destination, - use_in_feeds, - self.config['TRANSLATIONS'], - self.config['DEFAULT_LANG'], - self.config['BLOG_URL'], - self.MESSAGES) - for lang, langpath in list(self.config['TRANSLATIONS'].items()): - dest = (destination, langpath, post.pagenames[lang]) - if dest in targets: - raise Exception( - 'Duplicated output path %r in post %r' % - (post.pagenames[lang], base_path)) - targets.add(dest) - self.global_data[post.post_name] = post - if post.use_in_feeds: - self.posts_per_year[ - str(post.date.year)].append(post.post_name) - for tag in post.tags: - self.posts_per_tag[tag].append(post.post_name) - else: - self.pages.append(post) + print(".", end='') + base_len = len(destination.split(os.sep)) + dirname = os.path.dirname(wildcard) + for dirpath, _, _ in os.walk(dirname): + dir_glob = os.path.join(dirpath, + os.path.basename(wildcard)) + dest_dir = os.path.join(*([destination] + + dirpath.split( + os.sep)[base_len:])) + for base_path in glob.glob(dir_glob): + post = Post( + base_path, + self.config['CACHE_FOLDER'], + dest_dir, + use_in_feeds, + self.config['TRANSLATIONS'], + self.config['DEFAULT_LANG'], + self.config['BLOG_URL'], + self.MESSAGES, + template_name, + self.config['FILE_METADATA_REGEXP']) + for lang, langpath in list( + self.config['TRANSLATIONS'].items()): + dest = (destination, langpath, dir_glob, + post.pagenames[lang]) + if dest in targets: + raise Exception( + 'Duplicated output path %r in post %r' % + (post.pagenames[lang], base_path)) + targets.add(dest) + self.global_data[post.post_name] = post + if post.use_in_feeds: + self.posts_per_year[ + str(post.date.year)].append(post.post_name) + for tag in post.tags: + self.posts_per_tag[tag].append(post.post_name) + else: + self.pages.append(post) for name, post in list(self.global_data.items()): self.timeline.append(post) self.timeline.sort(key=lambda p: p.date) @@ -468,52 +546,53 @@ class Nikola(object): self._scanned = True print("done!") - def generic_page_renderer(self, lang, wildcard, - template_name, destination, filters): + def generic_page_renderer(self, lang, post, filters): """Render post fragments to final HTML pages.""" - for post in glob.glob(wildcard): - post_name = os.path.splitext(post)[0] - context = {} - post = self.global_data[post_name] - deps = post.deps(lang) + \ - self.template_system.template_deps(template_name) - context['post'] = post - context['lang'] = lang - context['title'] = post.title(lang) - context['description'] = post.description(lang) - context['permalink'] = post.permalink(lang) - context['page_list'] = self.pages - output_name = os.path.join( - self.config['OUTPUT_FOLDER'], - self.config['TRANSLATIONS'][lang], - destination, - post.pagenames[lang] + ".html") - deps_dict = copy(context) - deps_dict.pop('post') - if post.prev_post: - deps_dict['PREV_LINK'] = [post.prev_post.permalink(lang)] - if post.next_post: - deps_dict['NEXT_LINK'] = [post.next_post.permalink(lang)] - deps_dict['OUTPUT_FOLDER'] = self.config['OUTPUT_FOLDER'] - deps_dict['TRANSLATIONS'] = self.config['TRANSLATIONS'] - deps_dict['global'] = self.config['GLOBAL_CONTEXT'] - - task = { - 'name': output_name.encode('utf-8'), - 'file_dep': deps, - 'targets': [output_name], - 'actions': [(self.render_template, - [template_name, output_name, context])], - 'clean': True, - 'uptodate': [config_changed(deps_dict)], - } - - yield utils.apply_filters(task, filters) - - def generic_post_list_renderer(self, lang, posts, - output_name, template_name, filters, extra_context): + context = {} + deps = post.deps(lang) + \ + self.template_system.template_deps(post.template_name) + context['post'] = post + context['lang'] = lang + context['title'] = post.title(lang) + context['description'] = post.description(lang) + context['permalink'] = post.permalink(lang) + context['page_list'] = self.pages + if post.use_in_feeds: + context['enable_comments'] = True + else: + context['enable_comments'] = self.config['COMMENTS_IN_STORIES'] + output_name = os.path.join(self.config['OUTPUT_FOLDER'], + post.destination_path(lang)).encode('utf8') + deps_dict = copy(context) + deps_dict.pop('post') + if post.prev_post: + deps_dict['PREV_LINK'] = [post.prev_post.permalink(lang)] + if post.next_post: + deps_dict['NEXT_LINK'] = [post.next_post.permalink(lang)] + deps_dict['OUTPUT_FOLDER'] = self.config['OUTPUT_FOLDER'] + deps_dict['TRANSLATIONS'] = self.config['TRANSLATIONS'] + deps_dict['global'] = self.GLOBAL_CONTEXT + deps_dict['comments'] = context['enable_comments'] + + task = { + 'name': output_name, + 'file_dep': deps, + 'targets': [output_name], + 'actions': [(self.render_template, [post.template_name, + output_name, context])], + 'clean': True, + 'uptodate': [config_changed(deps_dict)], + } + + yield utils.apply_filters(task, filters) + + def generic_post_list_renderer(self, lang, posts, output_name, + template_name, filters, extra_context): """Renders pages with lists of posts.""" + # This is a name on disk, has to be bytes + assert isinstance(output_name, bytes) + deps = self.template_system.template_deps(template_name) for post in posts: deps += post.deps(lang) @@ -526,15 +605,15 @@ class Nikola(object): context["nextlink"] = None context.update(extra_context) deps_context = copy(context) - deps_context["posts"] = [(p.titles[lang], p.permalink(lang)) - for p in posts] - deps_context["global"] = self.config['GLOBAL_CONTEXT'] + deps_context["posts"] = [(p.titles[lang], p.permalink(lang)) for p in + posts] + deps_context["global"] = self.GLOBAL_CONTEXT task = { - 'name': output_name.encode('utf8'), + 'name': output_name, 'targets': [output_name], 'file_dep': deps, - 'actions': [(self.render_template, - [template_name, output_name, context])], + 'actions': [(self.render_template, [template_name, output_name, + context])], 'clean': True, 'uptodate': [config_changed(deps_context)] } |
