visualn-8.x-1.x-dev/modules/visualn_file_field/src/Plugin/Field/FieldFormatter/VisualNImageFormatter.php
modules/visualn_file_field/src/Plugin/Field/FieldFormatter/VisualNImageFormatter.php
<?php
namespace Drupal\visualn_file_field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\Html;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\NestedArray;
use Symfony\Component\HttpFoundation\Request;
use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatter;
use Drupal\Core\Render\Element;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\visualn\Helpers\VisualNFormsHelper;
use Drupal\visualn\Helpers\VisualN;
/**
* Plugin implementation of the 'visualn_image' formatter.
*
* @FieldFormatter(
* id = "visualn_image",
* label = @Translation("VisualN image"),
* field_types = {
* "image"
* },
* quickedit = {
* "editor" = "image"
* }
* )
*/
class VisualNImageFormatter extends ImageFormatter {
//class VisualNImageFormatter extends FormatterBase {
const RAW_RESOURCE_FORMAT = 'visualn_generic_data_array';
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return [
'visualn_style_id' => '',
'drawer_config' => [],
'drawer_fields' => [],
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
$visualn_styles = visualn_style_options(FALSE);
$description_link = Link::fromTextAndUrl(
$this->t('Configure VisualN Styles'),
Url::fromRoute('entity.visualn_style.collection')
);
$visualn_style_id = $this->getSetting('visualn_style_id');
// @todo: choose a more explicit formatter
$ajax_wrapper_id = 'visualn-image-formatter-drawer-config-form-ajax-wrapper';
$form['visualn_style_id'] = [
'#type' => 'select',
'#title' => t('VisualN style'),
'#options' => $visualn_styles,
'#default_value' => $visualn_style_id,
'#description' => t('Default style for the data to render.'),
// @todo: add permission check for current user
'#description' => $description_link->toRenderable() + [
//'#access' => $this->currentUser->hasPermission('administer visualn styles')
'#access' => TRUE
],
'#ajax' => [
'callback' => [get_called_class(), 'ajaxCallback'],
'wrapper' => $ajax_wrapper_id,
],
'#required' => TRUE,
'#empty_value' => '',
'#empty_option' => t('- Select visualization style -'),
];
$form['drawer_container'] = [
'#prefix' => '<div id="' . $ajax_wrapper_id . '">',
'#suffix' => '</div>',
'#type' => 'container',
//'#process' => [[get_called_class(), 'processDrawerContainerSubform']],
'#process' => [[$this, 'processDrawerContainerSubform']],
];
//$form['drawer_container']['#stored_configuration'] = $this->getSetting('drawer_config');
// @todo: basically just this->getSettings() can be passed
$form['drawer_container']['#stored_configuration'] = [
'visualn_style_id' => $this->getSetting('visualn_style_id'),
'drawer_config' => $this->getSetting('drawer_config'),
'drawer_fields' => $this->getSetting('drawer_fields'),
];
return $form;
//return $form + parent::settingsForm($form, $form_state);
}
/**
* Return drawer configuration form via ajax request at style change
*/
public static function ajaxCallback(array $form, FormStateInterface $form_state, Request $request) {
$triggering_element = $form_state->getTriggeringElement();
$visualn_style_id = $form_state->getValue($form_state->getTriggeringElement()['#parents']);
$triggering_element_parents = array_slice($triggering_element['#array_parents'], 0, -1);
$element = NestedArray::getValue($form, $triggering_element_parents);
return $element['drawer_container'];
}
// @todo: this should be static since may not work on field settings form (see fetcher field widget for example)
//public static function processDrawerContainerSubform(array $element, FormStateInterface $form_state, $form) {
public function processDrawerContainerSubform(array $element, FormStateInterface $form_state, $form) {
$stored_configuration = $element['#stored_configuration'];
$configuration = [
'visualn_style_id' => $stored_configuration['visualn_style_id'],
'drawer_config' => $stored_configuration['drawer_config'],
'drawer_fields' => $stored_configuration['drawer_fields'],
];
$element = VisualNFormsHelper::processDrawerContainerSubform($element, $form_state, $form, $configuration);
// Add a process callback to convert drawer_fields mapping textfields into select
// lists since the number and names of keys used by image field to provide data are
// known and unchangeable.
// @see VisualNFormsHelper::processDrawerContainerSubform()
// @see ImageFieldReaderDrawingFetcher::processDrawerContainerSubform()
$style_element_parents = array_slice($element['#parents'], 0, -1);
$visualn_style_id = $form_state->getValue(array_merge($style_element_parents, ['visualn_style_id']));
if (!$visualn_style_id) {
return $element;
}
$drawer_container_key = $visualn_style_id;
// $element[$drawer_container_key]['drawer_fields']['#process'] is supposed to be always set
// if $visualn_style_id is defined, see VisualNFormsHelper::processDrawerContainerSubform()
if ($element[$drawer_container_key]['drawer_fields']['#process']) {
$element[$drawer_container_key]['drawer_fields']['#process'][] = [get_called_class(), 'processDrawerFieldsSubform'];
}
return $element;
}
/**
* Replace drawer_fields configuration textfields with select lists.
*/
public static function processDrawerFieldsSubform(array $element, FormStateInterface $form_state, $form) {
$drawer_fields = $element['#drawer_fields'];
// @todo: check for additional data keys, e.g. size values though some of them
// could be considered secure and shouldn't be exposed for every case
// Image field provides data with a fixed set of data keys
$data_keys_options = [
'url' => 'url',
'title' => 'title',
'alt' => 'alt',
];
// replace textfields with selects
foreach (Element::children($element) as $key) {
$element[$key]['field'] = [
'#type' => 'select',
'#options' => $data_keys_options,
'#empty_option' => t('- None -'),
'#default_value' => isset($drawer_fields[$key]) ? $drawer_fields[$key] : '',
];
}
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
return parent::settingsSummary();
$summary = [];
// Implement settings summary.
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = parent::viewElements($items, $langcode);
$visualn_style_id = $this->getSetting('visualn_style_id');
if (empty($visualn_style_id)) {
return $elements;
}
$image_items = $elements;
// wrap elements into a div so that the initial image contents could be hidden by the formatter handler script
$fuid = substr(\Drupal::service('uuid')->generate(), 0, 4);
$image_items_wrapper_id = 'visualn-image-formatter-html-selector--' . $fuid;
$image_items = [
'#prefix' => '<div id="' . $image_items_wrapper_id . '">',
'#suffix' => '</div>',
'#attached' => [
'library' => [
'visualn_file_field/visualn-image-formatter-handler'
],
'drupalSettings' => [
'visualnFile' => ['imageFormatterItemsWrapperId' => [$fuid => $image_items_wrapper_id]],
],
],
] + $image_items;
// keep original image items for fallback bahaviour in case of disabled javascript
$elements = [
'#image_items' => $image_items,
];
// @see ImageFormatter::viewElements()
// @todo: try to get urls list from $elements
//$deltas = Element::children($elements);
$data = [];
$files = $this->getEntitiesToView($items, $langcode);
foreach ($files as $delta => $file) {
$image_uri = $file->getFileUri();
// @todo: see the note in ImageFormatter::viewElements() relating a bug
//$url = Url::fromUri(file_create_url($image_uri));
$url = file_create_url($image_uri);
// @todo: some other properties could be added, e.g. size etc.
// though some of them may be considered secure and shouldn't be added in every
// case (e.g. for js data it would be always exposed) and thus should be configured
$data[] = [
'url' => $url,
'title' => $items->get($delta)->get('title')->getString(),
'alt' => $items->get($delta)->get('alt')->getString(),
];
}
$drawer_config = $this->getSetting('drawer_config');
$drawer_fields = $this->getSetting('drawer_fields');
$raw_resource_plugin_id = static::RAW_RESOURCE_FORMAT;
$raw_input = [
'data' => $data,
];
// @todo: add service in ::create() method
$resource =
\Drupal::service('plugin.manager.visualn.raw_resource_format')
->createInstance($raw_resource_plugin_id, [])
->buildResource($raw_input);
// Get drawing build
$build = \Drupal::service('visualn.builder')->makeBuildByResource($resource, $visualn_style_id, $drawer_config, $drawer_fields);
// @todo: html_selector should be connected inside '.field__items' in order
// to be able to use quick edit feature
// field template seems to ignore anything added to the $elements and renders only items (see field.html.twig)
// @todo: check inline_template solution implemented for other visualn fields formatters
$elements['#theme'] = 'visualn_image_formatter';
$elements = [
'#visualn_drawing_build' => $build,
] + $elements;
return $elements;
}
}
