You are here

function scheduler_form_node_form_alter in Scheduler 8

Implements hook_form_FORM_ID_alter() for node_form().

File

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

Code

function scheduler_form_node_form_alter(&$form, FormStateInterface $form_state) {
  $config = \Drupal::config('scheduler.settings');

  /** @var \Drupal\node\NodeTypeInterface $type */
  $type = $form_state
    ->getFormObject()
    ->getEntity()->type->entity;
  $publishing_enabled = $type
    ->getThirdPartySetting('scheduler', 'publish_enable', $config
    ->get('default_publish_enable'));
  $unpublishing_enabled = $type
    ->getThirdPartySetting('scheduler', 'unpublish_enable', $config
    ->get('default_unpublish_enable'));

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

  /** @var \Drupal\node\NodeInterface $node */
  $node = $form_state
    ->getFormObject()
    ->getEntity();

  // Invoke all implementations of hook_scheduler_hide_publish_on_field() to
  // allow other modules to hide the field on the node edit form.
  if ($publishing_enabled && $publishing_displayed) {
    $hook = 'scheduler_hide_publish_on_field';
    foreach (\Drupal::moduleHandler()
      ->getImplementations($hook) as $module) {
      $function = $module . '_' . $hook;
      $publishing_displayed = $function($form, $form_state, $node) !== TRUE && $publishing_displayed;
    }
  }

  // Invoke all implementations of hook_scheduler_hide_unpublish_on_field() to
  // allow other modules to hide the field on the node edit form.
  if ($unpublishing_enabled && $unpublishing_displayed) {
    $hook = 'scheduler_hide_unpublish_on_field';
    foreach (\Drupal::moduleHandler()
      ->getImplementations($hook) as $module) {
      $function = $module . '_' . $hook;
      $unpublishing_displayed = $function($form, $form_state, $node) !== TRUE && $unpublishing_displayed;
    }
  }

  // If both publishing and unpublishing are either not enabled or are hidden
  // for this node 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
  // node is being created or it is currently not published but has a
  // scheduled publishing date.
  $publishing_required = $publishing_enabled && $type
    ->getThirdPartySetting('scheduler', 'publish_required', $config
    ->get('default_publish_required')) && ($node
    ->isNew() || !$node
    ->isPublished() && !empty($node->publish_on->value));

  // An unpublish_on date is required if the content type option is set and the
  // node is being created or the current status is published or the node is
  // scheduled to be published.
  $unpublishing_required = $unpublishing_enabled && $type
    ->getThirdPartySetting('scheduler', 'unpublish_required', $config
    ->get('default_unpublish_required')) && ($node
    ->isNew() || $node
    ->isPublished() || !empty($node->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($node->publish_on->value) || isset($node->unpublish_on->value);
  $always_expand = $type
    ->getThirdPartySetting('scheduler', '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 = $type
    ->getThirdPartySetting('scheduler', '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.
  if ($config
    ->get('hide_seconds')) {

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

    // Likewise for the unpublish_on time.
    if (isset($node->unpublish_on->value)) {
      $form['scheduler_settings']['#attached']['library'][] = 'scheduler/default-time';
      $form['scheduler_settings']['#attached']['drupalSettings']['schedulerHideSecondsUnpublishOn'] = date('H:i', $node->unpublish_on->value);
    }
  }
  if (!\Drupal::currentUser()
    ->hasPermission('schedule publishing of nodes')) {

    // 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) {
      \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 Field UI <a href="@link">content type 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' => \Drupal::moduleHandler()
          ->moduleExists('field_ui') ? Url::fromRoute('entity.entity_form_display.node.default', [
          'node_type' => $type
            ->get('type'),
        ])
          ->toString() : '#',
        ':not_available' => \Drupal::moduleHandler()
          ->moduleExists('field_ui') ? '' : '(' . t('not available') . ')',
      ]), 'warning', FALSE);
    }
  }
}