picocms / Pico

Pico is a stupidly simple, blazing fast, flat file CMS.
http://picocms.org/
MIT License
3.81k stars 615 forks source link

How to access content header YAML settings in template via pages function #550

Closed youcantryreachingme closed 3 years ago

youcantryreachingme commented 4 years ago

Hello,

First time trying to work with picocms.

In a nutshell, I've pasted the code snippet from the guide on how to build blogging functionality (here: http://picocms.org/docs/#blogging ) into a template. I can see the for loop iterating through all pages in the specified folder and that within the loop we reference page.title, page.date_formatted and page.description. If I understand correctly, these are entries in the YAML header of the content page being evaluated on the given loop.

However, if I wish to add my own header elements - say "ModuleRoot: true" to the index.md - how can I work with that value within the loop? Note - I am using index.md instead of blog.md.

Above is the general question. Below are specific issues I'm encountering.

Currently the loop is listing the index.md page in addition to all the other content. I don't want this. I can't use Hidden: true in the header for index.md because that ends up removing it from my site navigation. I'd like to add a variable/parameter to the header, then check, within that loop, whether that parameter is set (ie. ModuleRoot: true) and if so, avoid rendering that page in the list. I can see the current code checks for not page.hidden but adding not page.ModuleRoot does not work.

As a separate issue, The way I want to set up this pseudo-blogging feature is that the pages within my "blog" folder are not full articles. Instead, the page content is, in fact, the excerpt to be displayed in a list of articles. I want the Title attribute (text) from the header to link to a page elsewhere in the site. That is, the index.md will list all the pages in this folder, but clicking on an entry takes you elsewhere in the site.

I can think of two ways to do this:

1) I could set up additional parameters in the header for each content item in that folder - things like the URL to which you should be taken when clicking on this item. Then I could modify the template that renders the list of items from this folder so that it generates a link using that URL. I have two problems with this: first, I cannot seem to access custom header parameters; second, I actually want rich content to be shown - so for example, an image which is hyperlinked, has ALT text, has a TITLE attribute, then a paragraph of text - in addition to the article title which is also linked, then a separate paragraph showing the date. Putting all these attributes into separate parameters in the page header would work and would produce consistent formatting for each entry, but it is very cumbersome to maintain. So rather, I prefer the next option:

2) I could display the content portion of each page in that folder, within the list being generated by this for loop. To do that, I need to reference "page.content", as it were. That does not work. If I reference just "content", I get the content from index.md - which is the page running through that for loop. Is it possible to iterate through every content page and access the content from that page?

Lastly a suggestion - so far, the only docs I could find about the pages function (here: http://picocms.org/in-depth/features/pages-function/ ) - to try and understand what might be output, for example, is an article that reads more like a conversation between one developer coaching another. (The whole "family" and "flower shop" metaphor thing just doesn't work for me. And for example - how can I know if this CMS has other functions accessible to me?)

I think it would be great if there were documentation that was more formalised. Compare, for example, any documentation on any SQL function or statement with any reasonable database vendor - it lists all syntax, optional parameters and explains each parameter, return value, etc and even describes which versions of the product support the function.

On that last note, I don't recall now where I sourced the following line of code:

for page in pages|sort_by("time")|reverse

However I cannot find any documentation about the syntax of using a pipe character with the pages function, nor the sort_by function/parameter, nor the reverse parameter, etc. This is where a formalised reference would help.

Oh, and the blogging documentation uses page.date_formatted for display but instructs to set a parameter named date in the header - why the mismatch? Is date_formatted a parameter name or a special keyword that formats something named "date"? What is the correct format to enter the date against that parameter in the header? These kinds of questions would probably be answered by a formal reference, too, that talks about the YAML content header - and the way in which the pages function uses it - in more detail.

One more PS: I think even the for loop construct, and the conditional if logic, and the comparative operators like 'not' (what about greater than? null?) would also benefit from a formal doc.

Thanks.

youcantryreachingme commented 4 years ago

Ok - I solved one problem - excluding the index.md file from the list generated by the for loop, using the Twig reference documentation and the following:

and not (page.id ends with "index")

Now for accessing custom values from the page header and/or displaying the page content for each 'page' in the loop...

youcantryreachingme commented 4 years ago

And problem two - after searching through PHP code I found a useful comment for the function readPages:

    /**
     * Reads the data of all pages known to Pico
     *
     * The page data will be an array containing the following values:
     *
     * | Array key      | Type     | Description                              |
     * | -------------- | -------  | ---------------------------------------- |
     * | id             | string   | relative path to the content file        |
     * | url            | string   | URL to the page                          |
     * | title          | string   | title of the page (YAML header)          |
     * | description    | string   | description of the page (YAML header)    |
     * | author         | string   | author of the page (YAML header)         |
     * | time           | int      | timestamp derived from the Date header   |
     * | date           | string   | date of the page (YAML header)           |
     * | date_formatted | string   | formatted date of the page               |
     * | hidden         | bool     | this page shouldn't be visible           |
     * | raw_content    | string   | raw, not yet parsed contents of the page |
     * | meta           | string[] | parsed meta data of the page             |
     * | previous_page  | &array[] | reference to the previous page           |
     * | next_page      | &array[] | reference to the next page               |
     * | tree_node      | &array[] | reference to the page's tree node        |
     *
     * Please note that the `previous_page` and `next_page` keys don't
     * exist until the `onCurrentPageDiscovered` event is triggered
     * ({@see Pico::discoverPageSiblings()}). The `tree_node` key doesn't
     * exit until the `onPageTreeBuilt` event is triggered
     * ({@see Pico::buildPageTree()}).
     *
     * @see Pico::sortPages()
     * @see Pico::discoverPageSiblings()
     * @see Pico::getPages()
     */

This describes raw_content - which renders the raw markup. Elsewhere I saw that you can use the pipe character to specify when content is raw, thus: {{ page.raw_content|raw }} ... but quickly learned this relates to raw HTML, not markup.

However that gives me the workaround - just code these "blog" pages (really just excerpts to be listed) using straight-up HTML. It works.

EDIT: It does not work. That raw_content outputs the YAML header too - which I don't want to display but I don't want to drop because it provides the date parameter for sorting.

And I guess the final answer is that to return custom YAML variables in the pages object, I need to customise the above function. So the new question becomes:

How best would I customise that function? Should I be hacking around in Pico.php? Or is there a general approach to extending these functions in a manner that doesn't touch the core file? What would that approach look like?

youcantryreachingme commented 4 years ago

Ok, pushing a little further, the final answer comes through. The function returns a string array named meta. So here is the solution:

{% if not page.meta.ModuleRoot %}
   <p>Not the module root</p>
{% endif %}

(Incidentally, if I set the header value to the text "true", this return value is "1").

This answers all my questions, including to say that the pages object does not return the formatted content, but it can return the raw content. Is there a function I can call in my template that will parse the raw content and render formatted content?

PhrozenByte commented 4 years ago

Accessing meta data:

{{ page.meta.My_Custom_YAML_data }}

Parsing contents of another page:

{{ "sub/page"|content }}

About Twig templating see https://twig.symfony.com/doc/1.x/

About creating your own custom theme and Pico's additional Twig features see http://picocms.org/docs/#themes

Not sure whether you've got more open questions, so please don't hesitate to go ahead :smiley:

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in two days if no further activity occurs. Thank you for your contributions! :+1: