You are here

function _scheduler_entity_form_alter in Scheduler 2.x

Form alter handling for entity forms - add and edit.

1 call to _scheduler_entity_form_alter()
scheduler_form_alter in ./scheduler.module
Implements hook_form_alter().

File

./scheduler.module, line 72
Scheduler publishes and unpublishes entities on dates specified by the user.

Code

function _scheduler_entity_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Get the form display object. If this does not exist because the form is
  // prevented from displaying, such as in Commerce Add Product before any store
  // has been created, we can not (and do not need) to do anything, so exit.
  if (!($display = $form_state
    ->getFormObject()
    ->getFormDisplay($form_state))) {
    return;
  }
  $config = \Drupal::config('scheduler.settings');
  $scheduler_manager = \Drupal::service('scheduler.manager');

  /** @var \Drupal\Core\Entity\EntityInterface $entity */
  $entity = $form_state
    ->getFormObject()
    ->getEntity();
  $entityTypeId = $entity
    ->getEntityTypeId();
  $publishing_enabled = $scheduler_manager
    ->getThirdPartySetting($entity, 'publish_enable', $config
    ->get('default_publish_enable'));
  $unpublishing_enabled = $scheduler_manager
    ->getThirdPartySetting($entity, 'unpublish_enable', $config
    ->get('default_unpublish_enable'));

  // If neither publishing nor unpublishing are enabled then there is nothing to
  // do so remove the fields from the form and exit early.
  if (!$publishing_enabled && !$unpublishing_enabled) {
    unset($form['publish_on']);
    unset($form['unpublish_on']);
    return;
  }

  // Determine if the scheduler fields have been set to hidden (disabled).
  $publishing_displayed = !empty($display
    ->getComponent('publish_on'));
  $unpublishing_displayed = !empty($display
    ->getComponent('unpublish_on'));

  // Invoke all implementations of hook_scheduler_hide_publish_date() and
  // hook_scheduler_{type}_hide_publish_date() to allow other modules to hide
  // the field on the entity edit form.
  if ($publishing_enabled && $publishing_displayed) {
    $hook_implementations = $scheduler_manager
      ->getHookImplementations('hide_publish_date', $entity);
    foreach ($hook_implementations as $function) {
      $publishing_displayed = $function($form, $form_state, $entity) !== TRUE && $publishing_displayed;
    }
  }

  // Invoke all implementations of hook_scheduler_hide_unpublish_date() and
  // hook_scheduler_{type}_hide_unpublish_date() to allow other modules to hide
  // the field on the entity edit form.
  if ($unpublishing_enabled && $unpublishing_displayed) {
    $hook_implementations = $scheduler_manager
      ->getHookImplementations('hide_unpublish_date', $entity);
    foreach ($hook_implementations as $function) {
      $unpublishing_displayed = $function($form, $form_state, $entity) !== TRUE && $unpublishing_displayed;
    }
  }

  // If both publishing and unpublishing are either not enabled or are hidden
  // for this entity type then the only thing to do is remove the fields from
  // the form, then exit.
  if ((!$publishing_enabled || !$publishing_displayed) && (!$unpublishing_enabled || !$unpublishing_displayed)) {
    unset($form['publish_on']);
    unset($form['unpublish_on']);
    return;
  }
  $allow_date_only = $config
    ->get('allow_date_only');

  // A publish_on date is required if the content type option is set and the
  // entity is being created or it is currently not published but has a
  // scheduled publishing date.
  $publishing_required = $publishing_enabled && $scheduler_manager
    ->getThirdPartySetting($entity, 'publish_required', $config
    ->get('default_publish_required')) && ($entity
    ->isNew() || !$entity
    ->isPublished() && !empty($entity->publish_on->value));

  // An unpublish_on date is required if the content type option is set and the
  // entity is being created or the current status is published or the entity is
  // scheduled to be published.
  $unpublishing_required = $unpublishing_enabled && $scheduler_manager
    ->getThirdPartySetting($entity, 'unpublish_required', $config
    ->get('default_unpublish_required')) && ($entity
    ->isNew() || $entity
    ->isPublished() || !empty($entity->publish_on->value));

  // Create a 'details' field group to wrap the scheduling fields, and expand it
  // if publishing or unpublishing is required, if a date already exists or the
  // fieldset is configured to be always expanded.
  $has_data = isset($entity->publish_on->value) || isset($entity->unpublish_on->value);
  $always_expand = $scheduler_manager
    ->getThirdPartySetting($entity, 'expand_fieldset', $config
    ->get('default_expand_fieldset')) === 'always';
  $expand_details = $publishing_required || $unpublishing_required || $has_data || $always_expand;

  // Create the group for the fields.
  $form['scheduler_settings'] = [
    '#type' => 'details',
    '#title' => t('Scheduling options'),
    '#open' => $expand_details,
    '#weight' => 35,
    '#attributes' => [
      'class' => [
        'scheduler-form',
      ],
    ],
    '#optional' => FALSE,
  ];

  // Attach the fields to group.
  $form['unpublish_on']['#group'] = 'scheduler_settings';
  $form['publish_on']['#group'] = 'scheduler_settings';

  // Show the field group as a vertical tab if this option is enabled.
  $use_vertical_tabs = $scheduler_manager
    ->getThirdPartySetting($entity, 'fields_display_mode', $config
    ->get('default_fields_display_mode')) === 'vertical_tab';
  if ($use_vertical_tabs) {
    $form['scheduler_settings']['#group'] = 'advanced';

    // Attach the javascript for the vertical tabs.
    $form['scheduler_settings']['#attached']['library'][] = 'scheduler/vertical-tabs';
  }

  // Define the descriptions depending on whether the time can be skipped.
  $descriptions = [];
  if ($allow_date_only) {
    $descriptions['format'] = t('Enter a date. The time part is optional.');

    // Show the default time so users know what they will get if they do not
    // enter a time.
    $descriptions['default'] = t('The default time is @default_time.', [
      '@default_time' => $config
        ->get('default_time'),
    ]);

    // Use javascript to pre-fill the time parts if the dates are required.
    // See js/scheduler_default_time.js for more details.
    if ($publishing_required || $unpublishing_required) {
      $form['scheduler_settings']['#attached']['library'][] = 'scheduler/default-time';
      $form['scheduler_settings']['#attached']['drupalSettings']['schedulerDefaultTime'] = $config
        ->get('default_time');
    }
  }
  else {
    $descriptions['format'] = t('Enter a date and time.');
  }
  if (!$publishing_required) {
    $descriptions['blank'] = t('Leave the date blank for no scheduled publishing.');
  }
  $form['publish_on']['#access'] = $publishing_enabled && $publishing_displayed;
  $form['publish_on']['widget'][0]['value']['#required'] = $publishing_required;
  $form['publish_on']['widget'][0]['value']['#description'] = Xss::filter(implode(' ', $descriptions));
  if (!$unpublishing_required) {
    $descriptions['blank'] = t('Leave the date blank for no scheduled unpublishing.');
  }
  else {
    unset($descriptions['blank']);
  }
  $form['unpublish_on']['#access'] = $unpublishing_enabled && $unpublishing_displayed;
  $form['unpublish_on']['widget'][0]['value']['#required'] = $unpublishing_required;
  $form['unpublish_on']['widget'][0]['value']['#description'] = Xss::filter(implode(' ', $descriptions));

  // When hiding the seconds on time input, we need to remove the seconds from
  // the form value, as some browsers HTML5 rendering still show the seconds.
  // We can use the same jQuery drupal behaviors file as for default time.
  // This functionality is not covered by tests.
  if ($config
    ->get('hide_seconds')) {

    // If there is a publish_on time, then use jQuery to remove the seconds.
    if (isset($entity->publish_on->value)) {
      $form['scheduler_settings']['#attached']['library'][] = 'scheduler/default-time';
      $form['scheduler_settings']['#attached']['drupalSettings']['schedulerHideSecondsPublishOn'] = date('H:i', $entity->publish_on->value);
    }

    // Likewise for the unpublish_on time.
    if (isset($entity->unpublish_on->value)) {
      $form['scheduler_settings']['#attached']['library'][] = 'scheduler/default-time';
      $form['scheduler_settings']['#attached']['drupalSettings']['schedulerHideSecondsUnpublishOn'] = date('H:i', $entity->unpublish_on->value);
    }
  }

  // Check the permission for entering scheduled dates.
  $permission = $scheduler_manager
    ->permissionName($entityTypeId, 'schedule');
  if (!\Drupal::currentUser()
    ->hasPermission($permission)) {

    // Do not show the scheduler fields for users who do not have permission.
    // Setting #access to FALSE for 'scheduler_settings' is enough to hide the
    // fields. Setting FALSE for the individual fields is necessary to keep any
    // existing scheduled dates preserved and remain unchanged on saving.
    $form['scheduler_settings']['#access'] = FALSE;
    $form['publish_on']['#access'] = FALSE;
    $form['unpublish_on']['#access'] = FALSE;

    // @todo Find a more elegant solution for bypassing the validation of
    // scheduler fields when the user does not have permission.
    // Note: This scenario is NOT yet covered by any tests, neither in
    // SchedulerPermissionsTest.php nor SchedulerRequiredTest.php
    // @see https://www.drupal.org/node/2651448
    $form['publish_on']['widget'][0]['value']['#required'] = FALSE;
    $form['unpublish_on']['widget'][0]['value']['#required'] = FALSE;
  }

  // Check which widget type is set for the scheduler fields, and give a warning
  // if the wrong one has been set and provide a hint and link to fix it.
  $pluginDefinitions = $display
    ->get('pluginManager')
    ->getDefinitions();
  $fields_to_check = [];
  if ($publishing_enabled && $publishing_displayed) {
    $fields_to_check[] = 'publish_on';
  }
  if ($unpublishing_enabled && $unpublishing_displayed) {
    $fields_to_check[] = 'unpublish_on';
  }
  $correct_widget_id = 'datetime_timestamp_no_default';
  foreach ($fields_to_check as $field) {
    $actual_widget_id = $display
      ->getComponent($field)['type'];
    if ($actual_widget_id != $correct_widget_id) {
      $link = \Drupal::moduleHandler()
        ->moduleExists('field_ui') ? Url::fromRoute("entity.entity_form_display.{$entityTypeId}.default", [
        "{$entityTypeId}_type" => $entity
          ->bundle(),
      ])
        ->toString() : '#';
      \Drupal::messenger()
        ->addMessage(t('The widget for field %field is incorrectly set to %wrong. This should be changed to %correct by an admin user via the <a href="@link">Field UI form display</a> :not_available', [
        '%field' => (string) $form[$field]['widget']['#title'],
        '%correct' => (string) $pluginDefinitions[$correct_widget_id]['label'],
        '%wrong' => (string) $pluginDefinitions[$actual_widget_id]['label'],
        '@link' => $link,
        ':not_available' => \Drupal::moduleHandler()
          ->moduleExists('field_ui') ? '' : '(' . t('not available') . ')',
      ]), 'warning', FALSE);
    }
  }
}