public function DateRecurModularSierraModalOccurrencesForm::buildForm in Recurring Date Field Modular Widgets 8
Same name and namespace in other branches
- 3.x src/Form/DateRecurModularSierraModalOccurrencesForm.php \Drupal\date_recur_modular\Form\DateRecurModularSierraModalOccurrencesForm::buildForm()
- 2.x src/Form/DateRecurModularSierraModalOccurrencesForm.php \Drupal\date_recur_modular\Form\DateRecurModularSierraModalOccurrencesForm::buildForm()
Form constructor.
Parameters
array $form: An associative array containing the structure of the form.
\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.
Return value
array The form structure.
Overrides FormInterface::buildForm
File
- src/
Form/ DateRecurModularSierraModalOccurrencesForm.php, line 90
Class
- DateRecurModularSierraModalOccurrencesForm
- Generate a form to excluding occurrences, designed for display in modal.
Namespace
Drupal\date_recur_modular\FormCode
public function buildForm(array $form, FormStateInterface $form_state) {
$form['#attached']['library'][] = 'date_recur_modular/sierra_modal_occurrences_form';
$form['#attached']['library'][] = 'core/drupal.ajax';
$form['#theme'] = 'date_recur_modular_sierra_widget_modal_occurrences_form';
$collection = $this->tempStoreFactory
->get(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE);
/** @var string|null $rrule */
$rrule = $collection
->get(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE_KEY);
/** @var string $dateFormat */
$dateFormatId = $collection
->get(DateRecurModularSierraWidget::COLLECTION_MODAL_DATE_FORMAT);
$multiplier = $form_state
->get('occurrence_multiplier');
if (!isset($multiplier)) {
$form_state
->set('occurrence_multiplier', 1);
$multiplier = 1;
}
$form['original_string'] = [
'#type' => 'value',
'#value' => $rrule,
];
$dtStartString = $collection
->get(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE_DTSTART);
if (!empty($dtStartString)) {
$dtStart = \DateTime::createFromFormat(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE_DTSTART_FORMAT, $dtStartString);
}
else {
// Use current date if there is no valid starting date from handoff.
$dtStart = new \DateTime();
}
$form['date_start'] = [
'#type' => 'value',
'#value' => $dtStart,
];
if (isset($rrule)) {
try {
$helper = DateRecurHelper::create($rrule, $dtStart);
} catch (\Exception $e) {
}
}
// Rebuild using Rset because we want to be able to iterate over occurrences
// without considering any existing EXDATEs.
$rset = new RSet();
/** @var \DateTime[] $excluded */
$excludes = [];
if (isset($helper)) {
foreach ($helper
->getRules() as $rule) {
$rset
->addRRule($rule
->getParts());
}
$excludes = $helper
->getExcluded();
}
sort($excludes);
// Initial limit is 1024, with 128 per page thereafter, with an absolute
// maximum of 64000. Limit prevents performance issues and abuse.
$limit = min(1024 + 128 * ($multiplier - 1), 64000);
// 6 months from now plus 4 months thereafter.
$limitDate = (new \DateTime())
->modify(sprintf('+%d months', 6 + ($multiplier - 1) * 4));
$occurrences = [];
$matchedExcludes = [];
$unmatchedExcludes = [];
$iteration = 0;
foreach ($rset as $occurrenceDate) {
if ($iteration > $limit || $limitDate < $occurrenceDate) {
break;
}
$occurrences[$iteration] = [
'date' => $occurrenceDate,
'excluded' => FALSE,
];
// After each occurrence evaluate if there were any excludes that fit
// between this occurrence and last occurrence.
foreach ($excludes as $k => $exDate) {
if ($exDate < $occurrenceDate) {
// Occurrence was between this and last occurrence, so likely no
// longer matches against the RRULE.
// Its done progessively like this instead of comparing occurrences to
// EXDATEs as some EXDATEs may fall outside of the date/count limits.
$unmatchedExcludes[] = $exDate;
unset($excludes[$k]);
}
elseif ($exDate == $occurrenceDate) {
// Occurrence matches an exclude date exactly.
$matchedExcludes[] = $exDate;
$occurrences[$iteration]['excluded'] = TRUE;
unset($excludes[$k]);
}
}
$iteration++;
}
/** @var \DateTime[] $outOfLimitExcludes */
$outOfLimitExcludes = $excludes;
unset($excludes);
// Any remaining excludes are out of range. We dont know if they are matched
// to the rule or not. Keep them and save.
$form['excludes_out_of_limit'] = [
'#type' => 'value',
'#value' => $outOfLimitExcludes,
];
if (count($unmatchedExcludes)) {
$form['invalid_excludes'] = [
'#type' => 'details',
'#title' => $this
->formatPlural(count($unmatchedExcludes), '@count invalid exclude', '@count invalid excludes'),
];
$form['invalid_excludes']['help'] = [
'#type' => 'inline_template',
'#template' => '<p>{{ message }}</p>',
'#context' => [
'message' => $this
->formatPlural(count($unmatchedExcludes), 'This invalid excluded occurrence will be automatically removed.', 'These invalid excluded occurrences will be automatically removed.'),
],
];
$form['invalid_excludes']['table'] = [
'#type' => 'table',
'#header' => [
'date' => $this
->t('Date'),
],
];
$form['invalid_excludes']['table']['#rows'] = array_map(function (\DateTime $date) use ($dateFormatId, $dtStart) : array {
return [
'date' => $this->dateFormatter
->format($date
->getTimestamp(), $dateFormatId, '', $dtStart
->getTimezone()
->getName()),
];
}, $unmatchedExcludes);
}
$form['occurrences'] = [
'#type' => 'details',
'#title' => $this
->t('Occurrences'),
'#open' => TRUE,
];
$form['occurrences']['help'] = [
'#type' => 'inline_template',
'#template' => '<p>{{ message }}</p>',
'#context' => [
'message' => $this
->t('This table shows a selection of occurrences. Occurrences may be removed individually. Times are displayed in <em>@time_zone</em> time zone.', [
'@time_zone' => $dtStart
->getTimezone()
->getName(),
]),
],
];
$form['occurrences']['table'] = [
'#type' => 'table',
'#header' => [
'exclude' => $this
->t('Exclude'),
'date' => $this
->t('Date'),
],
'#empty' => $this
->t('There are no occurrences.'),
'#prefix' => '<div id="occurrences-table">',
'#suffix' => '</div>',
];
$i = 0;
foreach ($occurrences as $occurrence) {
/** @var \DateTime $date */
/** @var bool $excluded */
[
'date' => $date,
'excluded' => $excluded,
] = $occurrence;
$date
->setTimezone($dtStart
->getTimezone());
$row = [];
$row['exclude'] = [
'#type' => 'checkbox',
'#return_value' => $i,
'#default_value' => $excluded,
];
$row['date']['#markup'] = $this->dateFormatter
->format($date
->getTimestamp(), $dateFormatId, '', $dtStart
->getTimezone()
->getName());
$row['#date_object'] = $date;
$form['occurrences']['table'][$i] = $row;
$i++;
}
$form['occurrences']['show_more'] = [
'#type' => 'container',
];
$form['occurrences']['show_more']['count_message'] = [
'#type' => 'inline_template',
'#template' => '<p>{{ count_message }}</p>',
'#context' => [
'count_message' => $this
->formatPlural(count($outOfLimitExcludes), 'There is @count more hidden excluded occurrence.', 'There are @count more hidden excluded occurrences.'),
],
];
if (count($outOfLimitExcludes) > 0) {
$nextExclude = reset($outOfLimitExcludes);
$form['occurrences']['show_more']['next_message'] = [
'#type' => 'inline_template',
'#template' => '<p>{{ next_message }}</p>',
'#context' => [
'next_message' => $this
->t('Next hidden excluded occurrence is at @date', [
'@date' => $this->dateFormatter
->format($nextExclude
->getTimestamp(), $dateFormatId, '', $dtStart
->getTimezone()
->getName()),
]),
],
];
}
$form['occurrences']['show_more']['show_more'] = [
'#type' => 'submit',
'#value' => $this
->t('Show more'),
'#ajax' => [
'event' => 'click',
// Need 'url' and 'options' for this submission button to use this
// controller not the caller.
'url' => Url::fromRoute('date_recur_modular_widget.sierra_modal_occurrences_form'),
'options' => [
'query' => [
FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
],
],
'callback' => [
$this,
'ajaxShowMore',
],
],
];
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this
->t('Done'),
'#button_type' => 'primary',
'#ajax' => [
'event' => 'click',
// Need 'url' and 'options' for this submission button to use this
// controller not the caller.
'url' => Url::fromRoute('date_recur_modular_widget.sierra_modal_occurrences_form'),
'options' => [
'query' => [
FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
],
],
'callback' => [
$this,
'ajaxSubmitForm',
],
],
];
return $form;
}