Jak zobrazit vybraný content type pomocí jiného tématu vzhledu

V Drupalu si standartně můžete vybrat dvě témata – defaultní pro front-end, a běžně také jiné téma pro administraci. Co ale když potřebujete vybraný content type zobrazit pomocí admin tématu?

Pro jeden projekt jsem se rozhodl vytvářel dokumentaci k webu přímo jako stránky webu. Vytvořil jsem pro něj speciální content type Guidelines, nadefinoval přístupy, URL, menu … Vzhledem k tomu, že obsahově to jsou ale stránky "administrace", chtěl jsem je zobrazit pomocí admin tématu.

Pro Drupal 7 zde byly modul ThemeKey (https://www.drupal.org/project/themekey) nebo Content Theme (https://www.drupal.org/project/content_theme). Pro Drupal 8+ existuje module Administration theme (https://www.drupal.org/project/admin_theme), který umožnuje použít admin téma na základě URL. Pokud ale chceme definovat jiné téma na základě content typu, nezbývá, něž se pustit do kódování.

UPDATE: Nakonec jsem ještě našel modul Theme Switcher Rules (https://www.drupal.org/project/theme_switcher), který přesně umí měnit téma podle content typu. A nejenom. Podmínek pro vzhled se dá nadefinovat mnohem více. Což může být právě i nevýhoda - proč si pořizovat kanón, když chcete dostat jenom vrabce. Nebo když je potřeba se dostat až na samotné nody.

33Theme Switcher Rules

 

Kde vlastně změnit téma vzhledu - RouteSubscriberBase?

Pokud bych vytvářel vlastní stránky také pomocí kódu, tak v rámci definice cest se v routing.yml souboru může definovat i volba _admin_route: TRUE . Takže jsem zkusil nejdříve vytvořit vlastní třídu pro RouteSubscriberBase v souboru src/Routing/RouteSubscriber.php.

33RouteSubscriber-alterRoutes

Takhle to ale nefunguje.

Problém je, že alterRoutes se volá jen při vyprázdnění cache, a také že v tomto místě se $node = \Drupal::routeMatch()->getParameter('node'); nefunguje, informaci o nodu zde nelze získat.

Správné místo – ThemeNegotiatorInterface

Od verze Drupal 8 se změna tématu vzhledu odehrává pomocí theme negotiators, viz. " 'theme callback' and hook_custom_theme() replaced by theme negotiators" https://www.drupal.org/node/2158619.

Pokud už teď víme, kde se změna témat odehrává, je zbytek už snadný.

Nejdříve se nadefinuje nová služba, do souboru my_module.services.yml přidáme services:

services:
  theme.negotiator.theme_switcher:
    class: Drupal\my_module\Theme\SwitcherNegotiator
    tags:
      - { name: theme_negotiator, priority: -10 }

A v samotném souboru src/Theme/ SwitcherNegotiator.php máme

/**
 * @file
 * Contains \Drupal\myesi_alter_routes\Theme\SwitcherNegotiator
 */

namespace Drupal\my_module\Theme;

use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface;

class SwitcherNegotiator implements ThemeNegotiatorInterface
{

    /**
     * {@inheritdoc}
     */
    public function applies(RouteMatchInterface $route_match)
    {
        // Use this theme on a certain route.
        $node = $route_match->getParameter('node');
        if ( ! is_null($node) && $node instanceof \Drupal\node\Entity\Node) {
            if ($node->getType() == 'guideline') {
                return true;
            }
        }
        // apply default theme
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function determineActiveTheme(RouteMatchInterface $route_match)
    {
        // Here you return the actual theme name.
        return 'seven';
    }
}

Jak je vidět, v třídě SwitcherNegotiator už není problém získat informace o aktuálním nodu. A pak aplikovat změnu tématu právě např. podle content typu.

 

Závěr

Pokud chceme zobrazit vybrané nody pomocí jiného než defaultního tématu vzhledu, je třeba vytvořit si vlastní theme negotiator. Naštěstí to není to nic složitého. Pokud je naší podmínkou content type, je to opravdu velmi jednoduché.

Jak něco podobného udělat pro konkrétní nody si ukážeme někdy příště.