diff --git a/sphinx_gemini_builder/__init__.py b/sphinx_gemini_builder/__init__.py index e6d1fab..731fe0c 100644 --- a/sphinx_gemini_builder/__init__.py +++ b/sphinx_gemini_builder/__init__.py @@ -46,6 +46,11 @@ class GeminiBuilder(TextBuilder): super().__init__(app) self.add_footer = True self.baseurl = self.config.gemini_baseurl + self.footer_enabled = True + self.header_enabled = True + if self.baseurl: + # Creepy trick... + self.config.blog_baseurl = self.baseurl self.images = [] def prepare_writing(self, docnames: Set[str]) -> None: diff --git a/sphinx_gemini_builder/ablog_compatibility.py b/sphinx_gemini_builder/ablog_compatibility.py index adcccb3..30ed389 100644 --- a/sphinx_gemini_builder/ablog_compatibility.py +++ b/sphinx_gemini_builder/ablog_compatibility.py @@ -17,11 +17,12 @@ from docutils import nodes import ablog -from ablog.blog import Blog, os_path_join, revise_pending_xrefs +from ablog.blog import Post, Blog, os_path_join, revise_pending_xrefs logger = logging.getLogger(__name__) text_type = str + class Page: """ Mini translator for gemini """ @@ -31,8 +32,11 @@ class Page: self.builder = builder def add_title(self, text: str, level: int = 1) -> None: - self.body += '# ' * level - self.body += text + '\n' + self.body += '#' * level + self.body += ' ' + text + '\n' + + def add_item(self, text: str) -> None: + self.body += '* %s\n' % text def add_link(self, uri: str, desc: str = None) -> None: self.body += '=> %s' % uri @@ -40,6 +44,9 @@ class Page: self.body += ' %s' % desc self.body += '\n' + def add_raw(self, text: str) -> None: + self.body += text + def end_block(self) -> None: self.body += '\n' @@ -48,7 +55,7 @@ class Page: self.body += '\n\n' def write(self): - path = os.path.join(self.builder.outdir, self.docname) + path = os.path.join(self.builder.outdir, self.docname + self.builder.out_suffix) folder = os.path.dirname(path) if not os.path.exists(folder): os.makedirs(folder) @@ -57,7 +64,7 @@ class Page: out.write(self.body) -def to_gemini(builder, post, pagename, fulltext=False): +def to_gemini(builder, post, pagename: str, fulltext: bool = False): """ Convert post to gemini format """ @@ -86,6 +93,61 @@ def to_gemini(builder, post, pagename, fulltext=False): return gemini +def add_post_to_page(builder, doc: Page, post) -> None: + doc.add_title(post.title, 2) + doc.add_link( + builder.config.gemini_baseurl + post.docname + builder.out_suffix, + _("Read post") + ) + doc.end_block() + if post.published: + doc.add_item(_("Date: %s") % post.date.strftime(builder.config.post_date_format)) + else: + doc.add_item(_("Draft")) + + if post.date != post.update: + doc.add_item(_("Update: %s") % post.update.strftime(ablog.post_date_format)) + + for author in post.author: + doc.add_link( + builder.config.gemini_baseurl + author.docname, + _("Author: %s" % str(author)) + ) + + for location in post.location: + doc.add_link( + builder.config.gemini_baseurl + location.docname, + _("Location: %s" % str(location)) + ) + + for language in post.language: + doc.add_link( + builder.config.gemini_baseurl + language.docname, + _("Language: %s" % str(language)) + ) + + for category in post.category: + doc.add_link( + builder.config.gemini_baseurl + category.docname, + _("Category: %s" % str(category)) + ) + + for tag in post.tags: + doc.add_link( + builder.config.gemini_baseurl + tag.docname, + _("Tag: %s" % str(tag)) + ) + + doc.end_block() + + builder.footer_enabled = False + builder.header_enabled = False + doc.add_paragraph(to_gemini(builder, post, post.docname, fulltext=False)) + + builder.footer_enabled = True + builder.header_enabled = True + + def generate_archive_pages(builder): """ Generate archive pages for all posts, categories, tags, authors, and @@ -177,12 +239,34 @@ def generate_archive_pages(builder): doc.add_title(str(collection)) if context["atom_feed"]: doc.add_link( - collection.path+"/atom.xml", + builder.config.gemini_baseurl+collection.path+"/atom.xml", _("Atom feed") ) - doc.end_block() - doc.write() + doc.end_block() + for subcoll in collection: + if isinstance(subcoll, Post): + add_post_to_page(builder, doc, subcoll) + continue + + doc.add_title(str(subcoll), 2) + doc.add_link( + builder.get_target_uri(post.docname), + _("Go to collection") + ) + doc.end_block() + for post in subcoll: + doc.add_link( + builder.config.gemini_baseurl + post.docname + builder.out_suffix, + _("Read post") + ) + + doc.end_block() + + doc.add_raw(ablog_footer(builder)) + doc.add_paragraph(builder.config.gemini_footer) + + doc.write() def generate_atom_feeds(builder): @@ -190,6 +274,8 @@ def generate_atom_feeds(builder): Generate archive pages for all posts, categories, tags, authors, and drafts (from ablog). """ + builder.footer_enabled = False + builder.header_enabled = False blog = Blog(builder.app) url = builder.config.gemini_baseurl @@ -277,3 +363,57 @@ def generate_atom_feeds(builder): with open(feed_path, "w", encoding="utf-8") as out: feed_str = feed.atom_str(pretty=True) out.write(feed_str.decode()) + + builder.footer_enabled = True + builder.header_enabled = True + +def ablog_header(builder) -> str: + """ Generate header for Atom """ + header = '' + blog = Blog(builder.app) + if builder.current_docname in blog.posts: + post = blog.posts[builder.current_docname] + header = _('By %s') % ', '.join([str(author) for author in post.author]) + '\n' + header += post.date.strftime(builder.config.post_date_format) + header += '\n' + + if post.date != post.update: + header += _('Updated on %s') % \ + post.update.strftime(builder.config.post_date_format) + header += '\n' + header += '\n' + + return header + +def ablog_footer(builder) -> str: + """ Generate footer for Atom """ + blog = Blog(builder.app) + + baseurl = builder.config.gemini_baseurl + if not baseurl: + return + + footer = '\n\n' + footer += _('# Blog menu') + footer += '\n' + for title, catalog in [ + (_("Authors"), blog.author), + (_("Locations"), blog.location), + (_("Languages"), blog.language), + (_("Categories"), blog.category), + ]: + footer += '## %s\n' % title + footer += '=> %s %s\n' % (baseurl + catalog.docname + builder.out_suffix, _("All")) + for coll in catalog: + footer += '=> %s %s\n' % (baseurl + coll.docname + builder.out_suffix, str(coll)) + footer += '\n' + + footer += '## %s\n' % _("All posts") + footer += '=> %s %s\n' % (baseurl + blog.archive.docname + builder.out_suffix, _("All posts")) + footer += '\n' + + footer += '## %s\n' % _("Tags") + footer += '=> %s %s\n' % (baseurl + blog.tags.docname + builder.out_suffix, _("Tags")) + footer += '\n' + + return footer diff --git a/sphinx_gemini_builder/writer.py b/sphinx_gemini_builder/writer.py index c601147..74b21d9 100644 --- a/sphinx_gemini_builder/writer.py +++ b/sphinx_gemini_builder/writer.py @@ -93,9 +93,17 @@ class GeminiTranslator(SphinxTranslator): self.pre = True def visit_document(self, node: Element) -> None: - pass + if 'ablog' in self.builder.config.extensions and self.builder.header_enabled: + from .ablog_compatibility import ablog_header + self.add_text(ablog_header(self.builder)) def depart_document(self, node: Element) -> None: + if not self.builder.footer_enabled: + return + + if 'ablog' in self.builder.config.extensions: + from .ablog_compatibility import ablog_footer + self.add_text(ablog_footer(self.builder)) if self.builder.add_footer: self.add_text(self.config.gemini_footer) @@ -402,12 +410,13 @@ class GeminiTranslator(SphinxTranslator): def visit_image(self, node: Element) -> None: self.end_block() uri = relative_uri(self.builder.current_docname, node['uri']) + full_uri = uri if self.builder.config.gemini_baseurl: - uri = self.builder.config.gemini_baseurl + node['uri'] + full_uri = self.builder.config.gemini_baseurl + node['uri'] if 'alt' in node.attributes: - self.add_link(uri, __('[image: %s]') % node['alt']) + self.add_link(full_uri, __('[image: %s]') % node['alt']) else: - self.add_link(uri) + self.add_link(full_uri) self.end_block() self.builder.images.append(posixpath.join(self.builder.imgpath, uri)) raise nodes.SkipNode