Skip to content
Snippets Groups Projects
Select Git revision
  • 9cf1459a303275a56f887d8bea5f41c95dccd22c
  • 11.x default protected
  • 11.2.x protected
  • 10.6.x protected
  • 10.5.x protected
  • 11.1.x protected
  • 10.4.x protected
  • 11.0.x protected
  • 10.3.x protected
  • 7.x protected
  • 10.2.x protected
  • 10.1.x protected
  • 9.5.x protected
  • 10.0.x protected
  • 9.4.x protected
  • 9.3.x protected
  • 9.2.x protected
  • 9.1.x protected
  • 8.9.x protected
  • 9.0.x protected
  • 8.8.x protected
  • 10.5.1 protected
  • 11.2.2 protected
  • 11.2.1 protected
  • 11.2.0 protected
  • 10.5.0 protected
  • 11.2.0-rc2 protected
  • 10.5.0-rc1 protected
  • 11.2.0-rc1 protected
  • 10.4.8 protected
  • 11.1.8 protected
  • 10.5.0-beta1 protected
  • 11.2.0-beta1 protected
  • 11.2.0-alpha1 protected
  • 10.4.7 protected
  • 11.1.7 protected
  • 10.4.6 protected
  • 11.1.6 protected
  • 10.3.14 protected
  • 10.4.5 protected
  • 11.0.13 protected
41 results

Select.php

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    BlockBase.php 18.45 KiB
    <?php
    
    /**
     * @file
     * Contains \Drupal\Core\Block\BlockBase.
     */
    
    namespace Drupal\Core\Block;
    
    use Drupal\block\BlockInterface;
    use Drupal\block\Event\BlockConditionContextEvent;
    use Drupal\block\Event\BlockEvents;
    use Drupal\Component\Plugin\ContextAwarePluginInterface;
    use Drupal\Core\Condition\ConditionAccessResolverTrait;
    use Drupal\Core\Condition\ConditionPluginBag;
    use Drupal\Core\Form\FormState;
    use Drupal\Core\Form\FormStateInterface;
    use Drupal\Core\Plugin\ContextAwarePluginBase;
    use Drupal\Component\Utility\Unicode;
    use Drupal\Component\Utility\NestedArray;
    use Drupal\Core\Language\LanguageInterface;
    use Drupal\Core\Cache\Cache;
    use Drupal\Core\Session\AccountInterface;
    use Drupal\Component\Transliteration\TransliterationInterface;
    
    /**
     * Defines a base block implementation that most blocks plugins will extend.
     *
     * This abstract class provides the generic block configuration form, default
     * block settings, and handling for general user-defined block visibility
     * settings.
     *
     * @ingroup block_api
     */
    abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface {
    
      use ConditionAccessResolverTrait;
    
      /**
       * The condition plugin bag.
       *
       * @var \Drupal\Core\Condition\ConditionPluginBag
       */
      protected $conditionBag;
    
      /**
       * The condition plugin manager.
       *
       * @var \Drupal\Core\Executable\ExecutableManagerInterface
       */
      protected $conditionPluginManager;
    
      /**
       * The transliteration service.
       *
       * @var \Drupal\Component\Transliteration\TransliterationInterface
       */
      protected $transliteration;
    
      /**
       * {@inheritdoc}
       */
      public function label() {
        if (!empty($this->configuration['label'])) {
          return $this->configuration['label'];
        }
    
        $definition = $this->getPluginDefinition();
        // Cast the admin label to a string since it is an object.
        // @see \Drupal\Core\StringTranslation\TranslationWrapper
        return (string) $definition['admin_label'];
      }
    
      /**
       * {@inheritdoc}
       */
      public function __construct(array $configuration, $plugin_id, $plugin_definition) {
        parent::__construct($configuration, $plugin_id, $plugin_definition);
        $this->setConfiguration($configuration);
      }
    
      /**
       * {@inheritdoc}
       */
      public function getConfiguration() {
        return array(
          'visibility' => $this->getVisibilityConditions()->getConfiguration(),
        ) + $this->configuration;
      }
    
      /**
       * {@inheritdoc}
       */
      public function setConfiguration(array $configuration) {
        $this->configuration = NestedArray::mergeDeep(
          $this->baseConfigurationDefaults(),
          $this->defaultConfiguration(),
          $configuration
        );
      }
    
      /**
       * Returns generic default configuration for block plugins.
       *
       * @return array
       *   An associative array with the default configuration.
       */
      protected function baseConfigurationDefaults() {
        // @todo Allow list of conditions to be configured in
        //   https://drupal.org/node/2284687.
        $visibility = array_map(function ($definition) {
          return array('id' => $definition['id']);
        }, $this->conditionPluginManager()->getDefinitions());
        unset($visibility['current_theme']);
    
        return array(
          'id' => $this->getPluginId(),
          'label' => '',
          'provider' => $this->pluginDefinition['provider'],
          'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
          'cache' => array(
            'max_age' => 0,
            'contexts' => array(),
          ),
          'visibility' => $visibility,
        );
      }
    
      /**
       * {@inheritdoc}
       */
      public function defaultConfiguration() {
        return array();
      }
    
      /**
       * {@inheritdoc}
       */
      public function setConfigurationValue($key, $value) {
        $this->configuration[$key] = $value;
      }
    
      /**
       * {@inheritdoc}
       */
      public function calculateDependencies() {
        return array();
      }
    
      /**
       * {@inheritdoc}
       */
      public function access(AccountInterface $account) {
        // @todo Add in a context mapping until the UI supports configuring them,
        //   see https://drupal.org/node/2284687.
        $mappings['user_role']['current_user'] = 'user';
    
        $conditions = $this->getVisibilityConditions();
        $contexts = $this->getConditionContexts();
        foreach ($conditions as $condition_id => $condition) {
          if ($condition instanceof ContextAwarePluginInterface) {
            if (!isset($mappings[$condition_id])) {
              $mappings[$condition_id] = array();
            }
            $this->contextHandler()->applyContextMapping($condition, $contexts, $mappings[$condition_id]);
          }
        }
        if ($this->resolveConditions($conditions, 'and', $contexts, $mappings) === FALSE) {
          return FALSE;
        }
        return $this->blockAccess($account);
      }
    
      /**
       * Gets the values for all defined contexts.
       *
       * @return \Drupal\Component\Plugin\Context\ContextInterface[]
       *   An array of set contexts, keyed by context name.
       */
      protected function getConditionContexts() {
        $conditions = $this->getVisibilityConditions();
        $this->eventDispatcher()->dispatch(BlockEvents::CONDITION_CONTEXT, new BlockConditionContextEvent($conditions));
        return $conditions->getConditionContexts();
      }
    
      /**
       * Indicates whether the block should be shown.
       *
       * @param \Drupal\Core\Session\AccountInterface $account
       *   The user session for which to check access.
       *
       * @return bool
       *   TRUE if the block should be shown, or FALSE otherwise.
       *
       * @see self::access()
       */
      protected function blockAccess(AccountInterface $account) {
        // By default, the block is visible.
        return TRUE;
      }
    
      /**
       * {@inheritdoc}
       *
       * Creates a generic configuration form for all block types. Individual
       * block plugins can add elements to this form by overriding
       * BlockBase::blockForm(). Most block plugins should not override this
       * method unless they need to alter the generic form elements.
       *
       * @see \Drupal\Core\Block\BlockBase::blockForm()
       */
      public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
        $definition = $this->getPluginDefinition();
        $form['provider'] = array(
          '#type' => 'value',
          '#value' => $definition['provider'],
        );
    
        $form['admin_label'] = array(
          '#type' => 'item',
          '#title' => t('Block description'),
          '#markup' => $definition['admin_label'],
        );
        $form['label'] = array(
          '#type' => 'textfield',
          '#title' => $this->t('Title'),
          '#maxlength' => 255,
          '#default_value' => $this->label(),
          '#required' => TRUE,
        );
        $form['label_display'] = array(
          '#type' => 'checkbox',
          '#title' => $this->t('Display title'),
          '#default_value' => ($this->configuration['label_display'] === BlockInterface::BLOCK_LABEL_VISIBLE),
          '#return_value' => BlockInterface::BLOCK_LABEL_VISIBLE,
        );
        // Identical options to the ones for page caching.
        // @see \Drupal\system\Form\PerformanceForm::buildForm()
        $period = array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400);
        $period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($period, $period));
        $period[0] = '<' . t('no caching') . '>';
        $period[\Drupal\Core\Cache\Cache::PERMANENT] = t('Forever');
        $form['cache'] = array(
          '#type' => 'details',
          '#title' => t('Cache settings'),
        );
        $form['cache']['max_age'] = array(
          '#type' => 'select',
          '#title' => t('Maximum age'),
          '#description' => t('The maximum time this block may be cached.'),
          '#default_value' => $this->configuration['cache']['max_age'],
          '#options' => $period,
        );
        $contexts = \Drupal::service("cache_contexts")->getLabels();
        // Blocks are always rendered in a "per theme" cache context. No need to
        // show that option to the end user.
        unset($contexts['cache_context.theme']);
        $form['cache']['contexts'] = array(
          '#type' => 'checkboxes',
          '#title' => t('Vary by context'),
          '#description' => t('The contexts this cached block must be varied by.'),
          '#default_value' => $this->configuration['cache']['contexts'],
          '#options' => $contexts,
          '#states' => array(
            'disabled' => array(
              ':input[name="settings[cache][max_age]"]' => array('value' => (string) 0),
            ),
          ),
        );
        if (count($this->getRequiredCacheContexts()) > 0) {
          // Remove the required cache contexts from the list of contexts a user can
          // choose to modify by: they must always be applied.
          $context_labels = array();
          foreach ($this->getRequiredCacheContexts() as $context) {
            $context_labels[] = $form['cache']['contexts']['#options'][$context];
            unset($form['cache']['contexts']['#options'][$context]);
          }
          $required_context_list = implode(', ', $context_labels);
          $form['cache']['contexts']['#description'] .= ' ' . t('This block is <em>always</em> varied by the following contexts: %required-context-list.', array('%required-context-list' => $required_context_list));
        }
    
        $form['visibility_tabs'] = array(
          '#type' => 'vertical_tabs',
          '#title' => $this->t('Visibility'),
          '#parents' => array('visibility_tabs'),
          '#attached' => array(
            'library' => array(
              'block/drupal.block',
            ),
          ),
        );
        foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
          $condition_form = $condition->buildConfigurationForm(array(), $form_state);
          $condition_form['#type'] = 'details';
          $condition_form['#title'] = $condition->getPluginDefinition()['label'];
          $condition_form['#group'] = 'visibility_tabs';
          $form['visibility'][$condition_id] = $condition_form;
        }
    
        // @todo Determine if there is a better way to rename the conditions.
        if (isset($form['visibility']['node_type'])) {
          $form['visibility']['node_type']['#title'] = $this->t('Content types');
          $form['visibility']['node_type']['bundles']['#title'] = $this->t('Content types');
          $form['visibility']['node_type']['negate']['#type'] = 'value';
          $form['visibility']['node_type']['negate']['#title_display'] = 'invisible';
          $form['visibility']['node_type']['negate']['#value'] = $form['visibility']['node_type']['negate']['#default_value'];
        }
        if (isset($form['visibility']['user_role'])) {
          $form['visibility']['user_role']['#title'] = $this->t('Roles');
          unset($form['visibility']['user_role']['roles']['#description']);
          $form['visibility']['user_role']['negate']['#type'] = 'value';
          $form['visibility']['user_role']['negate']['#value'] = $form['visibility']['user_role']['negate']['#default_value'];
        }
        if (isset($form['visibility']['request_path'])) {
          $form['visibility']['request_path']['#title'] = $this->t('Pages');
          $form['visibility']['request_path']['negate']['#type'] = 'radios';
          $form['visibility']['request_path']['negate']['#title_display'] = 'invisible';
          $form['visibility']['request_path']['negate']['#default_value'] = (int) $form['visibility']['request_path']['negate']['#default_value'];
          $form['visibility']['request_path']['negate']['#options'] = array(
            $this->t('Show for the listed pages'),
            $this->t('Hide for the listed pages'),
          );
        }
        if (isset($form['visibility']['language'])) {
          $form['visibility']['language']['negate']['#type'] = 'value';
          $form['visibility']['language']['negate']['#value'] = $form['visibility']['language']['negate']['#default_value'];
        }
    
        // Add plugin-specific settings for this block type.
        $form += $this->blockForm($form, $form_state);
        return $form;
      }
    
      /**
       * {@inheritdoc}
       */
      public function blockForm($form, FormStateInterface $form_state) {
        return array();
      }
    
      /**
       * {@inheritdoc}
       *
       * Most block plugins should not override this method. To add validation
       * for a specific block type, override BlockBase::blockValdiate().
       *
       * @see \Drupal\Core\Block\BlockBase::blockValidate()
       */
      public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
        // Remove the admin_label form item element value so it will not persist.
        $form_state->unsetValue('admin_label');
    
        // Transform the #type = checkboxes value to a numerically indexed array.
        $contexts = $form_state->getValue(array('cache', 'contexts'));
        $form_state->setValue(array('cache', 'contexts'), array_values(array_filter($contexts)));
    
        foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
          // Allow the condition to validate the form.
          $condition_values = new FormState(array(
            'values' => $form_state->getValue(array('visibility', $condition_id)),
          ));
          $condition->validateConfigurationForm($form, $condition_values);
          // Update the original form values.
          $form_state->setValue(array('visibility', $condition_id), $condition_values['values']);
        }
    
        $this->blockValidate($form, $form_state);
      }
    
      /**
       * {@inheritdoc}
       */
      public function blockValidate($form, FormStateInterface $form_state) {}
    
      /**
       * {@inheritdoc}
       *
       * Most block plugins should not override this method. To add submission
       * handling for a specific block type, override BlockBase::blockSubmit().
       *
       * @see \Drupal\Core\Block\BlockBase::blockSubmit()
       */
      public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
        // Process the block's submission handling if no errors occurred only.
        if (!$form_state->getErrors()) {
          $this->configuration['label'] = $form_state->getValue('label');
          $this->configuration['label_display'] = $form_state->getValue('label_display');
          $this->configuration['provider'] = $form_state->getValue('provider');
          $this->configuration['cache'] = $form_state->getValue('cache');
          foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
            // Allow the condition to submit the form.
            $condition_values = new FormState(array(
              'values' => $form_state->getValue(array('visibility', $condition_id)),
            ));
            $condition->submitConfigurationForm($form, $condition_values);
            // Update the original form values.
            $form_state->setValue(array('visibility', $condition_id), $condition_values['values']);
          }
          $this->blockSubmit($form, $form_state);
        }
      }
    
      /**
       * {@inheritdoc}
       */
      public function blockSubmit($form, FormStateInterface $form_state) {}
    
      /**
       * {@inheritdoc}
       */
      public function getMachineNameSuggestion() {
        $definition = $this->getPluginDefinition();
        $admin_label = $definition['admin_label'];
    
        // @todo This is basically the same as what is done in
        //   \Drupal\system\MachineNameController::transliterate(), so it might make
        //   sense to provide a common service for the two.
        $transliterated = $this->transliteration()->transliterate($admin_label, LanguageInterface::LANGCODE_DEFAULT, '_');
    
        $replace_pattern = '[^a-z0-9_.]+';
    
        $transliterated = Unicode::strtolower($transliterated);
    
        if (isset($replace_pattern)) {
          $transliterated = preg_replace('@' . $replace_pattern . '@', '', $transliterated);
        }
    
        return $transliterated;
      }
    
      /**
       * Wraps the transliteration service.
       *
       * @return \Drupal\Component\Transliteration\TransliterationInterface
       */
      protected function transliteration() {
        if (!$this->transliteration) {
          $this->transliteration = \Drupal::transliteration();
        }
        return $this->transliteration;
      }
    
      /**
       * Sets the transliteration service.
       *
       * @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
       *   The transliteration service.
       */
      public function setTransliteration(TransliterationInterface $transliteration) {
        $this->transliteration = $transliteration;
      }
    
      /**
       * Returns the cache contexts required for this block.
       *
       * @return array
       *   The required cache contexts IDs.
       */
      protected function getRequiredCacheContexts() {
        return array();
      }
    
      /**
       * {@inheritdoc}
       */
      public function getCacheKeys() {
        // Return the required cache contexts, merged with the user-configured cache
        // contexts, if any.
        return array_merge($this->getRequiredCacheContexts(), $this->configuration['cache']['contexts']);
      }
    
      /**
       * {@inheritdoc}
       */
      public function getCacheTags() {
        // If a block plugin's output changes, then it must be able to invalidate a
        // cache tag that affects all instances of this block: across themes and
        // across regions.
        $block_plugin_cache_tag = str_replace(':', '__', $this->getPluginID());
        return array('block_plugin' => array($block_plugin_cache_tag));
      }
    
      /**
       * {@inheritdoc}
       */
      public function getCacheBin() {
        return 'render';
      }
    
      /**
       * {@inheritdoc}
       */
      public function getCacheMaxAge() {
        return (int)$this->configuration['cache']['max_age'];
      }
    
      /**
       * {@inheritdoc}
       */
      public function isCacheable() {
        // Similar to the page cache, a block is cacheable if it has a max age.
        // Blocks that should never be cached can override this method to simply
        // return FALSE.
        $max_age = $this->getCacheMaxAge();
        return $max_age === Cache::PERMANENT || $max_age > 0;
      }
    
      /**
       * {@inheritdoc}
       */
      public function getVisibilityConditions() {
        if (!isset($this->conditionBag)) {
          $this->conditionBag = new ConditionPluginBag($this->conditionPluginManager(), $this->configuration['visibility']);
        }
        return $this->conditionBag;
      }
    
      /**
       * {@inheritdoc}
       */
      public function getVisibilityCondition($instance_id) {
        return $this->getVisibilityConditions()->get($instance_id);
      }
    
      /**
       * {@inheritdoc}
       */
      public function setVisibilityConfig($instance_id, array $configuration) {
        $this->getVisibilityConditions()->setInstanceConfiguration($instance_id, $configuration);
        return $this;
      }
    
      /**
       * Gets the condition plugin manager.
       *
       * @return \Drupal\Core\Executable\ExecutableManagerInterface
       *   The condition plugin manager.
       */
      protected function conditionPluginManager() {
        if (!isset($this->conditionPluginManager)) {
          $this->conditionPluginManager = \Drupal::service('plugin.manager.condition');
        }
        return $this->conditionPluginManager;
      }
    
      /**
       * Wraps the event dispatcher.
       *
       * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
       *   The event dispatcher.
       */
      protected function eventDispatcher() {
        return \Drupal::service('event_dispatcher');
      }
    
      /**
       * Wraps the context handler.
       *
       * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
       */
      protected function contextHandler() {
        return \Drupal::service('context.handler');
      }
    
    }