a12s-1.0.0-beta7/modules/page_context/src/Entity/PageContextForm.php
modules/page_context/src/Entity/PageContextForm.php
<?php
namespace Drupal\a12s_page_context\Entity;
use Drupal\a12s_page_context\Form\PageContextForm as ContextEditForm;
use Drupal\a12s_page_context\Plugin\FormDisplayPluginInterface;
use Drupal\a12s_page_context\RecordSet;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Serialization\Yaml;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\Error;
/**
* Defines the page context form entity type.
*
* @ConfigEntityType(
* id = "a12s_page_context_form",
* label = @Translation("Page context form"),
* label_collection = @Translation("Page context forms"),
* label_singular = @Translation("Page context form"),
* label_plural = @Translation("Page context forms"),
* label_count = @PluralTranslation(
* singular = "@count page context form",
* plural = "@count page context forms",
* ),
* handlers = {
* "list_builder" =
* "Drupal\a12s_page_context\Entity\PageContextFormListBuilder",
* "form" = {
* "add" = "Drupal\a12s_page_context\Form\PageContextForm",
* "edit" = "Drupal\a12s_page_context\Form\PageContextForm",
* "delete" = "Drupal\Core\Entity\EntityDeleteForm",
* },
* "access" =
* "\Drupal\a12s_page_context\Access\PageContextFormAccessControlHandler",
* "route_provider" = {
* "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
* }
* },
* config_prefix = "form",
* admin_permission = "administer a12s_page_context forms",
* links = {
* "collection" = "/admin/config/workflow/a12s-page-context",
* "add-form" = "/admin/config/workflow/a12s-page-context/add",
* "edit-form" =
* "/admin/config/workflow/a12s-page-context/{a12s_page_context_form}",
* "delete-form" =
* "/admin/config/workflow/a12s-page-context/{a12s_page_context_form}/delete"
* },
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* config_export = {
* "id",
* "label",
* "description",
* "definition",
* "all_themes",
* "themes",
* "display_settings"
* }
* )
*/
class PageContextForm extends ConfigEntityBase implements PageContextFormInterface {
use StringTranslationTrait;
/**
* The page context form ID.
*
* @var string
*/
protected string $id;
/**
* The page context form label.
*
* @var string
*/
protected string $label;
/**
* The page context form status.
*
* @var bool
*/
protected $status;
/**
* The page context form description.
*
* @var string
*/
protected string $description = '';
/**
* The definition of the form elements, as defined by the form API.
*
* @var string
*/
protected string $definition;
/**
* Whether the page context form applies to all active themes.
*
* @var bool
*/
protected bool $all_themes;
/**
* The list of themes for which the form is available.
*
* This applies only when the "all_themes" option is enabled.
*
* @var array
*/
protected array $themes;
/**
* The list of plugins used for displaying the form.
*
* @var array
*/
protected array $display_settings;
/**
* The list of enabled display plugin, as plugin instances.
*
* @var array<string, \Drupal\a12s_page_context\Plugin\FormDisplayPluginInterface>
*/
protected array $display_plugins;
/**
* {@inheritdoc}
*/
public function setThemes(array $themes): void {
$this->themes = $themes;
}
/**
* {@inheritdoc}
*/
public function getDisplaySettings(): array {
return $this->display_settings ?? [];
}
/**
* The stored setting.
*
* @param array $setting
* The stored setting.
*
* @return \Drupal\a12s_page_context\Plugin\FormDisplayPluginInterface|null
* The plugin instance.
*/
protected function createDisplayInstance(array $setting): ?FormDisplayPluginInterface {
$setting += ['plugin_id' => NULL, 'configuration' => []];
if ($setting['plugin_id']) {
try {
return \Drupal::service('plugin.manager.a12s_page_context_form_display')->createInstance($setting['plugin_id'], $setting['configuration']);
}
catch (PluginException $e) {
Error::logException(\Drupal::logger('a12s_page_context'), $e);
}
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function getDisplayPlugins(): array {
if (!isset($this->display_plugins)) {
$initPlugins = fn($plugins, array $setting) => ($plugin = $this->createDisplayInstance($setting)) ? array_merge($plugins, [$plugin->getPluginId() => $plugin]) : $plugins;
$this->display_plugins = array_reduce($this->getDisplaySettings(), $initPlugins, []);
}
return $this->display_plugins;
}
/**
* {@inheritdoc}
*/
public function getDisplayPlugin(string $pluginId): ?FormDisplayPluginInterface {
return $this->getDisplayPlugins()[$pluginId] ?? NULL;
}
/**
* {@inheritdoc}
*/
public function setDisplaySettings(array $displaySettings): void {
$this->display_settings = $displaySettings;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies(): ConfigEntityInterface {
parent::calculateDependencies();
if (empty($this->all_themes)) {
foreach ($this->themes ?? [] as $theme) {
$this->addDependency('theme', $theme);
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function getFullDescription(): MarkupInterface {
$html = [];
if ($description = $this->get('description')) {
$html[] = nl2br($description);
}
if ($this->get('all_themes')) {
$html[] = 'Used on all themes';
}
else {
$themes = ContextEditForm::getThemesList();
$themes = array_intersect_key($themes, array_flip($this->get('themes') ?? []));
$html[] = $this->formatPlural(count($themes), 'Used on the following theme: %themes', 'Used on the following themes: %themes', [
'%themes' => implode(', ', $themes),
]);
}
return Markup::create(implode('<br>', $html));
}
/**
* {@inheritdoc}
*/
public function getRecords(array $context = []): RecordSet {
return RecordSet::load($this, $context);
}
/**
* {@inheritdoc}
*/
public function getData(array $context = []): array {
return $this->getRecords($context)->getData();
}
/**
* {@inheritdoc}
*/
public function getForm(FormDisplayPluginInterface $plugin, array $context, array $form, FormStateInterface $form_state): array {
if (!empty($this->definition)) {
try {
$parents = $form['#parents'] ?? ['a12s_page_context', $this->id()];
$record = $plugin->getRecord($this->id(), $context);
$form['id']['#value'] = $record['id'];
$form['key']['#value'] = $record['key'];
$form['data'] = [];
$context['rel'] = 'edit_form';
// @todo How to manage several themes?
$context += ['theme' => \Drupal::service('theme_handler')->getDefault()];
$records = $this->getRecords($context);
$parentValues = [];
// Try to find parent values by priority.
foreach ($records as $currentRecord) {
$pluginId = $currentRecord->getPluginId();
if ($pluginId === $plugin->getPluginId()) {
break;
}
if ($displayPlugin = $this->getDisplayPlugin($pluginId)) {
$contextCopy = $displayPlugin->getCurrentContext($context);
// Plugins which does not have a storage key, like the "path"
// plugin, are ignored here. This is expected as they can have
// multiple values, and it is not possible to know which one will
// apply.
if ($displayPlugin->getStorageKey($contextCopy)) {
$parentValues = $records->compileRecordData($currentRecord->getData(), $parentValues);
}
}
}
$elements = Yaml::decode($this->definition);
if (is_array($elements)) {
$this->buildElementsRecursive($elements, $form['data'], $record['data'], array_merge($parents, ['data']), $parentValues);
return $form;
}
}
catch (\Exception $e) {
Error::logException(\Drupal::logger('a12s_page_context'), $e);
}
}
return [];
}
/**
* Build page context form elements.
*
* @param array $elements
* The page context form elements.
* @param array $builtElements
* The built elements.
* @param array $data
* The stored data.
* @param array $parents
* The array of #parents where the field lives in the COMPLETE form, not
* only the subform.
* @param array $parentValues
* The values defined by parent plugins.
*
* @todo Manage translations.
*/
protected function buildElementsRecursive(array &$elements, array &$builtElements, array $data = [], array $parents = [], array $parentValues = []): void {
foreach ($elements as $key => &$element) {
// Ignore non-form elements.
if ((isset($key[0]) && $key[0] === '#') || !is_array($element)) {
continue;
}
$builtElements[$key] = ['#a12s_page_context_form_id' => $this->id()];
$elementParents = array_merge($parents, [$key]);
foreach ($element as $propertyKey => $value) {
if ($propertyKey && is_string($propertyKey) && $propertyKey[0] === '#') {
$builtElements[$key][$propertyKey] = $value;
}
}
if (!empty($element['#type']) && !in_array($element['#type'], ['submit', 'button'])) {
$elementInfo = \Drupal::service('element_info')->getInfo($element['#type']);
if (!empty($elementInfo['#input'])) {
if (array_key_exists($key, $data)) {
$builtElements[$key]['#default_value'] = $data[$key];
}
if (array_key_exists($key, $parentValues)) {
$overrideKey = $key . '_override';
$override = !isset($data[$overrideKey]) || !empty($data[$overrideKey]);
$builtElements[$key]['#parents'] = $elementParents;
$builtElements[$key]['#wrapper_attributes']['class'][] = 'override-target';
$overrideParents = array_merge($parents, [$overrideKey]);
$inputName = reset($overrideParents);
if (count($overrideParents) > 1) {
$inputName .= '[' . implode('][', array_slice($overrideParents, 1)) . ']';
}
$builtElements[$key]['#states']['enabled'][':input[name="' . $inputName . '"]']['checked'] = TRUE;
/* if (!$override) {
$builtElements[$key]['#default_value'] = $parentValues[$key];
}*/
$builtElements[$key] = [
'#type' => 'container',
'#attributes' => ['class' => ['a12s-page-context-form-override']],
'_override' => [
'#type' => 'checkbox',
'#title' => $this->t('Override'),
'#default_value' => $override,
'#label_attributes' => ['title' => $this->t('Override')],
'#wrapper_attributes' => ['class' => ['override-toggle']],
'#parents' => $overrideParents,
],
'_value' => $builtElements[$key],
];
}
}
}
$childData = isset($data[$key]) && is_array($data[$key]) ? $data[$key] : [];
$currentValues = isset($parentValues[$key]) && is_array($parentValues[$key]) ? $parentValues[$key] : [];
$this->buildElementsRecursive($element, $builtElements[$key], $childData, $elementParents, $currentValues);
}
}
/**
* {@inheritdoc}
*/
public static function alterForm(string $pluginId, array $context, array &$form, FormStateInterface $form_state, array $parents = ['a12s_page_context']): void {
/** @var \Drupal\a12s_page_context\Entity\PageContextFormInterface $pageContextForm */
foreach (static::loadMultiple() as $pageContextForm) {
if ($pageContextForm->status()) {
$plugin = $pageContextForm->getDisplayPlugin($pluginId);
$plugin?->alterContextForm($pageContextForm, $context, $form, $form_state, $parents);
}
}
}
/**
* {@inheritdoc}
*/
public static function saveRecord(array $form, FormStateInterface $formState): void {
$logger = \Drupal::logger('a12s_page_context');
foreach ($formState->getValue('a12s_page_context', []) as $values) {
if (empty($values['plugin_id'])) {
$logger->warning('Error while saving page context form: the plugin ID is missing.');
continue;
}
if (empty($values['config_id'])) {
$logger->warning('Error while saving page context form for plugin %plugin_id: the configuration ID is missing.', ['%plugin_id' => $values['plugin_id']]);
continue;
}
if (!isset($values['key'])) {
$logger->warning('Error while saving page context form for plugin %plugin_id: the key is missing.', ['%plugin_id' => $values['plugin_id']]);
continue;
}
$pageContextForm = self::load($values['config_id']);
if (!($pageContextForm instanceof PageContextFormInterface)) {
$logger->warning('Error while saving page context form for plugin %plugin_id: the configuration ID is invalid.', ['%plugin_id' => $values['plugin_id']]);
continue;
}
// Ensure the plugin_id is expected.
if (!$pageContextForm->getDisplayPlugin($values['plugin_id'])) {
$logger->warning('Error while saving page context form for plugin %plugin_id: the given plugin is not enabled or not valid.', ['%plugin_id' => $values['plugin_id']]);
continue;
}
$connection = \Drupal::database();
$values += [
'settings' => [],
];
if (empty($values['data'])) {
// Delete data if we have an existing record, otherwise do nothing.
if (!empty($values['id'])) {
$connection->delete('a12s_page_context_record')
->condition('id', $values['id'])
->execute();
}
}
else {
if (!empty($values['id'])) {
$status = $connection->update('a12s_page_context_record')
->fields([
'data' => serialize($values['data']),
'config_id' => $values['config_id'],
'plugin_id' => $values['plugin_id'],
'key' => $values['key'],
'settings' => serialize($values['settings']),
])
->condition('id', $values['id'])
->execute();
}
else {
$status = $connection->merge('a12s_page_context_record')
->fields([
'data' => serialize($values['data']),
'settings' => serialize($values['settings']),
])
->keys([
'config_id' => $values['config_id'],
'plugin_id' => $values['plugin_id'],
'key' => $values['key'],
])
->execute();
}
if (!$status) {
$logger->error('Could not save the page context form for plugin %plugin_id, with config ID %config_id and key %key.', [
'%plugin_id' => $values['plugin_id'],
'%config_id' => $values['config_id'],
'%key' => $values['key'] ?: '""',
]);
}
}
}
}
}
