Problem/Motivation
Site Settings provides 2 blocks, one is a rendered entity, the other is just plain field values, which is great, but limits the flexibility for site builders to control what is displayed and how.
Use case: I have a Site Setting type, Locations. In this type, I have an address field ( rendered as an address ) and a Geolocation field rendered as a Google Map. I have 3 view modes, Default, Address, and Map.
- Default displays the address and map.
- Address just displays the address.
- Map just displays the map.
I want site builders to be able to place a map block in some contexts, and just an address block in other contexts. However, the Site Settings block only ever renders the default view_mode.
https://git.drupalcode.org/project/site_settings/-/blob/8.x-1.x/src/Plug...
$pre_render = $view_builder->view($entity, 'default');
Proposed resolution
Add a setting to the RenderedSiteSettingsBlock that allows for selecting any enabled view_mode for the selected bundle.
My thoughts are, add an AJAX callback on the BlockForm such that when a Site Setting Type is selected, a second select box is displayed with enabled view_modes for that Site Setting Type.
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
// Inject this service.
// ...
public function defaultConfiguration() {
return [
'setting' => NULL,
'label_display' => FALSE,
'view_mode' => NULL,
] + parent::defaultConfiguration();
}
public function blockForm($form, FormStateInterface $form_state) {
// Allow selection of a site settings entity type.
$form['setting'] = [
'#type' => 'entity_autocomplete',
'#target_type' => 'site_setting_entity_type',
'#title' => $this->t('Site setting type'),
'#weight' => '20',
'#required' => TRUE,
// Add ajax callback
'#ajax' => [
'callback' => '::viewModeCallback',
'event' => 'autocompleteclose change',
'wrapper' => 'edit-view-mode',
],
];
if (isset($this->configuration['setting']) && !empty($this->configuration['setting'])) {
$setting_entity_type = $this->entityTypeManager
->getStorage('site_setting_entity_type')
->load($this->configuration['setting']);
$form['setting']['#default_value'] = $setting_entity_type;
// Load the view_mode widget if configuration 'view_mode' is set.
$view_mode_default = NULL;
if (isset($this->configuration['view_mode']) && !empty($this->configuration['view_mode'])) {
$view_mode_default = $this->configuration['view_mode'];
}
array_push($form, $this->viewModeElement($setting_entity_type, $view_mode_default);
}
else {
// Return empty div so we have a target container.
$form['view_mode'] = [
'#markup' => '<div class="view-mode"></div>',
'#allowed_tags' => ['div'],
];
}
return $form;
}
public function getViewModes($entity_type) {
$view_modes = $this->repository->getViewModeOptionsByBundle('site_setting_entity_type', $entity_type);
return $view_modes;
}
public function viewModeCallback(array &$form, FormStateInterface $form_state) {
$entity_type = $form_state->getValue('setting');
$element = $this->viewModeElement($entity_type);
return $element;
}
public function viewModeElement($entity_type = NULL, $default = NULL) {
$view_modes = $this->getViewModes($entity_type);
$options = $this->getViewModes($entity_type);
if (is_array($options) && count($options) > 1) {
$element['view_mode'] = [
'#type' => 'select',
'#title' => $this->t('Select View Mode'),
'#options' => $options,
];
if ($default != NULL) {
$element['view_mode']['#default_value'] = $default;
}
}
else {
$element['view_mode'] = [
'#type' => 'hidden',
'#value' => ($default) ? $default : $options,
];
}
return $element;
}
I haven't tested any of this, but I feel like it's the correct approach.
Remaining tasks
Build / test.
User interface changes
New field to select view mode.
API changes
Data model changes
Add a new setting for view mode.