You are here

panelizer.module in Panelizer 8.3

Hook implementations for the Panelizer module.

File

panelizer.module
View source
<?php

/**
 * @file
 * Hook implementations for the Panelizer module.
 */
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant;

/**
 * Implements hook_theme().
 */
function panelizer_theme() {
  return [
    'panelizer_view_mode' => [
      'render element' => 'element',
    ],
    'panelizer_wizard_form' => [
      'render element' => 'form',
    ],
    'panelizer_wizard_tree' => [
      'variables' => [
        'wizard' => NULL,
        'cached_values' => [],
        'tree' => [],
        'divider' => ' » ',
        'step' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_entity_type_alter().
 */
function panelizer_entity_type_alter(array &$entity_types) {

  /** @var \Drupal\panelizer\Plugin\PanelizerEntityManager $panelizer_manager */
  $panelizer_manager = \Drupal::service('plugin.manager.panelizer_entity');

  // Replace the entity view builder on any entity where we have a Panelizer
  // entity plugin and the entity itself has a view builder.
  foreach ($panelizer_manager
    ->getDefinitions() as $entity_type_id => $panelizer_info) {
    if (isset($entity_types[$entity_type_id]) && $entity_types[$entity_type_id]
      ->hasHandlerClass('view_builder')) {
      $entity_types[$entity_type_id]
        ->setHandlerClass('fallback_view_builder', $entity_types[$entity_type_id]
        ->getHandlerClass('view_builder'));
      $entity_types[$entity_type_id]
        ->setHandlerClass('view_builder', '\\Drupal\\panelizer\\PanelizerEntityViewBuilder');
    }
  }
}

/**
 * Implements hook_panels_build_alter().
 */
function panelizer_panels_build_alter(&$build, PanelsDisplayVariant $panels_display) {
  $builder = $panels_display
    ->getBuilder();
  $storage_type = $panels_display
    ->getStorageType();
  $is_panelizer = $builder
    ->getPluginId() == 'ipe' && in_array($storage_type, [
    'panelizer_default',
    'panelizer_field',
  ]) && isset($build['#attached']) && isset($build['#attached']['library']) && in_array('panels_ipe/panels_ipe', $build['#attached']['library']);

  // Add our Javascript customizations for the IPE.
  if ($is_panelizer) {
    $build['#attached']['library'][] = 'panelizer/panels_ipe';

    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $panels_display
      ->getContexts()['@panelizer.entity_context:entity']
      ->getContextValue();
    $revision_id = $entity instanceof RevisionableInterface && $entity
      ->getEntityType()
      ->isRevisionable() && !$entity
      ->isDefaultRevision() ? $entity
      ->getRevisionId() : NULL;
    list(, , $view_mode) = explode(':', $panels_display
      ->getStorageId());

    // Get the default storage id, if we're looking at a panelizer default or
    // the panelizer field contains a revision.
    if (sizeof(explode(':', $panels_display
      ->getStorageId())) == 4) {
      list(, , , $default) = explode(':', $panels_display
        ->getStorageId());
    }
    else {
      $default = NULL;
    }
    if ($panels_display
      ->getStorageType() == 'panelizer_field') {
      $panelizer_default_storage_id = rtrim(implode(':', [
        $entity
          ->getEntityTypeId(),
        $entity
          ->bundle(),
        $view_mode,
        $default,
      ]), ':');
    }
    else {
      $panelizer_default_storage_id = $panels_display
        ->getStorageId();
    }

    // Get the special, internal default storage id that includes the entity id,
    // which will allow us to correctly set the contexts on the Panels display.
    $panelizer_default_internal_storage_id = '*' . rtrim(implode(':', [
      $entity
        ->getEntityTypeId(),
      $entity
        ->id(),
      $view_mode,
      $default,
    ]), ':');

    // Get the custom storage id (omitting revision id if this is the default
    // revision).
    $panelizer_field_storage_id_parts = [
      $entity
        ->getEntityTypeId(),
      $entity
        ->id(),
      $view_mode,
    ];
    if ($revision_id) {
      $panelizer_field_storage_id_parts[] = $revision_id;
    }
    $panelizer_field_storage_id = implode(':', $panelizer_field_storage_id_parts);

    /** @var \Drupal\panelizer\PanelizerInterface $panelizer */
    $panelizer = \Drupal::service('panelizer');
    $build['#attached']['drupalSettings']['panelizer']['entity'] = [
      'entity_type_id' => $entity
        ->getEntityTypeId(),
      'entity_id' => $entity
        ->id(),
      'view_mode' => $view_mode,
      'revision_id' => $revision_id,
      'panelizer_default_storage_id' => $panelizer
        ->hasDefaultPermission('change content', $entity
        ->getEntityTypeId(), $entity
        ->bundle(), $view_mode, $default) ? $panelizer_default_internal_storage_id : FALSE,
      'panelizer_field_storage_id' => $panelizer
        ->hasEntityPermission('change content', $entity, $view_mode) ? $panelizer_field_storage_id : FALSE,
      'panelizer_default_real_storage_id' => $panelizer_default_storage_id,
    ];

    // Whether or not the current user has access to the "revert to default"
    // action in the IPE; any user with the 'administer panelizer' will also
    // have access.
    $build['#attached']['drupalSettings']['panelizer']['user_permission']['revert'] = $panelizer
      ->hasEntityPermission('revert to default', $entity, $view_mode);

    // Whether or not the current user has access to the "set as default" action
    // in the IPE; any user with the 'administer panelizer' will also have
    // access.
    $user = \Drupal::currentUser();
    $build['#attached']['drupalSettings']['panelizer']['user_permission']['save_default'] = $user
      ->hasPermission('set panelizer default') || $user
      ->hasPermission('administer panelizer');
    if ($panels_display
      ->getStorageType() == 'panelizer_field') {

      // If using panelizer_field, we change the storage id to match what we put
      // together here because it'll have the revision id omitted in the right
      // situation.
      $build['#attached']['drupalSettings']['panels_ipe']['panels_display']['storage_id'] = $panelizer_field_storage_id;
    }
    else {

      // If using panelizer_default, we need to switch to a the special,
      // internal storage id.
      $build['#attached']['drupalSettings']['panels_ipe']['panels_display']['storage_id'] = $panelizer_default_internal_storage_id;
    }
  }
}

/**
 * Implements hook_panels_ipe_panels_display_presave().
 */
function panelizer_panels_ipe_panels_display_presave(PanelsDisplayVariant $panels_display, array $layout_model) {
  if (empty($layout_model['panelizer_save_as'])) {
    return;
  }

  // See if the user requested changing the storage type.
  $current_storage = $panels_display
    ->getStorageType();
  $panelizer_save_as = $layout_model['panelizer_save_as'];
  if ($current_storage !== $panelizer_save_as) {
    $panelizer_entity = $layout_model['panelizer_entity'];

    // When actually saving, we want to use the real storage id for me the
    // Panelizer default.
    $panelizer_entity['panelizer_default_storage_id'] = $panelizer_entity['panelizer_default_real_storage_id'];

    // If we were custom and now we want to save to the default, we need to
    // save specially to the Panelizer field so that we can tell it we're on
    // a default.
    if ($panelizer_save_as == 'panelizer_default') {

      /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
      $entity_type_manager = \Drupal::service("entity_type.manager");
      $storage = $entity_type_manager
        ->getStorage($panelizer_entity['entity_type_id']);
      $entity = $storage
        ->load($panelizer_entity['entity_id']);
      if ($entity instanceof FieldableEntityInterface) {

        /** @var \Drupal\panelizer\PanelizerInterface $panelizer */
        $panelizer = \Drupal::service('panelizer');
        list(, , , $default_name) = explode(':', $panelizer_entity['panelizer_default_storage_id']);
        $panelizer
          ->setPanelsDisplay($entity, $panelizer_entity['view_mode'], $default_name);
      }
    }

    // We need to generate a new UUID if we're creating a custom display.
    if ($current_storage == 'panelizer_default' && $panelizer_save_as == 'panelizer_field') {
      $configuration = $panels_display
        ->getConfiguration();
      $configuration['uuid'] = \Drupal::service('uuid')
        ->generate();
      $panels_display
        ->setConfiguration($configuration);
    }

    // Set the new storage information.
    $panels_display
      ->setStorage($panelizer_save_as, $panelizer_entity[$panelizer_save_as . '_storage_id']);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function panelizer_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
  $form_object = $form_state
    ->getFormObject();

  /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
  $display = $form_object
    ->getEntity();

  /** @var \Drupal\panelizer\Plugin\PanelizerEntityManager $panelizer_manager */
  $panelizer_manager = \Drupal::service('plugin.manager.panelizer_entity');

  /** @var \Drupal\panelizer\PanelizerInterface $panelizer */
  $panelizer = \Drupal::service('panelizer');
  $entity_type_id = $display
    ->getTargetEntityTypeId();
  $bundle = $display
    ->getTargetBundle();
  $mode = $display
    ->getMode();
  if ($panelizer_manager
    ->hasDefinition($entity_type_id)) {
    $settings = $panelizer
      ->getPanelizerSettings($entity_type_id, $bundle, $mode, $display);

    // Always put the field table below the Panelizer options.
    $form['fields']['#weight'] = 10;
    $form['panelizer'] = [
      '#tree' => TRUE,
    ];
    $form['panelizer']['enable'] = [
      '#type' => 'checkbox',
      '#title' => t('Panelize this view mode'),
      '#default_value' => isset($settings['enable']) ? $settings['enable'] : FALSE,
    ];
    $entity_type = \Drupal::entityTypeManager()
      ->getDefinition($entity_type_id);
    $form['panelizer']['options'] = [
      '#type' => 'details',
      '#open' => TRUE,
      '#title' => t('Panelizer options'),
      '#states' => [
        'visible' => [
          '#edit-panelizer-enable' => [
            'checked' => TRUE,
          ],
        ],
      ],
      '#parents' => [
        'panelizer',
      ],
    ];
    $form['panelizer']['options']['custom'] = [
      '#type' => 'checkbox',
      '#title' => t('Allow each @entity to have its display customized', [
        '@entity' => $entity_type
          ->getSingularLabel(),
      ]),
      '#default_value' => isset($settings['custom']) ? $settings['custom'] : FALSE,
    ];
    $form['panelizer']['options']['allow'] = [
      '#type' => 'checkbox',
      '#title' => t('Allow users to select which display to use'),
      '#description' => t('When multiple default displays are available for a view mode it can be useful to allow content creators to choose which display to use for an individual @entity.', [
        '@entity' => $entity_type
          ->getSingularLabel(),
      ]),
      '#default_value' => isset($settings['allow']) ? $settings['allow'] : FALSE,
    ];

    // If this display mode is panelized, then show the available displays in a
    // table.
    if (!empty($settings['enable'])) {
      $form['#cache']['tags'][] = "panelizer_default:{$entity_type_id}:{$bundle}:{$mode}";
      $form['panelizer']['displays'] = [
        '#type' => 'table',
        '#caption' => t('Default displays available for this view mode'),
        '#header' => [
          t('Name'),
          t('Use as default'),
          t('Operations'),
        ],
      ];
      foreach ($display
        ->getThirdPartySetting('panelizer', 'displays', []) as $machine_name => $panels_display) {

        // Reset operations when in the foreach loop.
        $operations = [];
        $display_name = $machine_name;
        $machine_name = "{$entity_type_id}__{$bundle}__{$mode}__{$machine_name}";
        $operations['edit'] = [
          'title' => t('Edit'),
          'url' => Url::fromRoute('panelizer.wizard.edit', [
            'machine_name' => $machine_name,
          ]),
        ];
        if ($settings['default'] != $display_name) {
          $operations['set_default'] = [
            'title' => t('Use as default'),
            'url' => Url::fromRoute('panelizer.default.select', [
              'machine_name' => $machine_name,
            ]),
          ];
          $operations['delete'] = [
            'title' => t('Delete'),
            'url' => Url::fromRoute('panelizer.default.delete', [
              'machine_name' => $machine_name,
            ]),
          ];
        }
        $form['panelizer']['displays'][$machine_name] = [
          'label' => [
            '#markup' => $panels_display['label'],
          ],
          'default' => [
            '#markup' => $settings['default'] == $display_name ? '&#10003;' : '',
          ],
          'operations' => [
            'data' => [
              '#type' => 'operations',
              '#links' => $operations,
            ],
          ],
        ];
      }
      $form['fields']['#access'] = FALSE;
    }
    $form['#attached']['library'][] = 'panelizer/panelizer_default_form';
    $form['actions']['submit']['#submit'][] = 'panelizer_form_entity_view_display_edit_submit';
  }
}
function panelizer_form_entity_view_display_edit_submit(&$form, FormStateInterface $form_state) {
  $rebuild = FALSE;

  /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
  $form_object = $form_state
    ->getFormObject();

  /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
  $display = $form_object
    ->getEntity();

  /** @var \Drupal\panelizer\Plugin\PanelizerEntityManager $panelizer_manager */
  $panelizer_manager = \Drupal::service('plugin.manager.panelizer_entity');

  /** @var \Drupal\panelizer\Panelizer $panelizer */
  $panelizer = \Drupal::service('panelizer');
  if ($panelizer_manager
    ->hasDefinition($display
    ->getTargetEntityTypeId())) {
    $settings = $panelizer
      ->getPanelizerSettings($display
      ->getTargetEntityTypeId(), $display
      ->getTargetBundle(), $display
      ->getMode(), $display);
    if ($settings['enable'] != $form_state
      ->getValue([
      'panelizer',
      'enable',
    ])) {
      $rebuild = TRUE;
    }
    $settings['enable'] = $form_state
      ->getValue([
      'panelizer',
      'enable',
    ]);
    $settings['custom'] = $form_state
      ->getValue([
      'panelizer',
      'custom',
    ]);
    $settings['allow'] = $form_state
      ->getValue([
      'panelizer',
      'allow',
    ]);
    $panelizer
      ->setPanelizerSettings($display
      ->getTargetEntityTypeId(), $display
      ->getTargetBundle(), $display
      ->getMode(), $settings, $display);
    if ($rebuild) {
      \Drupal::service('router.builder')
        ->rebuild();

      /** @var \Drupal\Core\Menu\LocalActionManager $local_action_manager */
      $local_action_manager = \Drupal::service('plugin.manager.menu.local_action');
      $local_action_manager
        ->clearCachedDefinitions();

      // Manually reinitialize these.
      $local_action_manager
        ->getDefinitions();
      \Drupal::service('cache.render')
        ->invalidateAll();
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for field_ui_field_storage_add_form().
 */
function panelizer_form_field_ui_field_storage_add_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Hide the legacy Panelizer field.
  // @todo Remove the Panelizer field entirely.
  if (isset($form['add']['new_storage_type']['#options']['General']['panelizer'])) {
    unset($form['add']['new_storage_type']['#options']['General']['panelizer']);
  }
}

/**
 * Preprocess function for panelizer-wizard-tree.html.twig.
 */
function template_preprocess_panelizer_wizard_tree(&$variables) {

  /** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */
  $wizard = $variables['wizard'];
  $cached_values = $variables['cached_values'];
  $tree = $variables['tree'];
  $variables['step'] = $wizard
    ->getStep($cached_values);
  foreach ($wizard
    ->getOperations($cached_values) as $step => $operation) {
    $parameters = $wizard
      ->getNextParameters($cached_values);

    // Override step to be the step we want.
    $parameters['step'] = $step;

    // Fill in parents if there are breadcrumbs.
    $parent =& $tree;
    if (isset($operation['breadcrumbs'])) {
      foreach ($operation['breadcrumbs'] as $breadcrumb) {
        $breadcrumb_string = (string) $breadcrumb;
        if (!isset($parent[$breadcrumb_string])) {
          $parent[$breadcrumb_string] = [
            'title' => $breadcrumb,
            'children' => [],
          ];
        }
        $parent =& $parent[$breadcrumb_string]['children'];
      }
    }
    $parent[$step] = [
      'title' => !empty($operation['title']) ? $operation['title'] : '',
      'url' => new Url($wizard
        ->getRouteName(), $parameters),
      'step' => $step,
    ];
  }
  $variables['tree'] = $tree;
}

/**
 * Preprocess function for panelizer-view-mode.html.twig
 *
 * Prepare variables for Panelizer view mode templates.
 */
function template_preprocess_panelizer_view_mode(&$variables) {
  $element = $variables['element'];

  // Copy values into the variables.

  /** @var \Drupal\panelizer\Plugin\PanelizerEntityInterface $panelizer_plugin */
  $panelizer_plugin = $variables['panelizer_plugin'] = $element['#panelizer_plugin'];

  /** @var \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display */
  $panels_display = $variables['panels_display'] = $element['#panels_display'];

  /** @var \Drupal\Core\Entity\EntityInterface $entity */
  $entity = $variables['entity'] = $element['#entity'];
  $view_mode = $variables['view_mode'] = $element['#view_mode'];
  $variables['content'] = $element['content'];
  $variables['title'] = isset($element['#title']) ? $element['#title'] : '';

  // Setup the defaults.
  $variables['title_element'] = 'h2';
  $variables['entity_url'] = $entity
    ->toUrl('canonical', [
    'language' => $entity
      ->language(),
  ]);

  // Allow the Panelizer entity plugin to do additional preprocessing.
  $panelizer_plugin
    ->preprocessViewMode($variables, $entity, $panels_display, $view_mode);
}