squidfunk / mkdocs-material

Documentation that simply works
https://squidfunk.github.io/mkdocs-material/
MIT License
19.26k stars 3.4k forks source link

[Change Request] Support `git` option for date metadata on blog posts #6647

Closed McDic closed 6 months ago

McDic commented 6 months ago

Context

No response

Description

Currently mkdocs-material is providing date option on blogs.

---
date:
  created: 2024-01-31 
  updated: 2024-02-01
---

I wanted system automatically guesses the created date and updated date by searching from git commit history. For example;

---
date:
  created: git
  updated: git
---

So the date metadata is automatically provided by a git repo history. I see there were some discussions about git-revision-date-localized (link available below), but it added separated metadata on the page, so I still need to manually set the date and also I think it deconcentrates reader's attentions.

Therefore I propose mkdocs-material to allow git or similar option instead of datetime syntax for date metadata on blog posts.

Related links

mkdocs-material documentations and discussions

git date stuffs

Use Cases

With this option, authors can avoid to manually update the blog post dates when they just want to sync the dates with git history. I am ok with either built-in features or integration with other external plugins.

Visuals

No response

Before submitting

squidfunk commented 6 months ago

Thanks for suggesting. Definitely a reasonable feature request, but nothing we plan to implement in Material for MkDocs, because it can be perfectly implemented as part of a third party plugin or hook ☺️ We already integrate with several git-related plugins, so maybe somebody (or you?) could implement a plugin to automatically set the date of blog posts. In fact, the blog plugin is designed so that metadata can be changed by plugins before blog posts are loaded:

https://github.com/squidfunk/mkdocs-material/blob/ab5ccbe6b8c49dbf58a25eb066a98b243a382967/src/plugins/blog/plugin.py#L216-L220

Closing as out of scope.

McDic commented 6 months ago

@squidfunk

I see. I troubleshooted several half hours and now I can handle blog plugin's configuration. At first I thought I have to modify page.meta inside of on_page_markdown, but I should modify page.config.date instead when isinstance(page, Post) is true, because modifying page.meta doesn't have an effect on blog plugin. Following is some minimally imported part of the working code:

# ...

from material.plugins.blog.structure import Post

class McDicBlogPlugin(BasePlugin[McDicBlogPluginConfig]):

    # ...

    def on_page_markdown(
        self, markdown: str, *, page: Page, config: MkDocsConfig, files: Files
    ) -> str | None:
        if isinstance(page, Post):
            page.config.date = {
                "created": datetime(2024, 1, 1),
                "updated": datetime(2024, 1, 1),
            }
        return None

    # ...

I think it's worthy mentioning you have to modify page.config instead of page.meta (and additionally isinstance(page, Post) typecheck for safety) when you want to override on the documentation.

alexvoss commented 6 months ago

Hi @McDic, I am not sure I understand the logic of the code. You seem to be unconditionally setting the date to the 1st of January 2024? In terms of producing dates for blog posts, the Git approach seemed sensible. Are you meaning to add the logic that retrieves the date from Git later on?

I was actually going to suggest an alternative, which would be to have a macro in the text editor that inserts the header fields. That saves some of the tedium but allows the user to retain some control. If you implement the Git version, can I suggest that you make sure to check if the blog entry already has a date set manually?

McDic commented 6 months ago

Hello, @alexvoss .

That datetime(2024, 1, 1) was just for testing. The actual .md file had date: 2024-01-15 as metadata and I was trying to change the metadata itself before the calling git log, and I was in trouble.

So far I concluded that I need to reimplement somewhere else to replace the whole original source itself being passed to the mkdocs-material blog plugin to implement date: git thing, which I think it's a bit tedious. I tried to reimplement on_page_read_source and failed then realized mkdocs-material is touching markdown text even before this method.

McDic commented 6 months ago

These are traces I faced.

https://github.com/squidfunk/mkdocs-material/blob/ab5ccbe6b8c49dbf58a25eb066a98b243a382967/src/plugins/blog/plugin.py#L107-L136

https://github.com/squidfunk/mkdocs-material/blob/ab5ccbe6b8c49dbf58a25eb066a98b243a382967/src/plugins/blog/plugin.py#L423-L447

https://github.com/squidfunk/mkdocs-material/blob/ab5ccbe6b8c49dbf58a25eb066a98b243a382967/src/plugins/blog/plugin.py#L405-L406

https://github.com/squidfunk/mkdocs-material/blob/ab5ccbe6b8c49dbf58a25eb066a98b243a382967/src/plugins/blog/structure/__init__.py#L48-L61

Now I think date metadata stuff is tricky to implement with custom plugins, unless it targets to be integrated towards mkdocs-materials only. Notice the markdown is directly read from on_files event by constructions of Post, which is not generally supposed to care page metadata options from markdown text. You must edit the original sourcefile by your own plugin before mkdocs-material/on_files event to get no config validation error.

One another temporary fix would be giving date: 1970-01-01 or something similar as fake date and resolve it later, which I think it is not very elegant.

alexvoss commented 6 months ago

If I interpret things correctly, there is a bit of a conundrum in that the Post object is not available before the Blog plugin's on_files handler but the handler itself uses the created date to sort the blog posts, so no plugin has the chance of injecting a date. I wonder if it would be possible to defer the sorting and then have a plugin with a lower priority run after the on_files handler of the blog plugin - or a higher priority one running before whatever other handler does the next bit of work? @suidfunk, am I holding the wrong end of the stick here?

McDic commented 6 months ago

@alexvoss You have a typo to mention @squidfunk .

squidfunk commented 6 months ago

My initial tip was incorrect, yes, the Markdown must be read before the navigation is constructed, so we can correctly exclude posts from the navigation or MkDocs will complain. This is all necessary to work around this behavior. For this reason, we already added a hack to the Insiders version of the blog plugin, calling the on_page_markdown hook for the meta plugin manually. This can be considered a bandaid, but I'm not sure how we should work around this catch-22. We need the metadata to prepare and exclude posts from navigation, as well as constructing all archive and category pages, adding them to the navigation. The archive page is deduced from the date, the category from categories, both of which need Markdown. I'm not sure how to proceed and will tag this as currently not fixable. If somebody has a good idea, were happy to consider merging it, but we currently have other much more important priorities @alexvoss.

alexvoss commented 6 months ago

Could you change things to produce the archive pages later, in another callback? I can see that the nav needs to be configured but the other work could come later, no? Would this enable other use cases that somehow require modifying the blog post metadata?

squidfunk commented 6 months ago

Maybe, I don't know. As I said, we currently have no bandwidth to investigate. The blog plugin is part of the community edition, so if somebody wants to take it on, we're happy to consider a PR. However, before changing something so fundamental, we need to add tests first, so we can be sure that all other bugs we fixed in the past do not reappear. But until we can do that, we first need to fix the open bugs, rework our documentation and finish search.

I'm really not for opening even more construction sites with the manpower we currently have.

McDic commented 6 months ago

I removed the built-in blog plugin from my blog and made my own blog plugin which does the process above but more respect on the structure of whole mkdocs plugin events.

Demo: https://blog.mcdic.net

My plugin does following patches;

However I think the whole structure of mkdocs plugin events is not very elegant, because they force every pages to be rendered separately and sequentially which makes harder to implement some multiple pages that depends on each other pages(i.e. dependency graph is not a DAG) while respecting the event order described in the official documentation. Even a maintainer of mkdocs told me to manipulate the file order as bandaid fix, which does not solve the fundamental problems.

Since the amount of codes in blog plugin is huge, not sure if it would be easier to change the built-in blog plugin works with this. If you are interested you can refer to my code. (Warning: I think core.py is quite dirty)

rajguru7 commented 1 month ago

Was anyone able to implement this?