Newer
Older
Angie Byron
committed
<?php
namespace Drupal\block;
use Drupal\Component\Utility\Html;
Alex Pott
committed
use Drupal\Core\Entity\EntityForm;
Angie Byron
committed
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Executable\ExecutableManagerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
Dries Buytaert
committed
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
Angie Byron
committed
/**
Alex Pott
committed
* Provides form for block instance forms.
Angie Byron
committed
*/
Alex Pott
committed
class BlockForm extends EntityForm {
/**
* The block entity.
*
* @var \Drupal\block\BlockInterface
*/
protected $entity;
/**
catch
committed
* The block storage.
*
catch
committed
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
catch
committed
protected $storage;
/**
* The condition plugin manager.
*
* @var \Drupal\Core\Condition\ConditionManager
*/
protected $manager;
/**
* The event dispatcher service.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $dispatcher;
/**
* The language manager service.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $language;
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandler
*/
protected $themeHandler;
/**
* The context repository service.
*
* @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
*/
protected $contextRepository;
/**
Alex Pott
committed
* Constructs a BlockForm object.
*
Angie Byron
committed
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
* The ConditionManager for building the visibility UI.
* @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
* The lazy context repository service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language
* The language manager.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
*/
public function __construct(EntityManagerInterface $entity_manager, ExecutableManagerInterface $manager, ContextRepositoryInterface $context_repository, LanguageManagerInterface $language, ThemeHandlerInterface $theme_handler) {
catch
committed
$this->storage = $entity_manager->getStorage('block');
$this->manager = $manager;
$this->contextRepository = $context_repository;
$this->language = $language;
$this->themeHandler = $theme_handler;
}
/**
* {@inheritdoc}
*/
Alex Pott
committed
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('plugin.manager.condition'),
$container->get('context.repository'),
$container->get('language_manager'),
$container->get('theme_handler')
);
}
Angie Byron
committed
/**
* {@inheritdoc}
Angie Byron
committed
*/
Dries Buytaert
committed
public function form(array $form, FormStateInterface $form_state) {
$entity = $this->entity;
Angie Byron
committed
// Store theme settings in $form_state for use below.
if (!$theme = $entity->getTheme()) {
Alex Pott
committed
$theme = $this->config('system.theme')->get('default');
Angie Byron
committed
}
Angie Byron
committed
$form_state->set('block_theme', $theme);
Angie Byron
committed
// Store the gathered contexts in the form state for other objects to use
// during form building.
$form_state->setTemporaryValue('gathered_contexts', $this->contextRepository->getAvailableContexts());
$form['#tree'] = TRUE;
$form['settings'] = $entity->getPlugin()->buildConfigurationForm(array(), $form_state);
$form['visibility'] = $this->buildVisibilityInterface([], $form_state);
// If creating a new block, calculate a safe default machine name.
$form['id'] = array(
'#type' => 'machine_name',
'#maxlength' => 64,
'#description' => $this->t('A unique name for this block instance. Must be alpha-numeric and underscore separated.'),
'#default_value' => !$entity->isNew() ? $entity->id() : $this->getUniqueMachineName($entity),
'#machine_name' => array(
'exists' => '\Drupal\block\Entity\Block::load',
'replace_pattern' => '[^a-z0-9_.]+',
'source' => array('settings', 'label'),
),
'#required' => TRUE,
'#disabled' => !$entity->isNew(),
);
// Theme settings.
if ($entity->getTheme()) {
$form['theme'] = array(
'#type' => 'value',
Angie Byron
committed
'#value' => $theme,
);
}
else {
$theme_options = array();
foreach ($this->themeHandler->listInfo() as $theme_name => $theme_info) {
if (!empty($theme_info->status)) {
$theme_options[$theme_name] = $theme_info->info['name'];
}
}
$form['theme'] = array(
'#type' => 'select',
'#options' => $theme_options,
'#title' => t('Theme'),
'#default_value' => $theme,
'#ajax' => array(
'callback' => '::themeSwitch',
'wrapper' => 'edit-block-region-wrapper',
),
);
}
Angie Byron
committed
// Region settings.
$entity_region = $entity->getRegion();
$region = $entity->isNew() ? $this->getRequest()->query->get('region', $entity_region) : $entity_region;
$form['region'] = array(
'#type' => 'select',
'#title' => $this->t('Region'),
'#description' => $this->t('Select the region where this block should be displayed.'),
'#default_value' => $region,
Alex Pott
committed
'#empty_value' => BlockInterface::BLOCK_REGION_NONE,
'#options' => system_region_list($theme, REGIONS_VISIBLE),
'#prefix' => '<div id="edit-block-region-wrapper">',
'#suffix' => '</div>',
);
$form['#attached']['library'][] = 'block/drupal.block.admin';
return $form;
Angie Byron
committed
}
/**
* Handles switching the available regions based on the selected theme.
*/
Dries Buytaert
committed
public function themeSwitch($form, FormStateInterface $form_state) {
Alex Pott
committed
$form['region']['#options'] = system_region_list($form_state->getValue('theme'), REGIONS_VISIBLE);
return $form['region'];
}
/**
* Helper function for building the visibility UI form.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form array with the visibility UI added in.
*/
protected function buildVisibilityInterface(array $form, FormStateInterface $form_state) {
$form['visibility_tabs'] = [
'#type' => 'vertical_tabs',
'#title' => $this->t('Visibility'),
'#parents' => ['visibility_tabs'],
'#attached' => [
'library' => [
'block/drupal.block',
],
],
];
// @todo Allow list of conditions to be configured in
// https://www.drupal.org/node/2284687.
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
$visibility = $this->entity->getVisibility();
foreach ($this->manager->getDefinitions() as $condition_id => $definition) {
// Don't display the current theme condition.
if ($condition_id == 'current_theme') {
continue;
}
// Don't display the language condition until we have multiple languages.
if ($condition_id == 'language' && !$this->language->isMultilingual()) {
continue;
}
/** @var \Drupal\Core\Condition\ConditionInterface $condition */
$condition = $this->manager->createInstance($condition_id, isset($visibility[$condition_id]) ? $visibility[$condition_id] : []);
$form_state->set(['conditions', $condition_id], $condition);
$condition_form = $condition->buildConfigurationForm([], $form_state);
$condition_form['#type'] = 'details';
$condition_form['#title'] = $condition->getPluginDefinition()['label'];
$condition_form['#group'] = 'visibility_tabs';
$form[$condition_id] = $condition_form;
}
if (isset($form['node_type'])) {
$form['node_type']['#title'] = $this->t('Content types');
$form['node_type']['bundles']['#title'] = $this->t('Content types');
$form['node_type']['negate']['#type'] = 'value';
$form['node_type']['negate']['#title_display'] = 'invisible';
$form['node_type']['negate']['#value'] = $form['node_type']['negate']['#default_value'];
}
if (isset($form['user_role'])) {
$form['user_role']['#title'] = $this->t('Roles');
unset($form['user_role']['roles']['#description']);
$form['user_role']['negate']['#type'] = 'value';
$form['user_role']['negate']['#value'] = $form['user_role']['negate']['#default_value'];
}
if (isset($form['request_path'])) {
$form['request_path']['#title'] = $this->t('Pages');
$form['request_path']['negate']['#type'] = 'radios';
Alex Pott
committed
$form['request_path']['negate']['#default_value'] = (int) $form['request_path']['negate']['#default_value'];
$form['request_path']['negate']['#title_display'] = 'invisible';
$form['request_path']['negate']['#options'] = [
$this->t('Show for the listed pages'),
$this->t('Hide for the listed pages'),
];
}
if (isset($form['language'])) {
$form['language']['negate']['#type'] = 'value';
$form['language']['negate']['#value'] = $form['language']['negate']['#default_value'];
}
return $form;
}
Angie Byron
committed
/**
* {@inheritdoc}
Angie Byron
committed
*/
Dries Buytaert
committed
protected function actions(array $form, FormStateInterface $form_state) {
Angie Byron
committed
$actions = parent::actions($form, $form_state);
$actions['submit']['#value'] = $this->t('Save block');
Angie Byron
committed
return $actions;
}
/**
* {@inheritdoc}
Angie Byron
committed
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
Angie Byron
committed
// The Block Entity form puts all block plugin form elements in the
// settings form element, so just pass that to the block for validation.
$settings = (new FormState())->setValues($form_state->getValue('settings'));
// Call the plugin validate handler.
$this->entity->getPlugin()->validateConfigurationForm($form, $settings);
Dries Buytaert
committed
// Update the original form values.
Angie Byron
committed
$form_state->setValue('settings', $settings->getValues());
$this->validateVisibility($form, $form_state);
}
/**
* Helper function to independently validate the visibility UI.
*
* @param array $form
* A nested array form elements comprising the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
protected function validateVisibility(array $form, FormStateInterface $form_state) {
// Validate visibility condition settings.
foreach ($form_state->getValue('visibility') as $condition_id => $values) {
Alex Pott
committed
// All condition plugins use 'negate' as a Boolean in their schema.
// However, certain form elements may return it as 0/1. Cast here to
// ensure the data is in the expected type.
if (array_key_exists('negate', $values)) {
$values['negate'] = (bool) $values['negate'];
}
// Allow the condition to validate the form.
$condition = $form_state->get(['conditions', $condition_id]);
$condition_values = (new FormState())
->setValues($values);
$condition->validateConfigurationForm($form, $condition_values);
// Update the original form values.
$form_state->setValue(['visibility', $condition_id], $condition_values->getValues());
}
Angie Byron
committed
}
/**
* {@inheritdoc}
Angie Byron
committed
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
Angie Byron
committed
$entity = $this->entity;
// The Block Entity form puts all block plugin form elements in the
// settings form element, so just pass that to the block for submission.
Angie Byron
committed
// @todo Find a way to avoid this manipulation.
$settings = (new FormState())->setValues($form_state->getValue('settings'));
Angie Byron
committed
Angie Byron
committed
// Call the plugin submit handler.
$entity->getPlugin()->submitConfigurationForm($form, $settings);
Angie Byron
committed
$block = $entity->getPlugin();
// If this block is context-aware, set the context mapping.
if ($block instanceof ContextAwarePluginInterface && $block->getContextDefinitions()) {
$context_mapping = $settings->getValue('context_mapping', []);
$block->setContextMapping($context_mapping);
}
Dries Buytaert
committed
// Update the original form values.
Angie Byron
committed
$form_state->setValue('settings', $settings->getValues());
Angie Byron
committed
// Submit visibility condition settings.
foreach ($form_state->getValue('visibility') as $condition_id => $values) {
// Allow the condition to submit the form.
$condition = $form_state->get(['conditions', $condition_id]);
$condition_values = (new FormState())
->setValues($values);
$condition->submitConfigurationForm($form, $condition_values);
if ($condition instanceof ContextAwarePluginInterface) {
$context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : [];
$condition->setContextMapping($context_mapping);
}
// Update the original form values.
$condition_configuration = $condition->getConfiguration();
$form_state->setValue(['visibility', $condition_id], $condition_configuration);
// Update the visibility conditions on the block.
$entity->getVisibilityConditions()->addInstanceId($condition_id, $condition_configuration);
}
Angie Byron
committed
// Save the settings of the plugin.
$entity->save();
drupal_set_message($this->t('The block configuration has been saved.'));
Alex Pott
committed
$form_state->setRedirect(
'block.admin_display_theme',
array(
Alex Pott
committed
'theme' => $form_state->getValue('theme'),
Angie Byron
committed
),
array('query' => array('block-placement' => Html::getClass($this->entity->id())))
Angie Byron
committed
);
Angie Byron
committed
}
/**
* Generates a unique machine name for a block.
*
* @param \Drupal\block\BlockInterface $block
* The block entity.
*
* @return string
* Returns the unique name.
*/
public function getUniqueMachineName(BlockInterface $block) {
$suggestion = $block->getPlugin()->getMachineNameSuggestion();
// Get all the blocks which starts with the suggested machine name.
catch
committed
$query = $this->storage->getQuery();
$query->condition('id', $suggestion, 'CONTAINS');
$block_ids = $query->execute();
$block_ids = array_map(function ($block_id) {
$parts = explode('.', $block_id);
return end($parts);
}, $block_ids);
// Iterate through potential IDs until we get a new one. E.g.
// 'plugin', 'plugin_2', 'plugin_3', etc.
$count = 1;
$machine_default = $suggestion;
while (in_array($machine_default, $block_ids)) {
$machine_default = $suggestion . '_' . ++$count;
}
return $machine_default;
}