You are here

class FormManglerService in Token Content Access 2.0.x

Same name and namespace in other branches
  1. 8 src/FormManglerService.php \Drupal\tca\FormManglerService

Class FormManglerService.

@package Drupal\tca

Hierarchy

Expanded class hierarchy of FormManglerService

1 string reference to 'FormManglerService'
tca.services.yml in ./tca.services.yml
tca.services.yml
1 service uses FormManglerService
tca.form_mangler in ./tca.services.yml
Drupal\tca\FormManglerService

File

src/FormManglerService.php, line 20

Namespace

Drupal\tca
View source
class FormManglerService {
  use StringTranslationTrait;

  /**
   * Drupal\Core\Entity\EntityTypeManager definition.
   *
   * @var Drupal\Core\Entity\EntityTypeManager
   */
  private $entityTypeManager = NULL;

  /**
   * Drupal\Core\Entity\EntityTypeBundleInfo definition.
   *
   * @var Drupal\Core\Entity\EntityTypeBundleInfo
   */
  private $bundleInfo = NULL;

  /**
   * Drupal\tca\Plugin\TcaPluginManager definition.
   *
   * @var Drupal\tca\Plugin\TcaPluginManager
   */
  private $tcaPluginManager = NULL;

  /**
   * Constructor.
   */
  public function __construct(EntityTypeManagerInterface $etm, EntityTypeBundleInfo $etbi, TcaPluginManager $tca_plugin_manager, TcaSettingsManager $tca_settings_manager, TranslationInterface $translation) {
    $this->entityTypeManager = $etm;
    $this->allBundleInfo = $etbi
      ->getAllBundleInfo();
    $this->tcaPluginManager = $tca_plugin_manager;
    $this->tcaSettingsManager = $tca_settings_manager;
    $this->stringTranslation = $translation;
  }

  /**
   * Form structure for the TCA configuration.
   *
   * This should be used by other modules that wish to implement the TCA
   * configurations in any form.
   *
   * @param array $attach
   *   The form that the TCA form should be attached to.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity that we're adding the form to, e.g. a node.  This should be
   *    defined even in the case of bundles since it is used to determine bundle
   *    and entity type.
   * @param string|int|object $form_state
   *   The form state.
   * @param string $form_id
   *   The form id.
   */
  public function addTcaSettingsToEntityForm(array &$attach, EntityInterface $entity, FormStateInterface $form_state, $form_id) {
    $this
      ->addTcaSettingsToForm($attach, $entity
      ->getEntityType()
      ->id(), $entity, $form_state, $form_id);
  }

  /**
   * Common functionality for adding TCA options to forms.
   *
   * @param array $attach
   *   The form that the TCA form should be attached to.
   * @param string $entity_type_id
   *   The string ID of the entity type for the form, e.g. 'node'.
   * @param object $entity
   *   The entity that we're adding the form to, e.g. a node.  This should be
   *    defined even in the case of bundles since it is used to determine bundle
   *    and entity type.
   * @param string|int|object $form_state
   *   The form state.
   * @param string $form_id
   *   The form id.
   */
  private function addTcaSettingsToForm(array &$attach, $entity_type_id, $entity, FormStateInterface $form_state, $form_id) {
    $entity_type = $this->entityTypeManager
      ->getStorage($entity_type_id)
      ->getEntityType();
    $is_entity_viewable = $this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->hasViewBuilderClass();

    // TRUE if an entity such as node_type.
    $is_entity_bundle = $this
      ->isEntityBundle($entity);
    $entity_id = $entity
      ->id();
    $tca_bundle_settings = NULL;
    $tca_settings = NULL;
    $active = NULL;
    $token = NULL;

    // TCA for entity bundles such as node_type.
    if ($is_entity_bundle) {

      // Load TCA settings for entity.
      $tca_settings = $this->tcaSettingsManager
        ->loadSettingsAsConfig($entity_type_id, $entity_id);
      $active = $tca_settings
        ->get('active');
      $forced = $tca_settings
        ->get('force') ?? FALSE;
    }
    else {
      $bundle_entity_type_id = $entity_type
        ->getBundleEntityType() ?: $entity_type_id;
      $bundle_entity_id = $entity
        ->getEntityType()
        ->getBundleEntityType() ? $entity
        ->bundle() : NULL;

      // Load TCA settings for entity bundle.
      $tca_bundle_settings = $this->tcaSettingsManager
        ->loadSettingsAsConfig($bundle_entity_type_id, $bundle_entity_id);

      // If the form is about to be attached to an entity,
      // but the bundle isn't allowed to be overridden, exit.
      if (!$tca_bundle_settings
        ->get('active')) {
        return;
      }

      // Load TCA settings for entity.
      $tca_settings = $this->tcaSettingsManager
        ->loadSettingsAsConfig($entity_type_id, $entity_id);
      $active = $tca_settings
        ->get('active');
      $forced = $tca_bundle_settings
        ->get('force') ?? FALSE;
      $token = $tca_settings
        ->get('token');
    }
    $entity_plugin = $this->tcaPluginManager
      ->createInstanceByEntityType($is_entity_bundle && !empty($entity_type
      ->getBundleOf()) ? $entity_type
      ->getBundleOf() : $entity_type_id);
    $form = [];

    // Wrap everything in a fieldset.
    $form['tca'] = [
      '#type' => 'details',
      '#title' => $this
        ->t('Token Content Access settings'),
      '#open' => $active ? TRUE : FALSE,
      // TODO: Should probably handle group in a plugin - not sure if, e.g.,
      // files will work in the same way and even if they do later entities
      // might not.
      '#group' => $is_entity_bundle ? 'additional_settings' : 'advanced',
      '#attributes' => [
        'class' => [
          'tca-settings-form',
        ],
      ],
      '#tree' => FALSE,
    ];
    $form['tca']['tca_is_entity_bundle'] = [
      '#type' => 'value',
      '#value' => $is_entity_bundle,
    ];
    $form['tca']['tca_entity_type_id'] = [
      '#type' => 'value',
      '#value' => $entity_type_id,
    ];
    $form['tca']['tca_entity_id'] = [
      '#type' => 'value',
      '#value' => $entity_id,
    ];
    $form['tca']['tca_active'] = [
      '#type' => 'checkbox',
      '#title' => t('Enable Token Content Access protection'),
      '#default_value' => $forced ?: $active,
      '#description' => t('If this is checked, users with the Administer Token Content Access settings permission will be able to enable Token Content Access protection for individual entities.'),
    ];

    // Override setting if we're not editing a bundle and entity is viewable.
    if (!$is_entity_bundle && $is_entity_viewable) {
      $entity_url = !$entity
        ->isNew() ? $entity
        ->toUrl('canonical', [
        'query' => [
          'tca' => $token,
        ],
        'absolute' => TRUE,
      ])
        ->toString() : t('N/A');
      $form['tca']['tca_active']['#description'] = t('Prevent users from viewing this content without providing an access token via the URL.');
      $form['tca']['tca_active']['#disabled'] = $forced;
      $states = [
        'visible' => [
          ':input[name="tca_active"]' => [
            'checked' => TRUE,
          ],
        ],
      ];

      // Allows content to be viewed without the "View published content"
      // permission.
      $form['tca']['tca_public'] = [
        '#type' => 'checkbox',
        '#title' => t('All users with access token can view this content'),
        '#default_value' => $tca_settings
          ->get('public'),
        '#description' => t('Allow all users to view this content bypassing any permissions restrictions.'),
        '#states' => $states,
      ];
      $form['tca']['tca_token'] = [
        '#type' => 'textfield',
        '#title' => t('Access Token'),
        '#default_value' => $token,
        '#description' => t('Append this access token to the URL as the value for the "tca" query parameter.'),
        '#attributes' => [
          'readonly' => 'readonly',
        ],
        '#states' => $states,
      ];

      // Attach clipboard functionality.
      $module_handler = \Drupal::service('module_handler');
      if ($module_handler
        ->moduleExists('clipboardjs')) {
        $form['tca']['#attached']['library'][] = 'clipboardjs/clipboardjs';
        $form['tca']['tca_clipboardjs'] = [
          '#type' => 'textfield',
          '#theme' => 'clipboardjs',
          '#text' => $entity_url,
          '#alert_text' => NULL,
          '#disabled' => TRUE,
          '#access' => !empty($token),
          '#states' => $states,
        ];
      }
      $form['tca']['tca_url_copy'] = [
        '#type' => 'item',
        '#title' => t('URL to copy'),
        '#markup' => '<span>' . $entity_url . '</span>',
        '#access' => !empty($token),
        '#states' => $states,
      ];
      $form['tca']['actions'] = [
        '#type' => 'actions',
      ];
      $form['tca']['actions']['tca_regenerate'] = [
        '#type' => 'submit',
        '#value' => t('Generate a new Access Token'),
        '#access' => !empty($token),
        '#ajax' => [
          'callback' => [
            'Drupal\\tca\\FormManglerService',
            'staticHandleFormAjax',
          ],
        ],
        '#submit' => [
          [
            'Drupal\\tca\\FormManglerService',
            'staticHandleFormSubmit',
          ],
        ],
        '#states' => $states,
      ];
    }

    // Add "forced" settings to bundle form.
    if ($is_entity_bundle) {
      $states = [
        'visible' => [
          ':input[name="tca_active"]' => [
            'checked' => TRUE,
          ],
        ],
      ];

      // Add force checkbox.
      $form['tca']['tca_force'] = [
        '#type' => 'checkbox',
        '#title' => t('Enforce Token usage'),
        '#default_value' => $forced,
        '#description' => t('Enforce usage of Tokens on this bundle.'),
        '#states' => $states,
      ];
    }

    // Attach the TCA form to the main form, and add a custom validation
    // callback.
    $attach += $form;

    // Optionally provide a form validation handler.
    $submit_handler_locations = $is_entity_bundle ? $entity_plugin
      ->getBundleFormSubmitHandlerAttachLocations() : $entity_plugin
      ->getFormSubmitHandlerAttachLocations();
    foreach ($submit_handler_locations as $location) {
      $array_ref =& $attach;
      if (is_array($location)) {
        foreach ($location as $subkey) {
          $array_ref =& $array_ref[$subkey];
        }
      }
      else {
        $array_ref =& $array_ref[$location];
      }
      if (isset($array_ref)) {
        $array_ref[] = [
          'Drupal\\tca\\FormManglerService',
          'staticHandleFormSubmit',
        ];
      }
    }
  }

  /**
   * Handle general aspects of TCA form submission.
   *
   * (Not specific to node etc.).
   *
   * @param array $form
   *   The form.
   * @param string|int|object $form_state
   *   The form state.
   */
  public static function staticHandleFormSubmit(array $form, $form_state) {
    \Drupal::service('tca.form_mangler')
      ->handleFormSubmit($form, $form_state);
  }

  /**
   * Handle general aspects of TCA form submission.
   *
   * (Not specific to node etc.).
   *
   * @param array $form
   *   The form.
   * @param string|int|object $form_state
   *   The form state.
   */
  public function handleFormSubmit(array $form, $form_state) {
    $is_entity_bundle = $form_state
      ->getValue('tca_is_entity_bundle');
    $entity_type_id = $form_state
      ->getValue('tca_entity_type_id');
    $entity_id = $form_state
      ->getValue('tca_entity_id');
    $active = $form_state
      ->getValue('tca_active');
    $forced = !$active ? FALSE : (bool) $form_state
      ->getValue('tca_force');
    $settings = [
      'active' => $active,
      'token' => NULL,
      'public' => $form_state
        ->getValue('tca_public'),
      'force' => $forced,
    ];
    if (!$entity_id) {
      $entity_id = $form_state
        ->getformObject()
        ->getEntity()
        ->id();
      if (!$entity_id) {
        return;
      }
    }
    $triggered_element = $form_state
      ->getTriggeringElement();
    $regenerate = in_array('tca', $triggered_element['#array_parents']);
    $token = NULL;
    $is_new = FALSE;

    // Set token.
    if (!$is_entity_bundle && $active) {
      $tca_settings = $this->tcaSettingsManager
        ->loadSettingsAsConfig($entity_type_id, $entity_id);
      $token = $tca_settings
        ->get('token');
      $is_new = !$token;
      if ($regenerate || $is_new) {
        $token = $this->tcaSettingsManager
          ->generateToken($entity_type_id, $entity_id);
        $form_state
          ->setValue('tca_token', $token);
      }
      $settings['token'] = $token;
    }

    // Doesn't save if Regenearte button was clicked.
    if (!$regenerate) {
      $this->tcaSettingsManager
        ->saveSettings($settings, $entity_type_id, $entity_id);
    }

    // Set message.
    if ($is_new && $token) {
      $entity = $this->entityTypeManager
        ->getStorage($entity_type_id)
        ->load($entity_id);
      $entity_url = $entity
        ->toUrl('canonical', [
        'query' => [
          'tca' => $token,
        ],
        'absolute' => TRUE,
      ])
        ->toString();
      \Drupal::messenger()
        ->addMessage(t('URL to bypass Token Access Control for this entity: @url', [
        '@url' => $entity_url,
      ]));
    }
  }

  /**
   * Handle general aspects of AJAX form behavior.
   *
   * (Not specific to node etc.).
   *
   * @param array $form
   *   The form.
   * @param string|int|object $form_state
   *   The form state.
   */
  public static function staticHandleFormAjax(array $form, $form_state) {
    $token = $form_state
      ->getValue('tca_token');
    $entity_type_id = $form_state
      ->getValue('tca_entity_type_id');
    $entity_id = $form_state
      ->getValue('tca_entity_id');
    if ($entity_id) {
      $entity = \Drupal::entityTypeManager()
        ->getStorage($entity_type_id)
        ->load($entity_id);
      $entity_url = $entity
        ->toUrl('canonical', [
        'query' => [
          'tca' => $token,
        ],
        'absolute' => TRUE,
      ])
        ->toString();
    }
    else {
      $entity_url = t('N/A');
    }
    $response = new AjaxResponse();
    $response
      ->addCommand(new InvokeCommand('[name="tca_token"]', 'val', [
      $token,
    ]));
    $response
      ->addCommand(new InvokeCommand('.form-item-tca-url-copy span', 'text', [
      $entity_url,
    ]));
    $response
      ->addCommand(new InvokeCommand('.js-form-item-tca-clipboardjs .clipboardjs', 'val', [
      $entity_url,
    ]));
    return $response;
  }

  /**
   * TODO.
   */
  protected function isEntityBundle($entity) {
    return is_subclass_of($entity, 'Drupal\\Core\\Config\\Entity\\ConfigEntityBundleBase');
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FormManglerService::$bundleInfo private property Drupal\Core\Entity\EntityTypeBundleInfo definition.
FormManglerService::$entityTypeManager private property Drupal\Core\Entity\EntityTypeManager definition.
FormManglerService::$tcaPluginManager private property Drupal\tca\Plugin\TcaPluginManager definition.
FormManglerService::addTcaSettingsToEntityForm public function Form structure for the TCA configuration.
FormManglerService::addTcaSettingsToForm private function Common functionality for adding TCA options to forms.
FormManglerService::handleFormSubmit public function Handle general aspects of TCA form submission.
FormManglerService::isEntityBundle protected function TODO.
FormManglerService::staticHandleFormAjax public static function Handle general aspects of AJAX form behavior.
FormManglerService::staticHandleFormSubmit public static function Handle general aspects of TCA form submission.
FormManglerService::__construct public function Constructor.
StringTranslationTrait::$stringTranslation protected property The string translation service. 4
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.