Skip to content
Snippets Groups Projects
Commit 9667a87f authored by Oleksandr Akerman's avatar Oleksandr Akerman
Browse files

Resolve #3265850 "Fallback"

parent 5837f6f8
No related branches found
No related tags found
1 merge request!10Resolve #3265850 "Fallback"
field.formatter.settings.editablefields_formatter:
type: mapping
label: 'Editabl Fields formatter form mode'
label: 'Editable Fields formatter'
mapping:
form_mode:
type: string
label: 'Form mode'
bypass_access:
type: bool
label: 'Bypass access'
fallback_access:
type: bool
label: 'Fallback access'
display_mode_access:
type: string
label: 'Display mode access'
fallback_edit:
type: bool
label: 'Fallback edit'
display_mode_edit:
type: string
label: 'Display mode edit'
......@@ -3,6 +3,7 @@
namespace Drupal\editablefields\Form;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
......@@ -34,6 +35,13 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
*/
protected $form_mode;
/**
* Formatter settings.
*
* @var array $settings
*/
protected $settings;
/**
* Drupal\editablefields\services\EditableFieldsHelper definition.
*
......@@ -77,11 +85,18 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$wrapper = str_replace('_', '-', $this->getFormId()) . '-wrapper';
$fallback = $this->settings['fallback_edit'];
$form['#prefix'] = "<div id=\"$wrapper\">";
$form['#suffix'] = '</div>';
$operation = $form_state->get('operation');
$field = $this->field_name;
$form_display = $this->getFormDisplay();
$is_admin = $this->editablefieldsHelper->isAdmin();
if (empty($form_display) || !$form_display->id()) {
if ($form_display === NULL || !$form_display->id()) {
if ($is_admin) {
return [
'#markup' => $this->t('Form mode @mode missing', [
......@@ -92,6 +107,31 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
return [];
}
// If fallback formatter selected.
if ($fallback && (!$operation || $operation === 'cancel')) {
/** @var FieldItemListInterface $item */
$item = $this->entity->get($field);
$form['formatter'] = $item->view($this->settings['display_mode_edit']);
if (empty($form['formatter'])) {
$form['formatter'] = [
'#markup' => $this->t('N/A'),
];
}
$form['formatter']['#weight'] = 0;
$form['edit'] = [
'#type' => 'submit',
'#op' => 'edit',
'#value' => $this->t('Edit'),
'#weight' => 10,
'#ajax' => [
'callback' => [$this, 'ajaxCallback'],
'wrapper' => $wrapper,
],
];
return $form;
}
// Get the field widget from the form mode.
$component = $form_display->getComponent($field);
if (!$component) {
......@@ -124,13 +164,33 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
$form['submit'] = [
'#type' => 'submit',
'#op' => 'save',
'#value' => $this->t('Update'),
'#ajax' => [
'callback' => [$this, 'ajaxCallback'],
'wrapper' => str_replace('_', '-', $this->getFormId()),
'wrapper' => $wrapper,
],
];
if ($operation === 'save' && !$form_state->getErrors()) {
$form['confirm_message'] = [
'#markup' => $this->t('Updated'),
];
}
if ($fallback && $operation && $operation !== 'cancel') {
$form['cancel'] = [
'#type' => 'submit',
'#op' => 'cancel',
'#value' => $this->t('Cancel'),
'#weight' => 20,
'#ajax' => [
'callback' => [$this, 'ajaxCallback'],
'wrapper' => $wrapper,
],
];
}
return $form;
}
......@@ -138,7 +198,15 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->setRebuild(TRUE);
$form_state->setRebuild();
$trigger = $form_state->getTriggeringElement();
if (!empty($trigger['#op'])) {
$form_state->set('operation', $trigger['#op']);
// No further processing for edit and cancel.
if ($trigger['#op'] !== 'save') {
return;
}
}
// Make sure we load fresh entity to prevent data loss:
// https://www.drupal.org/project/editablefields/issues/3292392
......@@ -146,6 +214,10 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
->getStorage($this->entity->getEntityTypeId())
->load($this->entity->id());
if (!$entity) {
return;
}
$field = $this->field_name;
$form_display = $this->getFormDisplay();
......@@ -154,8 +226,12 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
}
// Update the entity.
if ($component = $form_display->getComponent($field)) {
if ($form_display->getComponent($field)) {
$widget = $form_display->getRenderer($field);
if (!$widget) {
return;
}
$items = $entity->get($field);
$items->filterEmptyItems();
$widget->extractFormValues($items, $form, $form_state);
......@@ -175,11 +251,6 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
* Updated form.
*/
public function ajaxCallback(array &$form, FormStateInterface $form_state) {
if (!$form_state->getErrors()) {
$form['confirm_message'] = [
'#markup' => $this->t('Updated'),
];
}
return $form;
}
......@@ -203,13 +274,16 @@ class EditableFieldsForm extends FormBase implements BaseFormIdInterface {
* Edited entity.
* @param $field_name
* Field name.
* @param $form_mode
* @param $settings
* Form mode.
*/
public function setDefaults(EntityInterface $entity, $field_name, $form_mode = 'default') {
public function setDefaults(EntityInterface $entity, $field_name, array $settings) {
$this->entity = $entity;
$this->field_name = $field_name;
$this->form_mode = $form_mode;
$this->form_mode = !empty($settings['form_mode'])
? $settings['form_mode']
: $this->editablefieldsHelper::DEFAULT_MODE;
$this->settings = $settings;
}
/**
......
......@@ -67,6 +67,11 @@ class EditableFieldsFieldFormatter extends FormatterBase {
public static function defaultSettings() {
return [
'form_mode' => 'default',
'bypass_access' => FALSE,
'fallback_access' => FALSE,
'display_mode_access' => '',
'fallback_edit' => FALSE,
'display_mode_edit' => '',
] + parent::defaultSettings();
}
......@@ -74,14 +79,79 @@ class EditableFieldsFieldFormatter extends FormatterBase {
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$entity_type_id = $this->fieldDefinition->getTargetEntityTypeId();
return [
'form_mode' => [
'#type' => 'select',
'#title' => $this->t('Form mode'),
'#title' => $this->t('Select form mode:'),
'#default_value' => $this->getSetting('form_mode'),
'#options' => $this->editablefieldsHelper->getModesOptions(),
'#required' => 'required',
'#options' => $this->editablefieldsHelper
->getFormModesOptions($entity_type_id),
'#description' => $this
->t('The widget of the selected form mode will be used.'),
->t('The widget for this field in the selected form mode will be used.'),
],
'bypass_access' => [
'#type' => 'checkbox',
'#title' => $this->t('Bypass access check'),
'#default_value' => $this->getSetting('bypass_access'),
'#description' => $this
->t('Allows to bypass check if the user has access to update entity.'),
],
'fallback_access' => [
'#type' => 'checkbox',
'#title' => $this->t('No access formatter'),
'#default_value' => $this->getSetting('fallback_access'),
'#description' => $this
->t('Allows to select fallback formatter in case when user has no access to update entity.'),
'#states' => [
'visible' => [
':input[name="options[settings][bypass_access]"]' => ['checked' => FALSE],
],
],
],
'display_mode_access' => [
'#type' => 'select',
'#title' => $this->t('Select no access display mode:'),
'#default_value' => $this->getSetting('display_mode_access'),
'#options' => $this->editablefieldsHelper
->getViewModesOptions($entity_type_id),
'#description' => $this
->t('Use this formatter if user has no access to update entity.'),
'#states' => [
'visible' => [
':input[name="options[settings][fallback_access]"]' => ['checked' => TRUE],
':input[name="options[settings][bypass_access]"]' => ['checked' => FALSE],
],
'required' => [
':input[name="options[settings][fallback_access]"]' => ['checked' => TRUE],
':input[name="options[settings][bypass_access]"]' => ['checked' => FALSE],
],
],
],
'fallback_edit' => [
'#type' => 'checkbox',
'#title' => $this->t('Use fallback formatter'),
'#default_value' => $this->getSetting('fallback_edit'),
'#description' => $this
->t('The widget for this field in the selected form mode will be used.'),
],
'display_mode_edit' => [
'#type' => 'select',
'#title' => $this->t('Select fallback display mode:'),
'#default_value' => $this->getSetting('display_mode_edit'),
'#options' => $this->editablefieldsHelper
->getViewModesOptions($entity_type_id),
'#description' => $this
->t('Use this formatter before user clicks "edit" to get the widget.'),
'#states' => [
'visible' => [
':input[name="options[settings][fallback_edit]"]' => ['checked' => TRUE],
],
'required' => [
':input[name="options[settings][fallback_edit]"]' => ['checked' => TRUE],
],
],
],
] + parent::settingsForm($form, $form_state);
}
......@@ -103,8 +173,17 @@ class EditableFieldsFieldFormatter extends FormatterBase {
public function viewElements(FieldItemListInterface $items, $langcode) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $items->getEntity();
$has_access = $this->editablefieldsHelper->checkAccess($entity);
// No bypass access setting and user has no access.
if (!$has_access && !$this->getSetting('bypass_access')) {
if ($this->getSetting('fallback_access')) {
// If we have fallback option for the no-access case - render field.
return $entity->get($this->fieldDefinition->getName())
->view($this->getSetting('display_mode_access'));
}
if (!$this->editablefieldsHelper->checkAccess($entity)) {
// No access & no fallback - no data.
return [];
}
......@@ -112,9 +191,35 @@ class EditableFieldsFieldFormatter extends FormatterBase {
$this->editablefieldsHelper->getForm(
$entity,
$this->fieldDefinition->getName(),
$this->getSetting('form_mode')
$this->getSettings()
),
];
}
/**
* {@inheritdoc}
*/
public function setSettings(array $settings) {
$default = EditableFieldsHelper::DEFAULT_MODE;
$this->settings = [
'form_mode' => $settings['form_mode'] ?? $default,
'bypass_access' => !empty($settings['bypass_access']),
'fallback_access' => !empty($settings['fallback_access'])
&& !empty($settings['display_mode_access']),
'display_mode_access' => !empty($settings['display_mode_access'])
? $settings['display_mode_access']
: $default,
'fallback_edit' => !empty($settings['fallback_edit'])
&& !empty($settings['display_mode_edit']),
'display_mode_edit' => !empty($settings['display_mode_edit'])
? $settings['display_mode_edit']
: $default,
];
$this->defaultSettingsMerged = FALSE;
return parent::setSettings($settings);
}
}
......@@ -3,11 +3,13 @@
namespace Drupal\editablefields\services;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Class EditableFieldsHelper.
......@@ -15,31 +17,32 @@ use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
* Helper service for the "editablefields" functionality.
*/
class EditableFieldsHelper {
use StringTranslationTrait;
/**
* Editablefields formatter ID.
*/
const FORMATTER_ID = 'editablefields_formatter';
public const FORMATTER_ID = 'editablefields_formatter';
/**
* Use "editablefields" permission.
*/
const PERMISSION = 'use editablefields';
public const PERMISSION = 'use editablefields';
/**
* Editablefields admin permission.
*/
const ADMIN_PERMISSION = 'administer editablefields';
public const ADMIN_PERMISSION = 'administer editablefields';
/**
* Editablefields form class.
*/
const FORM_CLASS = 'Drupal\editablefields\Form\EditableFieldsForm';
public const FORM_CLASS = 'Drupal\editablefields\Form\EditableFieldsForm';
/**
* Default form mode.
*/
const DEFAULT_FORM_MODE = 'default';
public const DEFAULT_MODE = 'default';
/**
* Drupal\Core\Field\FieldTypePluginManagerInterface definition.
......@@ -144,18 +147,18 @@ class EditableFieldsHelper {
* Entity object.
* @param string $field_name
* Field name.
* @param string $form_mode
* Form mode.
* @param array $settings
* Settings of the editablefields formatter.
*
* @return array
* Form render array.
*/
public function getForm(EntityInterface $entity, string $field_name, string $form_mode) {
public function getForm(EntityInterface $entity, string $field_name, array $settings) {
/** @var \Drupal\editablefields\Form\EditableFieldsForm $form_object */
$form_object = $this->classResolver->getInstanceFromDefinition(
self::FORM_CLASS
);
$form_object->setDefaults($entity, $field_name, $form_mode);
$form_object->setDefaults($entity, $field_name, $settings);
return $this->formBuilder->getForm($form_object);
}
......@@ -180,23 +183,28 @@ class EditableFieldsHelper {
/**
* Helper method to prepare the list of the form modes.
*
* @var string $entity_type_id
* Entity type ID.
*
* @return array
* Array of form modes.
*/
public function getModesOptions() {
$options[self::DEFAULT_FORM_MODE] = self::DEFAULT_FORM_MODE;
$form_modes = $this->entityDisplayRepository->getAllFormModes();
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface[] $modes */
foreach ($form_modes as $entity_type => $modes) {
foreach ($modes as $mode) {
$label = explode('.', $mode['id']);
$label = end($label);
$options[$entity_type][$label] = $label;
}
}
public function getFormModesOptions(string $entity_type_id) {
return $this->entityDisplayRepository->getFormModeOptions($entity_type_id);
}
return $options;
/**
* Helper method to prepare the list of the view modes.
*
* @var string $entity_type_id
* Entity type ID.
*
* @return array
* Array of view modes.
*/
public function getViewModesOptions(string $entity_type_id) {
return $this->entityDisplayRepository->getViewModeOptions($entity_type_id);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment