Skip to content

Commit

Permalink
Merge pull request #1766 from danskernesdigitalebibliotek/event-unpub…
Browse files Browse the repository at this point in the history
…lishing-rehaul_DDFHER-88

Re-factor event scheduler setup. DDFHER-88
  • Loading branch information
rasben authored Dec 10, 2024
2 parents 6daa3d4 + c00e36a commit d73743e
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 24 deletions.
4 changes: 3 additions & 1 deletion config/sync/dpl_event.settings.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
unpublish_schedule: 0
unpublish_schedule: '21600'
price_currency: DKK
unpublish_enable: 1
unpublish_series_enable: 0
10 changes: 8 additions & 2 deletions config/sync/views.view.events.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,12 @@ display:
options:
perm: 'access content'
cache:
type: none
options: { }
type: search_api_time
options:
results_lifespan: 3600
results_lifespan_custom: 0
output_lifespan: 3600
output_lifespan_custom: 0
empty:
area:
id: area
Expand Down Expand Up @@ -377,5 +381,7 @@ display:
- url.query_args
- user.permissions
tags:
- 'config:facets.facet.branch'
- 'config:facets.facet.event_categories'
- 'config:search_api.index.events'
- 'search_api_list:events'
9 changes: 9 additions & 0 deletions web/modules/custom/dpl_admin/assets/dpl_admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,12 @@
body:has(.eventseries-form--confirm) .form-actions {
display: none;
}

/**
A very simple element, used in Drupal forms when displaying warnings.
*/
.dpl-form-warning {
color: #d80404;
font-weight: bold;
max-width: 500px;
}
42 changes: 42 additions & 0 deletions web/modules/custom/dpl_event/dpl_event.install
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/

use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\dpl_event\Form\SettingsForm;
use Drupal\dpl_event\Workflows\UnpublishSchedule;
use Drupal\drupal_typed\DrupalTyped;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
Expand All @@ -18,6 +20,7 @@ use Drupal\locale\StringDatabaseStorage;
function dpl_event_install(): void {
_dpl_event_create_mock_title();
_dpl_event_add_weekday_translations();
_dpl_event_set_scheduler_settings();
}

/**
Expand Down Expand Up @@ -67,6 +70,45 @@ function dpl_event_update_10004(): string {
return _dpl_event_add_weekday_translations();
}

/**
* Set default values of event-unpublishing-schedule settings, and re-schedule.
*/
function dpl_event_update_10005(): string {
return _dpl_event_set_scheduler_settings();
}

/**
* Set default values of event-unpublishing-schedule settings, and re-schedule.
*/
function _dpl_event_set_scheduler_settings(): string {
$config_name = SettingsForm::CONFIG_NAME;

$config = \Drupal::configFactory()->getEditable($config_name);

$existing_schedule = $config->get('unpublish_schedule');

// If we have no existing schedule set, set it as default to 6 hours.
$schedule_time = !empty($existing_schedule) ? $existing_schedule : 21600;

// If no schedule is set already, we'll disable the setting.
$unpublish_enable = ($existing_schedule != 0);

$config->set('unpublish_schedule', $schedule_time);
$config->set('unpublish_enable', $unpublish_enable);

// This was originally enabled for all sites, but as we no longer recommend
// it, we'll set the default to false.
// The libraries will be informed of this in the changelog.
$config->set('unpublish_series_enable', FALSE);

$config->save();

$unpublish_scheduler = DrupalTyped::service(UnpublishSchedule::class, 'dpl_event.unpublish_schedule');
$count = $unpublish_scheduler->rescheduleAll();

return "Enabled new unpublishing settings. $count events has been rescheduled.";
}

/**
* Add a mock title field to eventinstance, to fix entity reference fields.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
use Symfony\Component\HttpKernel\KernelEvents;

/**
* Redirects requests for single instance event series to that instance.
* Redirect to AND from eventseries, depending on the situation.
*
* - If accessing an eventseries with a single active instance, redirect to
* that instance
* - If failing to access an unpublished eventinstance, redirect to the
* parent series.
*
* @package Drupal\dpl_event\EventSubscriber
*/
Expand All @@ -31,7 +36,14 @@ public function __construct(
*/
public static function getSubscribedEvents(): array {
return [
KernelEvents::REQUEST => 'checkEventSeriesRedirect',
KernelEvents::REQUEST => [
['checkEventSeriesRedirect'],
],
// Only target users that end up on an unpublished eventinstance page,
// without being allowed to see it.
KernelEvents::EXCEPTION => [
['checkEventInstanceRedirect'],
],
];
}

Expand All @@ -47,7 +59,6 @@ public static function getSubscribedEvents(): array {
* The request event.
*/
public function checkEventSeriesRedirect(RequestEvent $event): void {

$request = $event->getRequest();
$route_name = $request->attributes->get('_route');
$event_series = $request->attributes->get('eventseries');
Expand All @@ -73,4 +84,31 @@ public function checkEventSeriesRedirect(RequestEvent $event): void {
}
}

/**
* Redirect unpublished eventinstances to eventseries.
*
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
* The response event.
*/
public function checkEventInstanceRedirect(RequestEvent $event): void {
$request = $event->getRequest();

if ($request->attributes->get('_route') !== 'entity.eventinstance.canonical') {
return;
}

$event_instance = $request->attributes->get('eventinstance');

if (!($event_instance instanceof EventInstance) || $event_instance->isPublished()) {
return;
}

// At this stage, we know we're on an unpublished eventinstance.
// Look up the eventseries, and redirect to it.
$event_series = $event_instance->getEventSeries();

$response = new RedirectResponse($event_series->toUrl()->toString());
$event->setResponse($response);
}

}
72 changes: 64 additions & 8 deletions web/modules/custom/dpl_event/src/Form/SettingsForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,21 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
],
];

$form['enable_screen_name'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enable screen names'),
'#default_value' => $config->get('enable_screen_name'),
'#description' => $this->t('Enable screen names on events. Requires an external system to actually fetch and display the events on real physical screens.'),
];

$form['unpublish'] = [
'#type' => 'fieldset',
'#title' => $this->t('Automatic unpublication', [], ['context' => 'DPL event']),
];

$period = [
// 1 hour
3600,
// 6 hours
21600,
// 12 hours
Expand All @@ -98,21 +107,66 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
15552000,
];
$period = array_map([$this->dateFormatter, 'formatInterval'], array_combine($period, $period));

$form['unpublish']['unpublish_enable'] = [
'#type' => 'checkbox',
'#title' => $this->t('Unpublish eventinstances when they have occured (recommended)', [], ['context' => 'DPL event']),
'#default_value' => $config->get('unpublish_enable'),
];

$form['unpublish']['unpublish_disable_warning'] = [
'#type' => 'container',
// js-form-wrapper is important - otherwise, Drupal states will not work.
'#prefix' => '<div class="dpl-form-warning js-form-wrapper">',
'#markup' => $this->t('Notice - if you do not choose that eventinstances get unpublished, they may show up in automatic and manual lists, across the site.', [], ['context' => 'DPL event']),
'#suffix' => '</div>',
'#states' => [
'visible' => [
':input[name="unpublish_enable"]' => ['checked' => FALSE],
],
],
];

$form['unpublish']['unpublish_schedule'] = [
'#type' => 'select',
'#title' => $this->t('Schedule', [], ['context' => "DPL event"]),
'#title' => $this->t('How much time should pass after an eventinstance has occurred before it should be unpublished?', [], ['context' => "DPL event"]),
'#default_value' => $config->get('unpublish_schedule'),
'#options' => $period,
'#empty_option' => $this->t('Automatic unpublication disabled', [], ['context' => "DPL event"]),
'#empty_value' => 0,
'#description' => $this->t('How much time should pass after an event has occurred before it should be unpublished automatically.', [], ['context' => "DPL event"]),
'#states' => [
'visible' => [
':input[name="unpublish_enable"]' => ['checked' => TRUE],
],
],
];

$form['enable_screen_name'] = [
$form['unpublish']['unpublish_series_enable'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enable screen names'),
'#default_value' => $config->get('enable_screen_name'),
'#description' => $this->t('Enable screen names on events. Requires an external system to actually fetch and display the events on real physical screens.'),
'#title' => $this->t('Unpublish the series when all instances have occurred (not recommended)', [], ['context' => "DPL event"]),
'#default_value' => $config->get('unpublish_series_enable'),
// Only display the field when unpublish schedule has a non-0 value.
'#states' => [
'visible' => [
':input[name="unpublish_enable"]' => ['checked' => TRUE],
],
],
];

$form['unpublish']['unpublish_series_enable_warning'] = [
'#type' => 'container',
// js-form-wrapper is important - otherwise, Drupal states will not work.
'#prefix' => '<div class="dpl-form-warning js-form-wrapper">',
'#markup' => $this->t('Notice - if series get unpublished, old instance links will no longer work. If you however keep the series published, expired instances will redirect to the associated series.', [], ['context' => 'DPL event']),
'#suffix' => '</div>',
'#states' => [
'visible' => [
':input[name="unpublish_enable"]' => ['checked' => TRUE],
// PHPCS doesn't understand Drupal's weird way of doing states.
// phpcs:disable Squiz.Arrays.ArrayDeclaration.NoKeySpecified
'and',
// phpcs:enable Squiz.Arrays.ArrayDeclaration.NoKeySpecified
':input[name="unpublish_series_enable"]' => ['checked' => TRUE],
],
],
];

return parent::buildForm($form, $form_state);
Expand All @@ -124,7 +178,9 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
public function submitForm(array &$form, FormStateInterface $form_state): void {
$this->config(self::CONFIG_NAME)
->set('price_currency', $form_state->getValue('price_currency'))
->set('unpublish_enable', $form_state->getValue('unpublish_enable'))
->set('unpublish_schedule', $form_state->getValue('unpublish_schedule'))
->set('unpublish_series_enable', $form_state->getValue('unpublish_series_enable'))
->set('enable_screen_name', $form_state->getValue('enable_screen_name'))
->save();
parent::submitForm($form, $form_state);
Expand Down
47 changes: 37 additions & 10 deletions web/modules/custom/dpl_event/src/Workflows/UnpublishSchedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,44 @@ public function getSchedule(): array {
* Callback to execute scheduled unpublication.
*/
public function callback(JobSchedule $job): void {
$config = $this->configFactory->get(SettingsForm::CONFIG_NAME);

$enabled = (boolean) $config->get('unpublish_enable');

// If the automatic unpublication is disabled, we will skip past this job.
// This should technically not happen, as updating the settings will trigger
// rescheduleAll() on all eventinstances, but this is a nice fallback.
if (!$enabled) {
return;
}

$event = $this->eventInstanceStorage->load($job->getId());
if (!$event || !$event instanceof EventInstance) {
throw new \UnexpectedValueException("Unable to load event instance {$job->getId()} for automatic unpublication");
}

$event->setUnpublished()->save();

// If all instances in the series are unpublished then also unpublish the
// series.
// Detect if site wishes series to be unpublished when all instances are
// unpublished.
$seriesUnpublishingEnabled = (boolean) $config->get('unpublish_series_enable');

if (!$seriesUnpublishingEnabled) {
return;
}

// Count the number of published eventinstances, and if it is 0, unpublish
// the series.
$eventSeries = $event->getEventSeries();
$allEventInstances = $eventSeries->get('event_instances')->referencedEntities();
$publishedInstances = array_filter($allEventInstances, function (EventInstance $event) {
return $event->isPublished();
});
if (empty($publishedInstances)) {

$publishedEventInstanceIds = ($this->eventInstanceStorage->getQuery())
->accessCheck(FALSE)
->condition('eventseries_id', $eventSeries->id())
->condition('status', 1)
->count()
->execute();

if (empty($publishedEventInstanceIds)) {
$eventSeries->setUnpublished()->save();
}
}
Expand All @@ -73,6 +96,7 @@ public function callback(JobSchedule $job): void {
*/
public function scheduleUnpublication(EventInstance $event): void {
$config = $this->configFactory->get(SettingsForm::CONFIG_NAME);
$enabled = (boolean) $config->get('unpublish_enable');
$schedule = (int) $config->get('unpublish_schedule');
$now_timestamp = $this->time->getCurrentTime();

Expand All @@ -93,9 +117,10 @@ public function scheduleUnpublication(EventInstance $event): void {

// Remove any preexisting job with the same name, type and id.
$this->jobScheduler->remove($job);

// If automatic unpublication is enabled and needed then schedule a new
// unpublication.
if ($schedule > 0 && $event->isPublished()) {
if ($enabled && $schedule > 0 && $event->isPublished()) {
$this->jobScheduler->set($job);

$this->logger->debug(
Expand All @@ -108,10 +133,10 @@ public function scheduleUnpublication(EventInstance $event): void {
/**
* Reschedule all event instances for unpublication.
*
* This will update schedules for all events even those that where not
* This will update schedules for all events even those that were not
* scheduled in the past.
*/
public function rescheduleAll(): void {
public function rescheduleAll(): int {
$this->jobScheduler->removeAll(self::JOB_SCHEDULE_NAME, self::JOB_SCHEDULE_TYPE);

$publishedEventInstanceIds = ($this->eventInstanceStorage->getQuery())
Expand All @@ -124,6 +149,8 @@ public function rescheduleAll(): void {
array_walk($publishedEventInstances, function (EventInstance $event) {
$this->scheduleUnpublication($event);
});

return count($publishedEventInstanceIds);
}

}

0 comments on commit d73743e

Please sign in to comment.