Commit 4aa93f7b authored by Martin Anderson-Clutz's avatar Martin Anderson-Clutz
Browse files

Issue #3300768 by mandclu: Widget configuration for min and max date_year_range

parent beb795dd
Loading
Loading
Loading
Loading
+98 −1
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ class SmartDateFieldItemList extends DateTimeFieldItemList {

    $element['default_duration_increments'] = [
      '#type' => 'textarea',
      '#title' => t('Allowed duration increments'),
      '#title' => t('Allowed duration values'),
      '#description' => $description,
      '#default_value' => isset($default_value['default_duration_increments']) ? $default_value['default_duration_increments'] : "30\n60|1 hour\n90\n120|2 hours\ncustom",
      '#required' => TRUE,
@@ -63,6 +63,22 @@ class SmartDateFieldItemList extends DateTimeFieldItemList {
      '#required' => TRUE,
    ];

    $element['min'] = [
      '#type' => 'textfield',
      '#title' => t('Minimum Date'),
      // @todo Add link to token browser.
      '#description' => t('Minimum date that will be accepted. Leave blank to accept any value. Provide a date value formatted like YYYY-MM-DD or a valid token that will return a date value.'),
      '#default_value' => $default_value['min'] ?? '',
    ];

    $element['max'] = [
      '#type' => 'textfield',
      '#title' => t('Maximum Date'),
      // @todo Add link to token browser.
      '#description' => t('Maximum date that will be accepted. Leave blank to accept any value. Provide a date value formatted like YYYY-MM-DD or a valid token that will return a date value.'),
      '#default_value' => isset($default_value['max']) ? $default_value['max'] : '',
    ];

    return $element;
  }

@@ -107,10 +123,77 @@ class SmartDateFieldItemList extends DateTimeFieldItemList {
        $form_state->setErrorByName('default_value_input][default_duration', $this->t('Please specify a default duration that is one of the provided options.'));
      }
    }
    // Validate that if min and max are both set max >= min.
    $limits_to_check = ['min', 'max'];
    $limits = [];
    foreach ($limits_to_check as $check) {
      $user_val = $form_state->getValue([
        'default_value_input',
        $check,
      ]) ?? '';
      if (empty($user_val)) {
        continue;
      }
      $user_val = $this->validateLimit($user_val);
      if ($user_val !== FALSE) {
        $limits[$check] = $user_val;
      }
      else {
        $form_state->setErrorByName('default_value_input][' . $check, $this->t('Invalid limit value provided. Please use either a date string in the format or YYYY-MM-DD or token.'));
      }
    }
    if (count($limits) == 2) {
      if ($limits['min'] > $limits['max']) {
        $form_state->setErrorByName('default_value_input][min', $this->t('The maximum date limit cannot be before the minimum.'));
      }
    }
    // Use the parent class method to validate relative dates.
    DateTimeFieldItemList::defaultValuesFormValidate($element, $form, $form_state);
  }

  /**
   * Check if the user-provided value can be resolved to a valid date string.
   *
   * @param string $value
   *   The user-provided input string.
   *
   * @return bool|string
   *   The validated/converted string, or FALSE.
   */
  protected static function validateLimit($value) {
    // If a simple date string, pass validation.
    if (static::validateDate($value)) {
      return $value;
    }
    // Check for a token.
    if (preg_match('|^\[.+(?::.+)+]$|', $value)) {
      $token_service = \Drupal::token();
      $value = $token_service->replace($value, [], ['clear' => TRUE]);
      // Consider a token valid if it returns a date string or empty value.
      if (empty($value) || static::validateDate($value)) {
        return $value;
      }
    }
    return FALSE;
  }

  /**
   * Validate that a date string follows the expected YYYY-MM-DD format.
   *
   * @param string $value
   *   The string to validate.
   *
   * @return bool
   *   Whether or not the string matches the expected format.
   */
  protected static function validateDate($value) {
    // If a simple date string, pass validation.
    if (preg_match('|\d{4}-\d{2}-\d{2}|', $value)) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
@@ -160,6 +243,20 @@ class SmartDateFieldItemList extends DateTimeFieldItemList {
      // After conversion to timestamp, we round up, so offset for this.
      $min = (int) $date->format('i') + 1;
      $date->modify('-' . $min . ' minutes');
      // If min or max values set, apply to default value.
      $limits_to_check = ['min', 'max'];
      $limits = [];
      foreach ($limits_to_check as $check) {
        if (!empty($default_value[0][$check]) && $limit = static::validateLimit($default_value[0][$check])) {
          $limits[$check] = new DrupalDateTime($limit, DateTimeItemInterface::STORAGE_TIMEZONE);
        }
      }
      if ($limits['min'] && $date < $limits['min']) {
        $date->setDate($limits['min']->format('Y'), $limits['min']->format('n'), $limits['min']->format('j'));
      }
      elseif ($limits['max'] && $date > $limits['min']) {
        $date->setDate($limits['max']->format('Y'), $limits['max']->format('n'), $limits['max']->format('j'));
      }
    }

    $value = $date->getTimestamp();
+51 −0
Original line number Diff line number Diff line
@@ -175,11 +175,19 @@ class SmartDateWidgetBase extends DateTimeWidgetBase {
        'default_duration' => 60,
      ];
    }
    $limits_to_check = ['min', 'max'];
    foreach ($limits_to_check as $check) {
      if (!empty($defaults[$check]) && $limit = static::validateLimit($defaults[$check])) {
        $element['value']['#attributes'][$check] = $limit;
        $element['end_value']['#attributes'][$check] = $limit;
      }
    }
    // Wrap all of the select elements with a fieldset.
    $element['#theme_wrappers'][] = 'fieldset';

    $element['#element_validate'][] = [static::class, 'validateStartEnd'];
    $element['value']['#title'] = t('Start');
    // @todo Remove #date_year_range as of Drupal 10.1 when BIGINT will be used.
    $element['value']['#date_year_range'] = '1902:2037';
    if (isset($values['start'])) {
      // Ensure values always display relative to the site.
@@ -582,4 +590,47 @@ class SmartDateWidgetBase extends DateTimeWidgetBase {
    return $date;
  }

  /**
   * Check if the user-provided value can be resolved to a valid date string.
   *
   * @param string $value
   *   The user-provided input string.
   *
   * @return bool|string
   *   The validated/converted string, or FALSE.
   */
  protected static function validateLimit($value) {
    // If a simple date string, pass validation.
    if (static::validateDate($value)) {
      return $value;
    }
    // Check for a token.
    if (preg_match('|^\[.+(?::.+)+]$|', $value)) {
      $token_service = \Drupal::token();
      $value = $token_service->replace($value, [], ['clear' => TRUE]);
      // Consider a token valid if it returns a date string or empty value.
      if (empty($value) || static::validateDate($value)) {
        return $value;
      }
    }
    return FALSE;
  }

  /**
   * Validate that a date string follows the expected YYYY-MM-DD format.
   *
   * @param string $value
   *   The string to validate.
   *
   * @return bool
   *   Whether or not the string matches the expected format.
   */
  protected static function validateDate($value) {
    // If a simple date string, pass validation.
    if (preg_match('|\d{4}-\d{2}-\d{2}|', $value)) {
      return TRUE;
    }
    return FALSE;
  }

}