View source
<?php
declare (strict_types=1);
namespace Drupal\date_recur_modular\Form;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Drupal\date_recur\DateRecurHelper;
use Drupal\date_recur\DateRecurRuleInterface;
use Drupal\date_recur_modular\DateRecurModularUtilityTrait;
use Drupal\date_recur_modular\DateRecurModularWidgetFieldsTrait;
use Drupal\date_recur_modular\Plugin\Field\FieldWidget\DateRecurModularSierraWidget;
use RRule\RSet;
use Symfony\Component\DependencyInjection\ContainerInterface;
class DateRecurModularSierraModalOccurrencesForm extends FormBase {
use DateRecurModularWidgetFieldsTrait;
use DateRecurModularUtilityTrait;
protected const UTC_FORMAT = 'Ymd\\THis\\Z';
protected $tempStoreFactory;
protected $dateFormatter;
public function __construct(ConfigFactoryInterface $configFactory, PrivateTempStoreFactory $tempStoreFactory, DateFormatterInterface $dateFormatter) {
$this->configFactory = $configFactory;
$this->tempStoreFactory = $tempStoreFactory;
$this->dateFormatter = $dateFormatter;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('config.factory'), $container
->get('tempstore.private'), $container
->get('date.formatter'));
}
public function getFormId() {
return 'date_recur_modular_sierra_occurrences_modal';
}
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);
$rrule = $collection
->get(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE_KEY);
$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 {
$dtStart = new \DateTime();
}
$form['date_start'] = [
'#type' => 'value',
'#value' => $dtStart,
];
if (isset($rrule)) {
try {
$helper = DateRecurHelper::create($rrule, $dtStart);
} catch (\Exception $e) {
}
}
$rset = new RSet();
$excludes = [];
if (isset($helper)) {
foreach ($helper
->getRules() as $rule) {
$rset
->addRRule($rule
->getParts());
}
$excludes = $helper
->getExcluded();
}
sort($excludes);
$limit = min(1024 + 128 * ($multiplier - 1), 64000);
$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,
];
foreach ($excludes as $k => $exDate) {
if ($exDate < $occurrenceDate) {
$unmatchedExcludes[] = $exDate;
unset($excludes[$k]);
}
elseif ($exDate == $occurrenceDate) {
$matchedExcludes[] = $exDate;
$occurrences[$iteration]['excluded'] = TRUE;
unset($excludes[$k]);
}
}
$iteration++;
}
$outOfLimitExcludes = $excludes;
unset($excludes);
$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) {
[
'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',
'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',
'url' => Url::fromRoute('date_recur_modular_widget.sierra_modal_occurrences_form'),
'options' => [
'query' => [
FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
],
],
'callback' => [
$this,
'ajaxSubmitForm',
],
],
];
return $form;
}
public function ajaxShowMore(array &$form, FormStateInterface $form_state) : AjaxResponse {
$form_state
->setRebuild();
$multiplier = $form_state
->get('occurrence_multiplier');
$form_state
->set('occurrence_multiplier', $multiplier + 1);
$response = new AjaxResponse();
$form = \Drupal::formBuilder()
->rebuildForm($this
->getFormId(), $form_state, $form);
$response
->addCommand(new OpenModalDialogCommand($this
->t('Occurrences'), $form, [
'width' => '575',
]));
return $response;
}
public function ajaxSubmitForm(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
if ($form_state
->getErrors()) {
$form['status_messages'] = [
'#type' => 'status_messages',
];
return $response
->addCommand(new OpenModalDialogCommand($this
->t('Errors'), $form, [
'width' => '575',
]));
}
$originalString = $form_state
->getValue('original_string');
$dtStart = $form_state
->getValue('date_start');
try {
$helper = DateRecurHelper::create($originalString, $dtStart);
} catch (\Exception $e) {
}
$rset = new RSet();
if (isset($helper)) {
array_walk($helper
->getRules(), function (DateRecurRuleInterface $rule) use ($rset) {
$parts = $rule
->getParts();
unset($parts['DTSTART']);
$rset
->addRRule($parts);
});
}
foreach ($form_state
->getValue('table') as $i => $row) {
if ($row['exclude'] !== 0) {
$date = $form['occurrences']['table'][$i]['#date_object'];
$rset
->addExDate($date);
}
}
foreach ($form_state
->getValue('excludes_out_of_limit') as $exDate) {
$rset
->addExDate($exDate);
}
$lines = [];
foreach ($rset
->getRRules() as $rule) {
$lines[] = 'RRULE:' . $rule
->rfcString(FALSE);
}
$utc = new \DateTimeZone('UTC');
$exDates = array_map(function (\DateTime $exDate) use ($utc) {
$exDate
->setTimezone($utc);
return $exDate
->format(static::UTC_FORMAT);
}, $rset
->getExDates());
if (count($exDates) > 0) {
$lines[] = 'EXDATE:' . implode(',', $exDates);
}
$collection = $this->tempStoreFactory
->get(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE);
$collection
->set(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE_KEY, implode("\n", $lines));
$refreshBtnName = sprintf('[name="%s"]', $collection
->get(DateRecurModularSierraWidget::COLLECTION_MODAL_STATE_REFRESH_BUTTON));
$response
->addCommand(new CloseDialogCommand())
->addCommand(new InvokeCommand($refreshBtnName, 'trigger', [
'click',
]));
return $response;
}
public function submitForm(array &$form, FormStateInterface $form_state) {
return new AjaxResponse();
}
}