Skip to content
Grav 2.0 is officially stable. Read the announcement →

Community guidelines

Please keep discussions civil and on-topic. Repeated violations may lead to a temporary ban.

Support

Admin plugin page blueprint, field type “pages”: slug (value) ignores translation

admin blueprints form theme

Started by Clive Beckett 3 years ago · 6 replies · 949 views
3 years ago

Hi!

In my theme I have a blueprint for a custom page template (let’s call it custom.html.twig) that includes an additional field with a page selection. In the returned <select> list the option labels show the translated page titles, but the value attributes – which will be stored in the page’s frontmatter – always contain the slug for the default language.

Is there a way to show the translated slugs?

Example: an option in the select list of a page in the secondary language German looks something like this (ignoring the fancy select lists here)

HTML
<option value="/projects">Projekte</option>

But it should look like this:

HTML
<option value="/de/projekte">Projekte</option>

In the blueprint (mytheme/blueprints/custom.yaml) I’ve tried both type: pages as well as type: select with data-options@. The result is the same in both cases:

YAML
fields:
  internalLinkVariant1:
    type: pages
    size: large
    label: LINK_INTERNAL.LABEL
    show_modular: false
    show_all: false
    options:
      'none': LINK_INTERNAL.NONE
  internalLinkVariant2:
    type: select
    label: LINK_INTERNAL.LABEL
    data-default@: '\Grav\Plugin\Admin::route'
    data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
    options:
      'none': LINK_INTERNAL.NONE

The page markdown (custom.de.md) then contains the following in its frontmatter:

TXT
internalLinkVariant1: /projects
last edited 01/17/23 by Clive Beckett
3 years ago

hi, @clivebeckett, you can set the var in the page template
(as i understand you define var in the theme blueprints.yaml file, if so)
an example for a pages form field could be
{% set internalLinkVariant1 = page.find(theme_var('internalLinkVariant1')) %}
and then do what you need, like this
<a href="{{ internalLinkVariant1.url|e }}">{{ internalLinkVariant1.title|e }}</a>

3 years ago

Hi @b.da

Thank you! But this is not about the theme blueprints.yaml, but about the blueprint for a page template as stored in mytheme/blueprints/. I tried to clarify that in an edit for the post, sorry if it was a bit unclear before.

3 years ago
  1. so use header_var('internalLinkVariant1'), fallback to theme_var, or page.header.internalLinkVariant1 instead. Just test it )

{% set internalLinkVariant1 = page.find(header_var('internalLinkVariant1')) %}
or
{% set internalLinkVariant1 = page.find(page.header.internalLinkVariant1)) %}

  1. the declaration for custom var in page blueprint must start with header. that why i thought it is theme blueprint
    YAML
    fields:
    header.internalLinkVariant1:
    type: pages
    size: large
    label: LINK_INTERNAL.LABEL
    show_modular: false
    show_all: false
    options:
      'none': LINK_INTERNAL.NONE
    
3 years ago

Thank you! If I’m not overlooking anything, this is still not what I’m searching for. I had tried to strip everything down wanting to avoid unnecessary information. Apparently I did not manage to explain properly with this 🙂 – Let’s have another go:

custom.yaml

  • still only a small section
  • my internal link is part of a field type:list which is why I forgot to add “header” after I dropped the original dot
  • the alternative implementation of the page selector is commented out – the result in the form is the same
YAML
fields:
  header.call2ActionLinks:
    name: call2action
    type: list
    label: CALL2ACTION.HEADER
    style: vertical
    fields:
      .label:
        type: text
        label: CALL2ACTION.LABEL
      .internal:
        type: pages
        size: large
        label: CALL2ACTION.INTERNAL.LABEL
        show_modular: false
        show_all: false
        options:
          'none': CALL2ACTION.INTERNAL.NONE
      # .internal:
      #   type: select
      #   label: CALL2ACTION.INTERNAL.LABEL
      #   # show_root: false
      #   # show_modular: false
      #   data-default@: '\Grav\Plugin\Admin::route'
      #   data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
      #   options:
      #     'none': CALL2ACTION.INTERNAL.NONE
      .external:
        type: text
        label: CALL2ACTION.EXTERNAL.LABEL

The problem is not in the Twig template (see below) – although it’s possible that it can be solved in the template. The problem is, that the form as defined in custom.yaml shows translated page titles – but not the translated slugs in the value attribute of the select list. So if I am in the form of a German page and select the page “Projekte” which has the slug “/de/projekte”, the form shows “/projects” as value instead – and this is what will be saved in the frontmatter/header section of the page’s markdown.

Desired admin form output (excerpt)

HTML
<option value="/de/projekte">Projekte</option>

Actual admin form output (excerpt)

HTML
<option value="/projects">Projekte</option>

Page’s markdown – frontmatter excerpt

YAML
call2ActionLinks:
    -
        label: 'Projekte'
        internal: /projects
        external: null

custom.html.twig
I do have access to the header data and it works – but on the German website it links to the English (default) page – because of the above mentioned default slug in the form.

HTML
{% if page.header.call2ActionLinks is not empty %}
    <ul class="c2a">
        {% for c2a in page.header.call2ActionLinks %}
            {% if c2a.internal != 'none' %}
                <li><a href="{{ c2a.internal }}">{{ c2a.label }}</a></li>
            {% elseif c2a.external %}
                <li><a href="{{ c2a.external }}" target="_blank">{{ c2a.label }}</a></li>
            {% endif %}
        {% endfor %}
    </ul>
{% endif %}
3 years ago

@clivebeckett, Not sure if this fits your needs, but you could play with the following:

  • Using fresh Grav installation
  • system.yaml
    YAML
    languages:
    supported: [en, de]
    include_default_lang: false
    
  • Pages:
    TXT
    user/pages
    ├── 01.home 
    │   └── default.md
    ├── 02.typography
    │   ├── default.de.md
    │   └── default.en.md
    └── images
    
  • Page header 02.typography/default.de.md
    YAML
    ---
    title: TypographyDE
    slug: slugDE
    ---
    
  • Create plugin MyPlugin
  • myplugin.php

    PHP
    public static function getTranslatedSlugs(): array
    {
    /** @var Grav */
    $grav = Grav::instance();
    
    /** @var Admin */
    $admin = $grav['admin'];
    $admin->enablePages();
    
    /** @var Pages */
    $pages = $grav['pages'];
    
    /** @var Language */
    $language = $grav['language'];
    $activeLanguage = $language->getActive();
    $isDefaultLanguage = $language->getDefault() === $activeLanguage;
    $showDefaultLanguageInUrl = $grav['config']->get('system.languages.include_default_lang', false);
    
    $pageList = $pages->getList(null, 0, true, false, true, true, true, false);
    
    $slugs = [];
    
    foreach ($pageList as $key => $value) {
      $page = $pages->find($key);
    
      $slug = $page->route();
    
      if (!$isDefaultLanguage || ($isDefaultLanguage && $showDefaultLanguageInUrl)) {
        $slug = "/{$activeLanguage}{$slug}";
      }
    
      $slugs[$slug] = $page->title();
    }
    
    return $slugs;
    }  
    
  • blueprints/default.yaml
    YAML
    header.internalLinkVariant2:
    type: select
    label: LINK_INTERNAL.LABEL
    data-options@: '\Grav\Plugin\MyPluginPlugin::getTranslatedSlugs'
    
  • Resulting array when editing page of the non-default German language:
    TXT
    
    /de/home: "Home"
    /de/slugDE: "TypographyDE"
    
last edited 01/19/23 by pamtbaau
3 years ago

Thank you @pamtbaau!

This works – with some changes. I wanted the default level marker (—-—-▸ Page Title) back in the list which was missing. Here’s my updated version for documentation. For easier readability it contains the original lines of code in comments:

PHP
public static function getTranslatedSlugs(): array
{
    /** @var Grav */
    $grav = Grav::instance();

    /** @var Admin */
    $admin = $grav['admin'];
    $admin->enablePages();

    /** @var Pages */
    $pages = $grav['pages'];

    /** @var Language */
    $language = $grav['language'];
    $activeLanguage = $language->getActive();
    $isDefaultLanguage = $language->getDefault() === $activeLanguage;
    $showDefaultLanguageInUrl = $grav['config']->get('system.languages.include_default_lang', false);

    // making sure $pages->getList() returns the level marker and hides modules
    // $pageList = $pages->getList(null, 0, true, false, true, true, true, false);
    $pageList = $pages->getList(null, 0, true, false, false, false, false, false);

    $translatedPages = [];

    foreach ($pageList as $key => $value) {
        $page = $pages->find($key);

        // this replacement is just a matter of personal taste
        // $slug = $page->route();
        // if (!$isDefaultLanguage || ($isDefaultLanguage && $showDefaultLanguageInUrl)) {
        //     $slug = "/{$activeLanguage}{$slug}";
        // }

        $slug = (!$isDefaultLanguage || ($isDefaultLanguage && $showDefaultLanguageInUrl))
            ? '/' . $activeLanguage . $page->route()
            : $page->route();

        // $value of $pageList now contains the level marker
        // $translatedPages[$slug] = $page->title();
        $translatedPages[$slug] = $value;
    }

    return $translatedPages;
}

Also make sure to include

PHP
use Grav\Common\Grav;

in your Plugin Class definition. This was not in the plugin skeleton created by devtools and it took me a while to figure out how to include it.

Suggested topics

Topic Participants Replies Views Activity
Support · by Thomas, 1 week ago
2 53 10 hours ago
Support · by Anna, 3 days ago
2 60 13 hours ago
Support · by Justin Young, 14 hours ago
1 30 14 hours ago
Support · by Duc , 1 week ago
2 65 5 days ago
Support · by Colin Hume, 1 week ago
2 56 5 days ago