Grav - Retrieving specific items from page in template - twig

I have the following page structure in Grav:
# Title
## Subtitle
### Subsubtitle
The page structure is always the same and it has just those three items.
How can I retrieve each item (Title/Subtitle/Subsubtitle) separately in Twig template?
The twig template will then do things like:
<p> You typed {{ page.whatever_retrieves_the_title_# }} as title, then {{ page.whatever_retrieves_the_subtitle_## }} as subtitle and finally {{ page.whatever_retrieves_the_subsubtitle_### }} as subsubtitle</p>
What if instead of the above structure I have:
- Title
- Subtitle
- Subsubtitle
The objective is that the user adds just that structure of three items and the twig template use each item to display a more complex layout.

This is Markdown, right?
# Title
## Subtitle
### Subsubtitle
You can get the HTML version of the page's Markdown in Twig with {{ page.content }}, as described in Grav's documentation. So you should get something like this:
<h1>Title</h1>
<h2>Subtitle</h2>
<h3>Subsubtitle</h3>
You can use the split and raw filters to extract the contents of those tags. I'm also using the default filter so that there won't be an error if the extraction of the tag contents fails:
Title is:
{{ page.content|split('<h1>')[1]|default|raw|split('</h1>')[0] }}
Subtitle is:
{{ page.content|split('<h2>')[1]|default|raw|split('</h2>')[0] }}
Subsubtitle is:
{{ page.content|split('<h3>')[1]|default|raw|split('</h3>')[0] }}
Or because Grav seems to provide a regex_replace filter, you could also use it:
Title is:
{{ page.content|regex_replace('~.*<h1>(.*)</h1>.*~s', '$1') }}
Subtitle is:
{{ page.content|regex_replace('~.*<h2>(.*)</h2>.*~s', '$1') }}
Subsubtitle is:
{{ page.content|regex_replace('~.*<h3>(.*)</h3>.*~s', '$1') }}
If instead you have this:
- Title
- Subtitle
- Subsubtitle
You can again use the split, default and raw filters:
Title is:
{{ page.content|split('<li>')[1]|default|raw|split('</li>')[0] }}
Subtitle is:
{{ page.content|split('<li>')[2]|default|raw|split('</li>')[0] }}
Subsubtitle is:
{{ page.content|split('<li>')[3]|default|raw|split('</li>')[0] }}
Not very beautiful. :-) If the titles can contain HTML (e.g. ## Hello **world**! → <h2>Hello <strong>world</strong>!</h2>) or special characters, you probably need to append |raw to the already long magic spells.

Others provided good solution with markdown manipulation. However, you could use grav features and provide your own blueprints, and have your user fills some fields.
Let's say this is for a page that uses the template blog_article.html.twig, you can therefore create a file named blog_article.yaml inside user/themes/yourtheme/blueprints and fill it with the following blueprint:
title: Blog_Article
'#extends':
type: default
context: blueprints://pages
form:
fields:
tabs:
fields:
content:
fields:
header.mytitle:
type: text
label: My Label
header.mysubtitle:
type: text
label: Type Subtitle
header.mysubsubtitle
type: text
label: Type subsubtitle
Now, if you try to edit your page from admin, you will see three new fields added to the page, under the page medias.
You can then display these fields in your template with the following twig:
{{ page.header.mytitle }}
{{ page.header.mysubtitle }}
{{ page.header.mysubsubtitle }}
Hope it helps

Related

Setting default value for CKEditor in Flask App wtforms

I'm trying to set the default value for a CKEditor html text input based on an existing object. The use case is for updating an existing row using flask-sqlalchemy so the user doesn't have to retype all of the existing rich text. As the code is written, the default value for form.description is blank even when object.description is not.
The relevant part of the form looks something like this:
<form method="POST">
{{ ckeditor.load() }}
{{ form.hidden_tag }}
{{ form.name.label }}
{{ form.name(class="form-control", value=object.name) }}
{{ form.description.label }}
{{ form.description(class="form-control", value=object.description) }}
{{ edit_loc_form.submit(class="btn btn-primary") }}
</form>
{{ ckeditor.config(name='description') }}
Thanks!
From the docs:
If you are using Flask-WTF/WTForms, it’s even more simple, just pass
the value to the form field’s data attribute:
#app.route('/edit')
def edit_post():
form = EditForm()
form.body.data = "default value" # default value entered here.
return render_template('edit.html', form=form)

Gutenberg Blocks with Timber and ACF Flexible Content

We're creating a block.twig template as per:
https://timber.github.io/docs/guides/gutenberg/#how-to-use-acf-blocks-with-timber
to use ACF Flexible Content Field Types to create Gutenberg Blocks.
For reference the following sample code would be for Repeater Field Types:
{% for field in fields.repeater %}
Title: {{ field.title }} <br/>
Url: {{ field.url }}
{% endfor %}
which we tested and seems to work fine.
What would be the equivalent using Flexible Content fields?
Thanks.
#Rafael they're a little more complicated, just need to handle them like arrays. Let's pretend your Flexible Content field is called my_fc_field inside of it. You created 2 layouts, each has a single text field, one called tf_one, the other fc_two
{% for subfield in fields.my_fc_field %}
Layout name: {{ subfield.acf_fc_layout }}
Text Field Value: {{ subfield.tf_one ? subfield.tf_one : subfield.tf_two }}
{% endfor %}
doing a {{ dump(fields.my_fc_field) }} should make it clear what's inside there that you can manipulate/output

Navigate through nested yaml list with liquid

I've seen a few questions about this topic but none could solve my problem. I want to have a multilingual jekyll site without using any plugin. I have a yaml file with this nested list
menu:
fr:
home:
name:"Acceuil"
url:"/"
team:
name:"Équipe"
url:"/equipe"
en:
home:
name:"Home"
url:"/home.html"
team:
name:"Team"
url:"/team"
I'd like to print a different menu depending on the page lang option in the yaml front matter. A minimal exemple would be:
---
lang: en
---
<ul>
{% for item in site.menu.[page.lang] %}
<li>{{ item[1].name }}</li>
{% endfor %}
</ul>
and this should print in raw html
Home
Team
But instead my code prints nothing, I just have two bullets with no text. I can't see what I'm doing the wrong way, could you guys help me ?
Edit:
Ok, my only problem was just that I didn't put space between the value indicator and the value. Now the problem is solved. Thanks !
Add a whitespace between colons and values in the menu file, according to the YAML specification:
YAML insists the “:” mapping value indicator be separated from the
value by white space.
Also remove the menu: key and save the menu in a file called _data/menu.yml.
fr:
home:
name: "Acceuil"
url: "/"
team:
name: "Équipe"
url: "/equipe"
en:
home:
name: "Home"
url: "/home.html"
team:
name: "Team"
url: "/team"
Then you can access that data with site.data.menu accessing directly to the lang key without a . between menu and page.lang:
<ul>
{% for item in site.data.menu[page.lang] %}
<li>{{ item[1].name }}</li>
{% endfor %}
</ul>
This generates:
<ul>
<li>Home</li>
<li>Team</li>
</ul>
I've used a similar approach in a multilingual site with nav.yaml and a langnav.html include.

Symfony Translations

i'm using the last version of symfony (3.1.3)
i want to trans multi vars.
but i didn't find and good solution
right now i'm doing:
{% block h1 %}{{ 'service.create'|trans }} {{ ('service'|trans) }}{% endblock %}
I try :
{% block h1 %}{{ 'service','service.create'|trans }} }} { %endblock %}
but no luck.
I also try with
{% trans %}service.create|service{% endtrans %}
thanks
You can use parameters in your translation like this:
{{ 'service.create'|trans }}
{{ 'service.create'|trans({'%separator%': 'any text'}) }}
And in your messages.ru.yml
...
service:
create: ... %separator% ...
...
If your separator is in html, add raw like this :
{{ 'service.create'|trans({'%separator%': '<br>'})|raw }}
You have more details in symfony doc here
You can translate strings in backend:
public function indexAction($name)
{
$translated = $this->get('translator')->trans('Hello '.$name);
return new Response($translated);
}
Or in twig templates:
<h1>{{ 'service.create'|trans }}</h1>
Each time you create a new translation resource (or install a bundle that includes a translation resource), be sure to clear your cache so that Symfony can discover the new translation resources:
php bin/console cache:clear
More info: http://symfony.com/doc/current/translation.html
I needed to just trans multi vars.
twig not support that...
If you have a "service.yml.en" file or something like that :
{{ service.create|trans({}, "service") }}
Here the translator will search in your service.yml.en file and load the correct translation key.
If you want to do advance translations, you can use vars like that (for example) :
{{ (className|lower ~ "." ~ field)|trans({}, className|lower) }}
Here if you have a "user.yml.en" and you want to load something like "user.width", it'll work.
Good luck :)

Swig (node.js) Include a File and pass an object

I am facing this problem.
I've got a page with an include of another like this:
index.html
{{ set pets = { pets : petsObject } }}
{{ include pets.html }}
petsObject is an object like this
petsObjects: [
{ name : "cat" },
{ name : "dog" }
]
When I try to render the page I get a blank page with only this:
[object Object]
I have no clue about what is going on :(
Thanks in advance!
Seems you'll need to use:
{% include pets.html with pets %}
According to docs for include:
Locally declared context variables are not passed to the included template by default.
It is also recommended for performance to use the only keyword after the included terms, like this:
{% include pets.html with pets only %}
Beyond that, it depends on the contents of pets.html, which you haven't included here. But, make sure that you're attempting to output the name:
{% for pet in pets %}
{{ pet.name }}
{% endfor %}
Or use a filter like json_encode() to format it:
{% for pet in pets %}
{{ pet|json_encode }}
{% endfor %}
Trying to output the Objects themselves will simply produce [object Object]:
new Object().toString() === "[object Object]"

Resources