GetDKAN/dkan

View on GitHub
modules/json_form_widget/src/WidgetRouter.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

namespace Drupal\json_form_widget;

use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\metastore\MetastoreService;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * JSON form widget router service.
 */
class WidgetRouter implements ContainerInjectionInterface {

  /**
   * Uuid Service.
   *
   * @var \Drupal\Component\Uuid\UuidInterface
   */
  protected $uuidService;

  /**
   * StringHelper Service.
   *
   * @var \Drupal\json_form_widget\StringHelper
   */
  protected $stringHelper;

  /**
   * Metastore Service.
   *
   * @var \Drupal\metastore\MetastoreService
   */
  protected $metastore;

  /**
   * Inherited.
   *
   * @{inheritdocs}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('uuid'),
      $container->get('json_form.string_helper'),
      $container->get('dkan.metastore.service')
    );
  }

  /**
   * Constructor.
   *
   * @param \Drupal\Component\Uuid\UuidInterface $uuid
   *   Uuid service.
   * @param \Drupal\json_form_widget\StringHelper $string_helper
   *   String Helper service.
   * @param \Drupal\metastore\MetastoreService $metastore
   *   Metastore service.
   */
  public function __construct(UuidInterface $uuid, StringHelper $string_helper, MetastoreService $metastore) {
    $this->uuidService = $uuid;
    $this->stringHelper = $string_helper;
    $this->metastore = $metastore;
  }

  /**
   * Helper function for getting element with configured widget.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to apply UI options.
   *
   * @return array
   *   Element with widget configuration based on UI options.
   */
  public function getConfiguredWidget($spec, array $element) {
    $widgets = $this->getWidgets();
    if (in_array($spec->widget, array_keys($widgets))) {
      $method_name = $widgets[$spec->widget];
      $element = $this->$method_name($spec, $element);
    }
    return $element;
  }

  /**
   * Get list of widgets available and functions to handle each widget.
   *
   * @return array
   *   Associative array of widgets vrs functions to handle the elements.
   */
  public function getWidgets() {
    return [
      'hidden' => 'handleHiddenElement',
      'textarea' => 'handleTextareaElement',
      'dkan_uuid' => 'handleDkanUuidElement',
      'upload_or_link' => 'handleUploadOrLinkElement',
      'list' => 'handleListElement',
      'date' => 'handleDateElement',
      'flexible_datetime' => 'handleDatetimeElement',
      'date_range' => 'handleDateRangeElement',
    ];
  }

  /**
   * Handle configuration for list elements.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into list element.
   *
   * @return array
   *   The element configured as a list element.
   */
  public function handleListElement($spec, array $element) {
    $title_property = ($spec->titleProperty ?? FALSE);

    if (isset($title_property, $element[$title_property])) {
      $element[$title_property] = $this->getDropdownElement($element[$title_property], $spec, $title_property);
    }

    if (isset($spec->source->returnValue)) {
      $element = $this->getDropdownElement($element, $spec, $title_property);
    }
    elseif (!isset($spec->titleProperty)) {
      $element = $this->getDropdownElement($element, $spec);
    }

    return $element;
  }

  /**
   * Helper function to build a dropdown element.
   *
   * @param mixed $element
   *   Element to apply UI options.
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param mixed $titleProperty
   *   The title property name in which the dropdown should be added (or FALSE).
   *
   * @return array
   *   The dropdown element configured.
   */
  public function getDropdownElement($element, $spec, $titleProperty = FALSE) {
    $element['#type'] = $this->getSelectType($spec);
    $element['#options'] = $this->getDropdownOptions($spec->source, $titleProperty);
    if ($element['#type'] === 'select_or_other_select') {
      $element = $this->handleSelectOtherDefaultValue($element, $element['#options']);
      $element['#input_type'] = $spec->other_type ?? 'textfield';
    }
    $element['#other_option'] = isset($element['#other_option']) ?? FALSE;

    if ($element['#type'] === 'select2') {
      $element['#multiple'] = isset($spec->multiple) ? TRUE : FALSE;
      $element['#autocreate'] = isset($spec->allowCreate) ? TRUE : FALSE;
    }
    if (isset($element['#autocreate']) && $spec->type !== 'select2') {
      $element['#target_type'] = 'node';
    }
    return $element;
  }

  /**
   * Helper function to get type of pick list.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   *
   * @return string
   *   The type of dropdown element to use.
   */
  public function getSelectType($spec) {
    if (isset($spec->type) && $spec->type === 'select_other') {
      return 'select_or_other_select';
    }
    elseif (isset($spec->type) && ($spec->type === 'autocomplete' || $spec->type === 'select2')) {
      return 'select2';
    }
    return 'select';
  }

  /**
   * Helper function to get options for dropdowns.
   *
   * @param mixed $source
   *   Source object from UI options.
   * @param mixed $titleProperty
   *   The title property name in which the dropdown should be added (or FALSE).
   *
   * @return array
   *   Array with options for the dropdown.
   */
  public function getDropdownOptions($source, $titleProperty = FALSE) {
    $options = [];
    if (isset($source->enum)) {
      $options = $this->stringHelper->getSelectOptions($source);
    }
    if (isset($source->metastoreSchema)) {
      $options = $this->getOptionsFromMetastore($source, $titleProperty);
    }
    return $options;
  }

  /**
   * Helper function to get options from metastore.
   *
   * @param mixed $source
   *   Source object from UI options.
   * @param mixed $titleProperty
   *   The title property name in which the dropdown should be added (or FALSE).
   *
   * @return array
   *   Array with options from metastore for the dropdown.
   */
  public function getOptionsFromMetastore($source, $titleProperty = FALSE) {
    $options = [];
    $metastore_items = $this->metastore->getAll($source->metastoreSchema);
    foreach ($metastore_items as $item) {
      $item = json_decode($item);
      $title = $this->metastoreOptionTitle($item, $source, $titleProperty);
      $value = $this->metastoreOptionValue($item, $source, $titleProperty);
      $options[$value] = $title;
    }
    return $options;
  }

  /**
   * Determine the title for the select option.
   *
   * @param object|string $item
   *   Single item from Metastore::getAll()
   * @param object $source
   *   Source defintion from UI schema.
   * @param string|false $titleProperty
   *   Title property defined in UI schema.
   *
   * @return string
   *   String to be used in title.
   */
  private function metastoreOptionTitle($item, object $source, $titleProperty): string {
    if ($titleProperty) {
      return is_object($item) ? $item->data->$titleProperty : $item;
    }
    return $item->data;
  }

  /**
   * Determine the value for the select option.
   *
   * @param object|string $item
   *   Single item from Metastore::getAll()
   * @param object $source
   *   Source defintion from UI schema.
   * @param string|false $titleProperty
   *   Title property defined in UI schema.
   *
   * @return string
   *   String to be used as option value.
   */
  private function metastoreOptionValue($item, object $source, $titleProperty): string {
    if (($source->returnValue ?? NULL) == 'url') {
      return 'dkan://metastore/schemas/' . $source->metastoreSchema . '/items/' . $item->identifier;
    }
    if ($titleProperty) {
      return is_object($item) ? $item->data->$titleProperty : $item;
    }
    return $item->data;
  }

  /**
   * Helper function to add the value of other to current list of options.
   */
  private function handleSelectOtherDefaultValue($element, $options) {
    if (!empty($element['#default_value'])) {
      if (!array_key_exists($element['#default_value'], $options)) {
        $element['#options'][$element['#default_value']] = $element['#default_value'];
      }
    }
    return $element;
  }

  /**
   * Handle configuration for upload_or_link elements.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into upload_or_link.
   *
   * @return array
   *   The element configured as upload_or_link.
   */
  public function handleUploadOrLinkElement($spec, array $element) {
    $element['#type'] = 'upload_or_link';
    $element['#upload_location'] = 'public://uploaded_resources';
    if (isset($element['#default_value'])) {
      $element['#uri'] = $element['#default_value'];
      unset($element['#default_value']);
    }
    if (isset($spec->extensions)) {
      $element['#upload_validators']['file_validate_extensions'][] = $spec->extensions;
    }
    return $element;
  }

  /**
   * Helper function for getting a textarea element.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into textarea.
   *
   * @return array
   *   The element configured as textarea.
   */
  public function handleTextareaElement($spec, array $element) {
    $element['#type'] = 'textarea';
    if (isset($spec->rows)) {
      $element['#rows'] = $spec->rows;
    }
    if (isset($spec->cols)) {
      $element['#cols'] = $spec->cols;
    }
    return $element;
  }

  /**
   * Helper function for hiding an element.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into hidden.
   *
   * @return array
   *   The element configured as hidden.
   */
  public function handleHiddenElement($spec, array $element) {
    $element['#access'] = FALSE;
    return $element;
  }

  /**
   * Helper function for getting a dkan_uuid element.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into dkan_uuid.
   *
   * @return array
   *   The element configured as dkan_uuid.
   */
  public function handleDkanUuidElement($spec, array $element) {
    $element['#default_value'] = !empty($element['#default_value']) ? $element['#default_value'] : $this->uuidService->generate();
    $element['#access'] = FALSE;
    return $element;
  }

  /**
   * Helper function for getting a date element.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into date.
   *
   * @return array
   *   The element configured as date.
   */
  public function handleDateElement($spec, array $element) {
    $element['#type'] = 'date';
    $format = $spec->format ?? 'Y-m-d';
    if (isset($element['#default_value'])) {
      $date = new DrupalDateTime($element['#default_value']);
      $element['#default_value'] = $date->format($format);
    }
    $element['#date_date_format'] = $format;
    return $element;
  }

  /**
   * Helper function for getting a datetime element.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into datetime.
   *
   * @return array
   *   The element configured as datetime.
   */
  public function handleDatetimeElement($spec, array $element) {
    $element['#type'] = 'flexible_datetime';
    if (isset($element['#default_value'])) {
      $date = new DrupalDateTime($element['#default_value']);
      $element['#default_value'] = $date;
    }
    if (isset($spec->timeRequired) && is_bool($spec->timeRequired)) {
      $element['#date_time_required'] = $spec->timeRequired;
    }
    return $element;
  }

  /**
   * Helper function for getting a date_range element.
   *
   * @param mixed $spec
   *   Object with spec for UI options.
   * @param array $element
   *   Element to convert into date_range.
   *
   * @return array
   *   The element configured as date_range.
   */
  public function handleDateRangeElement($spec, array $element) {
    $element['#type'] = 'date_range';
    return $element;
  }

}