diff --git a/src/Plugin/Block/EntityField.php b/src/Plugin/Block/EntityField.php index 60007ce..346142a 100644 --- a/src/Plugin/Block/EntityField.php +++ b/src/Plugin/Block/EntityField.php @@ -7,6 +7,7 @@ namespace Drupal\ctools\Plugin\Block; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Block\BlockBase; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Entity\EntityManagerInterface; @@ -73,11 +74,18 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont /** * The field definition. * - * @var \Drupal\Core\Field\FieldStorageDefinitionInterface + * @var \Drupal\Core\Field\FieldDefinitionInterface */ protected $fieldDefinition; /** + * The field definition. + * + * @var \Drupal\Core\Field\FieldStorageDefinitionInterface + */ + protected $fieldStorageDefinition; + + /** * Constructs a new EntityField. * * @param array $configuration @@ -121,15 +129,30 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont * * @return \Drupal\Core\Field\FieldStorageDefinitionInterface */ - protected function getFieldStorageDefinition() { + protected function getFieldDefinition() { if (empty($this->fieldDefinition)) { - $field_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityTypeId); + $field_map = $this->entityManager->getFieldMap(); + $bundle = reset($field_map[$this->entityTypeId][$this->fieldName]['bundles']); + $field_definitions = $this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle); $this->fieldDefinition = $field_definitions[$this->fieldName]; } return $this->fieldDefinition; } /** + * Get the field storage definition. + * + * @return \Drupal\Core\Field\FieldStorageDefinitionInterface + */ + protected function getFieldStorageDefinition() { + if (empty($this->fieldStorageDefinition)) { + $field_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityTypeId); + $this->fieldStorageDefinition = $field_definitions[$this->fieldName]; + } + return $this->fieldStorageDefinition; + } + + /** * Get field type. * * @return array @@ -150,6 +173,32 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont } /** + * Get the formatter object. + * + * @param string $type + * The formatter name. + * @param string $label + * The label option for the formatter. + * @param array $settings + * The formatter settings. + * @param array $third_party_settings + * The formatter third party settings. + * + * @return \Drupal\Core\Field\FormatterInterface + * The formatter object. + */ + protected function getFormatter($type, $label, array $settings, array $third_party_settings) { + return $this->formatterManager->createInstance($type, [ + 'field_definition' => $this->getFieldDefinition(), + 'view_mode' => 'default', + 'prepare' => TRUE, + 'label' => $label, + 'settings' => $settings, + 'third_party_settings' => $third_party_settings, + ]); + } + + /** * {@inheritdoc} */ public function defaultConfiguration() { @@ -159,6 +208,7 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont 'label' => 'above', 'type' => $field_type_definition['default_formatter'] ?: '', 'settings' => [], + 'third_party_settings' => [], 'weight' => 0.0, ], ]; @@ -187,20 +237,74 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont '#title' => $this->t('Formatter'), '#options' => $this->getFormatterOptions(), '#default_value' => $form_state->getValue('formatter_type') ?: $config['formatter']['type'], + '#ajax' => array( + 'callback' => array(get_class($this), 'formatterSettingsAjaxCallback'), + 'wrapper' => 'formatter-settings-wrapper', + 'effect' => 'fade', + ), ]; - // @todo: use AJAX to provide the settings form. + // Add the formatter settings to the form via AJAX. + $form['#process'][] = array($this, 'layoutSettingsProcessCallback'); + $form['formatter_settings_wrapper'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + $form['formatter_settings_wrapper']['formatter_settings'] = array( + '#tree' => TRUE, + // The settings from the formatter plugin will be added in the + // ::formatterSettingsProcessCallback method. + ); return $form; } /** + * Render API callback: builds the layout settings elements. + */ + public function layoutSettingsProcessCallback(array &$element, FormStateInterface $form_state, array &$complete_form) { + $config = $this->getConfiguration(); + $parents_base = $element['#parents']; + $formatter_parent = array_merge($parents_base, ['formatter_type']); + $formatter_settings_parent = array_merge($parents_base, ['formatter_settings']); + + $settings_element =& $element['formatter_settings_wrapper']['formatter_settings']; + + // Set the #parents on the formatter_settings so they end up as a peer to + // formatter_type. + $settings_element['#parents'] = $formatter_settings_parent; + + // Get the formatter name in a way that works regardless of whether we're + // getting the value via AJAX or not. + $formatter_name = NestedArray::getValue($form_state->getUserInput(), $formatter_parent) ?: $element['formatter_type']['#default_value']; + + // Place the formatter settings on the form if a formatter is selected. + $formatter = $this->getFormatter($formatter_name, $form_state->getValue('formatter_label'), $form_state->getValue($formatter_settings_parent, $config['formatter']['settings']), $config['formatter']['third_party_settings']); + $settings_element = array_merge($formatter->settingsForm($settings_element, $form_state), $settings_element); + + // Store the array parents for our element so that we can use it to pull out + // the formatter settings in our AJAX callback. + $complete_form['#formatter_array_parents'] = $element['#array_parents']; + + return $element; + } + + /** + * Render API callback: gets the layout settings elements. + */ + public static function formatterSettingsAjaxCallback(array $form, FormStateInterface $form_state) { + $formatter_array_parents = $form['#formatter_array_parents']; + return NestedArray::getValue($form, array_merge($formatter_array_parents, ['formatter_settings_wrapper'])); + } + + /** * {@inheritdoc} */ public function blockSubmit($form, FormStateInterface $form_state) { $config = $this->getConfiguration(); $config['formatter']['label'] = $form_state->getValue('formatter_label'); $config['formatter']['type'] = $form_state->getValue('formatter_type'); + $config['formatter']['settings'] = $form_state->getValue('formatter_settings'); $this->setConfiguration($config); } diff --git a/src/Plugin/Deriver/EntityFieldDeriver.php b/src/Plugin/Deriver/EntityFieldDeriver.php index 675edea..7feac5d 100644 --- a/src/Plugin/Deriver/EntityFieldDeriver.php +++ b/src/Plugin/Deriver/EntityFieldDeriver.php @@ -77,7 +77,7 @@ class EntityFieldDeriver extends DeriverBase implements ContainerDeriverInterfac $derivative['category'] = $this->t('@entity fields', array('@entity' => $entity_type_labels[$entity_type_id])); $derivative['admin_label'] = $admin_label; $derivative['context'] = [ - 'entity' => new ContextDefinition('entity:' . $entity_type_id), + 'entity' => new ContextDefinition('entity:' . $entity_type_id, $entity_type_labels[$entity_type_id], TRUE), ]; $this->derivatives[$derivative_id] = $derivative;