1

In Drupal we can define theme settings that can for instance allow us to create "theme variations".

For instance allow a user to choose a color-scheme, or even define individual colors, sizes, ...

But what if you want to be able to, when creating content, decide what variation is used on that content-page/entity/field?

One way would be to implement a pseudo-field that adds one or more select-box's to all entity forms. That would then populate it's information by reading the theme's info file.

In Drupal 7, I would do something like:

/**
 * @param bool $rebuild
 * @return array<
 *  $entityType:Array<
 *    $bundle:Array<Options>
 *  >
 * >
 */
function _ema_entity_variation_info($rebuild=true) {
  $cid = 'ema_entity_variation_info';

  if (!$rebuild) {
    $variations = &drupal_static(__FUNCTION__);
    if (isset($variations)) {
      return $variations;
    }
    if ($cache = cache_get($cid)) {
      $variations = $cache->data;
      return $variations;
    }
  }

  $variations = array();

  $themes = list_themes();
  foreach ($themes as $theme_key => $theme) {
    if ($theme->status && isset($theme->info['variations'])) {
      $variations += $theme->info['variations'];
    }
  }

  cache_set($cid, $variations);

  return $variations;
}

/**
 * Implements hook_entity_property_info_alter().
 */
function ema_entity_variation_entity_property_info_alter(&$info) {
  $variations = _ema_entity_variation_info();

  foreach ($variations as $entity_type => $variation_info) {
    if (!isset($info[$entity_type])) { continue; }

    foreach ($variation_info as $bundle => $bundle_variations) {
      if (!isset($info[$entity_type]['bundles'][$bundle])) { continue; }

      $properties = &$info[$entity_type]['bundles'][$bundle]['properties'];
      foreach($bundle_variations as $variation_name => $variation_options) {
        $properties['variation_'.$variation_name] = array(
          'type' => 'token',
          'label' => t('Variation'),
          'description' => t('Provides variations'),
          'setter callback' => 'entity_property_verbatim_set',
        );
      }
    }
  }
}

/**
 * Implements hook_field_attach_form().
 */
function ema_entity_variation_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  //stupid user profiles, comments, ...
  if (!isset($entity->type)) {
    return;
  }
  $variations = ema_entity_variation_get_variations($entity_type, $entity->type);
  if ($variations) {
    $form['variation'] = array(
      '#type' => 'fieldset',
      '#title' => t('Variation'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#group' => 'additional_settings'
    );
    foreach ($variations as $label => $options) {
      $form['variation']['variation_'.$label] = array(
        '#type' => 'select',
        '#title' => $label,
        '#options' => $options,
        '#default_value' => isset($entity->variations[$label]) ? $entity->variations[$label] : NULL
      );
    }
  }
}

(yes I know It's not perfect and only works with a single theme, but it got the job done).

This would allow a drupal themer (not everyone is full-stack) to easily define "variations" for all entity types/bundles in his info file:

variations[node][basic_page][content_align][left] = Left
variations[node][basic_page][content_align][center] = Center

And then in his theme add a class:

/**
 * Implements hook_preprocess_entity
 */
function THEME_preprocess_node(&$vars) {
  if (isset($vars['elements']['#entity']) && isset($vars['elements']['#entity']->variations)) {
    foreach ($vars['elements']['#entity']->variations as $variation_name => $value) {
      $vars['classes_array'][] = drupal_html_class($variation_name).'--'.drupal_html_class($value);
    }
  }
}

This however feels oh so wrong and I'm now looking into how this could be done "the drupal 8 way".

  • I looked into plugins, but themes can't implement plugins (?), atleast my plugin manager is not finding them.
  • I looked into reading the info file (using theme handled and such), but this just feels wrong (or is my gut feeling off here?)
  • I can only move all the form logic into the theme if it is also used for the admin, this is however hardly ever the case. And I want to support multiple themes and being able to select the variations on a per theme basis.

Anyone has any idea's where I should look into?

2 Answers 2

3

Assuming I'm understanding the problem correctly, you want to select a variation of the theme on content creation. This looks like a job for Panels and Page Manager (a Ctools submodule).

Enable both modules then do the following:

  1. Create a field on your entity to signal the mechanism what to use.
  2. Create the layouts using Drupal's built-in layout API.
  3. In Structure -> Pages, Enable the node/% custom page so that Page Manager can custom render nodes.
  4. Click Edit on node/% and create a "variant". A variant is a set of configurations used by Page Manager to render a page.
    • Define selection criteria. Use the field in step 1 to determine when this variant takes effect.
    • Define layout. Select a layout created from step 2 to use on this variant.
    • Define content. Position content in the layout.

There will be a bit of coding needed for creating the layouts. But other than that, everything else is pretty much admin duty, selecting which layout to use when. Layouts can also define their own CSS.

4
  • Thx, I'll look into it tomorrow. Just a quick question: does this also work with inline_entity_forms. e.g; with paragraphs ? Where you can define different variations per paragraph?
    – Pinoniq
    Jul 30, 2017 at 20:38
  • @Pinoniq That I haven't tried that. The answer is more on pages. However, there might be plugins to either Panels or Page Manager that allows for other kinds of entities.
    – Joseph
    Jul 30, 2017 at 20:42
  • Ok, I'll test it out tomorrow and I'll come back to you when I know more :) thx already for the possible answer
    – Pinoniq
    Jul 30, 2017 at 20:45
  • I'm accepting your answer as the correct one. It doesn't give me the flexibility that I was looking for, but it seems promissing. I'll dive into the layout API and maybe even implement something similar to page manager that doesn't take over entire pages, but parts of a page. I just wish we could install extra time ^^
    – Pinoniq
    Jul 31, 2017 at 22:28
2

Why do you need a generic code based solution? Why a pseudo field instead of a real field? You need storage for your data anyway.

Just create a field in the UI, provide values like

two_columns|Two columns
three_columns|Three columns

And then implement hook_theme_suggestions_HOOK_alter() and define suggestions based on the selected value.

3
  • Because you would be storing these field values in a feature/module. yet it is something that should be defined by the theme. and not part module, part theme. It's more a search for proper SOC.
    – Pinoniq
    Jul 31, 2017 at 14:01
  • 1
    And exactly how would the theme store that? You need to store it somewhere and a field to store per-entity data is IMHO perfectly fine. Maybe one day you'll implement a new theme or multiple themes and you need this information in all of them? IMHO, selection of how it should be displayed is also content if it's something provided by the editor/user.
    – Berdir
    Jul 31, 2017 at 19:22
  • Not all things built with Drupal have a single theme. Some Drupal's power literally 300 sites. Some Drupal's have multiple themes per page, just look at the power OG gives us. In this case, you are storing theme-related information in fields, something that was not ment for it. Installing/using a different theme should mean the availability of different variants instead of it implying to redo a featured field. I'm looking into a module storing the information on a per theme basis. And a per theme basis is something fields was not made for.
    – Pinoniq
    Jul 31, 2017 at 22:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.