Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • project/recurring_events
  • issue/recurring_events-3183502
  • issue/recurring_events-3183463
  • issue/recurring_events-3183483
  • issue/recurring_events-3190526
  • issue/recurring_events-3191715
  • issue/recurring_events-3190833
  • issue/recurring_events-3188808
  • issue/recurring_events-3180479
  • issue/recurring_events-3122823
  • issue/recurring_events-3196649
  • issue/recurring_events-3196428
  • issue/recurring_events-3196702
  • issue/recurring_events-3196704
  • issue/recurring_events-3198532
  • issue/recurring_events-3164409
  • issue/recurring_events-3206960
  • issue/recurring_events-3115678
  • issue/recurring_events-3218496
  • issue/recurring_events-3207435
  • issue/recurring_events-3219082
  • issue/recurring_events-3217367
  • issue/recurring_events-3229514
  • issue/recurring_events-3231841
  • issue/recurring_events-3238591
  • issue/recurring_events-3282502
  • issue/recurring_events-3283128
  • issue/recurring_events-3240862
  • issue/recurring_events-3247034
  • issue/recurring_events-3071679
  • issue/recurring_events-3264621
  • issue/recurring_events-3266436
  • issue/recurring_events-3268690
  • issue/recurring_events-3269555
  • issue/recurring_events-3271328
  • issue/recurring_events-3272361
  • issue/recurring_events-3163804
  • issue/recurring_events-3297681
  • issue/recurring_events-3299575
  • issue/recurring_events-3300786
  • issue/recurring_events-3302916
  • issue/recurring_events-3304286
  • issue/recurring_events-3298679
  • issue/recurring_events-3309652
  • issue/recurring_events-3310360
  • issue/recurring_events-3311843
  • issue/recurring_events-3311712
  • issue/recurring_events-3312003
  • issue/recurring_events-3312084
  • issue/recurring_events-3312242
  • issue/recurring_events-3316080
  • issue/recurring_events-3295367
  • issue/recurring_events-3196417
  • issue/recurring_events-3309859
  • issue/recurring_events-3318590
  • issue/recurring_events-3244975
  • issue/recurring_events-3318998
  • issue/recurring_events-3321269
  • issue/recurring_events-3320512
  • issue/recurring_events-3321235
  • issue/recurring_events-3321550
  • issue/recurring_events-3322998
  • issue/recurring_events-3315836
  • issue/recurring_events-3324055
  • issue/recurring_events-3328907
  • issue/recurring_events-3318490
  • issue/recurring_events-3339288
  • issue/recurring_events-3345618
  • issue/recurring_events-3347935
  • issue/recurring_events-3362297
  • issue/recurring_events-3359696
  • issue/recurring_events-3318666
  • issue/recurring_events-3366907
  • issue/recurring_events-3366910
  • issue/recurring_events-3403064
  • issue/recurring_events-3404311
  • issue/recurring_events-3405567
  • issue/recurring_events-3376639
  • issue/recurring_events-3384836
  • issue/recurring_events-3382387
  • issue/recurring_events-3384389
  • issue/recurring_events-3315503
  • issue/recurring_events-3411229
  • issue/recurring_events-3415222
  • issue/recurring_events-3415308
  • issue/recurring_events-3172514
  • issue/recurring_events-3419694
  • issue/recurring_events-3178696
  • issue/recurring_events-3408924
  • issue/recurring_events-3447130
  • issue/recurring_events-3416436
  • issue/recurring_events-3451613
  • issue/recurring_events-3452632
  • issue/recurring_events-3453086
  • issue/recurring_events-3452641
  • issue/recurring_events-3454012
  • issue/recurring_events-3455716
  • issue/recurring_events-3456300
  • issue/recurring_events-3456641
  • issue/recurring_events-3462327
  • issue/recurring_events-3463467
  • issue/recurring_events-3463979
  • issue/recurring_events-3462480
  • issue/recurring_events-3464792
  • issue/recurring_events-3456045
  • issue/recurring_events-3468300
  • issue/recurring_events-3468521
  • issue/recurring_events-3475611
  • issue/recurring_events-3477247
  • issue/recurring_events-3477047
  • issue/recurring_events-3477650
  • issue/recurring_events-3257502
  • issue/recurring_events-3090186
  • issue/recurring_events-3478802
  • issue/recurring_events-3479449
  • issue/recurring_events-3479843
  • issue/recurring_events-3479860
  • issue/recurring_events-3480495
  • issue/recurring_events-3480500
  • issue/recurring_events-3480746
  • issue/recurring_events-3480973
  • issue/recurring_events-3481021
  • issue/recurring_events-3481722
  • issue/recurring_events-3482804
  • issue/recurring_events-3483283
  • issue/recurring_events-3484209
  • issue/recurring_events-3170156
  • issue/recurring_events-3484558
  • issue/recurring_events-3485904
  • issue/recurring_events-3485935
  • issue/recurring_events-3487412
  • issue/recurring_events-3496270
  • issue/recurring_events-3480508
  • issue/recurring_events-3499792
  • issue/recurring_events-3500920
  • issue/recurring_events-3510919
  • issue/recurring_events-3510942
  • issue/recurring_events-3478268
138 results
Show changes
Commits on Source (28)
Showing
with 766 additions and 25 deletions
......@@ -220,6 +220,7 @@ function recurring_events_registration_update_8005() {
'format' => 'enabled-disabled',
],
])
->setDefaultValue(0)
->setDisplayConfigurable('view', FALSE);
$definition_update_manager->installFieldStorageDefinition('status', 'registrant', 'registrant', $status);
}
......@@ -259,3 +260,100 @@ function recurring_events_registration_update_8006(&$sandbox) {
$sandbox['#finished'] = 1;
}
}
/**
* Update the event_registration field type to add unique email field.
*/
function recurring_events_registration_update_8007() {
$entity_type_manager = \Drupal::entityTypeManager();
$storage = $entity_type_manager->getStorage('eventseries');
$bundle_definition = $entity_type_manager->getDefinition('eventseries');
$id_key = $bundle_definition->getKey('id');
$revision_key = $bundle_definition->getKey('revision');
$table_name = $storage->getDataTable() ?: $storage->getBaseTable();
$revision_table_name = $storage->getRevisionDataTable() ?: $storage->getRevisionTable();
$database = \Drupal::database();
$definition_manager = \Drupal::entityDefinitionUpdateManager();
// We need to grab all the current data for registration settings for an event
// before we remove the registration field, before re-adding it with our new
// unique email field.
$reg_fields = [
'event_registration__value',
'event_registration__end_value',
'event_registration__registration',
'event_registration__registration_type',
'event_registration__registration_dates',
'event_registration__capacity',
'event_registration__waitlist',
'event_registration__instance_schedule_open',
'event_registration__instance_schedule_open_amount',
'event_registration__instance_schedule_open_units',
'event_registration__instance_schedule_close',
'event_registration__instance_schedule_close_amount',
'event_registration__instance_schedule_close_units',
'event_registration__instance_schedule_close_type',
];
$registration_values = $database->select($table_name)
->fields($table_name, [$id_key, $revision_key] + $reg_fields)
->execute()
->fetchAll();
// We need to NULL out all the current values, so that we can remove the field
// without warning.
$update_fields = array_fill_keys($reg_fields, NULL);
$database->update($table_name)
->fields($update_fields)
->execute();
// Remove the existing registration field.
$field_storage_definition = $definition_manager->getFieldStorageDefinition('event_registration', 'eventseries');
$definition_manager->uninstallFieldStorageDefinition($field_storage_definition);
// Create a new instance of the registration field and install it.
$storage_definition = BaseFieldDefinition::create('event_registration')
->setName('event_registration')
->setLabel(t('Event Registration'))
->setDescription('The event registration configuration.')
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE)
->setRevisionable(TRUE)
->setTranslatable(FALSE)
->setCardinality(1)
->setRequired(FALSE)
->setDisplayOptions('form', [
'type' => 'event_registration',
'weight' => 10,
]);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('event_registration', 'eventseries', 'eventseries', $storage_definition);
// Restore previously stored values.
if (!empty($registration_values)) {
foreach ($registration_values as $value) {
$values_to_restore = [
$id_key => $value->{$id_key},
$revision_key => $value->{$revision_key},
];
foreach ($reg_fields as $field) {
$values_to_restore[$field] = rtrim($value->{$field}, 's');
}
$values_to_restore['event_registration__unique_email_address'] = 0;
$database->update($table_name)
->fields($values_to_restore)
->condition($id_key, $value->{$id_key})
->execute();
$database->update($revision_table_name)
->fields($values_to_restore)
->condition($id_key, $value->{$id_key})
->execute();
}
}
}
......@@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Render\Element;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\recurring_events\Entity\EventSeries;
use Drupal\recurring_events\Entity\EventInstance;
use Drupal\Core\Url;
......@@ -75,6 +76,15 @@ function recurring_events_registration_theme() {
'template' => 'registrant',
];
$theme['eventinstance_register_button'] = [
'render element' => 'elements',
'template' => 'eventinstance-register-button',
'variables' => [
'entity' => NULL,
'attributes' => NULL,
],
];
return $theme;
}
......@@ -405,3 +415,52 @@ function recurring_events_registration_module_implements_alter(&$implementations
$implementations['recurring_events_registration'] = $group;
}
}
/**
* Implements hook_preprocess_field().
*/
function recurring_events_registration_preprocess_page_title(array &$variables) {
$route_name = \Drupal::routeMatch()->getRouteName();
if ($route_name === 'entity.registrant.add_form') {
$event_instance = \Drupal::routeMatch()->getParameters()->get('eventinstance');
if (!empty($event_instance) && !empty($event_instance->title->value)) {
$variables['title'] = $event_instance->title->value;
}
}
}
/**
* Implements hook_entity_extra_field_info().
*/
function recurring_events_registration_entity_extra_field_info() {
$extra = [];
$bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo('eventinstance');
if (!empty($bundles)) {
foreach ($bundles as $bundle_id => $bundle) {
$extra['eventinstance'][$bundle_id]['display']['register_button'] = [
'label' => t('Register button'),
'description' => t('Show a button linking to the registration page for this event instance'),
'weight' => 999,
];
}
}
return $extra;
}
/**
* Implements hook_ENTITY_NAME_view().
*/
function recurring_events_registration_eventinstance_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
if ($display->getComponent('register_button')) {
$build['register_button'] = [
'#theme' => 'eventinstance_register_button',
'#entity' => $entity,
'#attributes' => [
'class' => [
'button',
'eventinstance-register',
],
],
];
}
}
......@@ -262,6 +262,7 @@ class Registrant extends EditorialContentEntityBase implements RegistrantInterfa
],
'weight' => 120,
])
->setDefaultValue(0)
->setDisplayConfigurable('form', TRUE);
return $fields;
......
......@@ -218,7 +218,7 @@ class ContactForm extends FormBase {
foreach ($registrants as $registrant) {
$params['registrant'] = $registrant;
$to = $registrant->mail->value;
$to = $registrant->email->value;
$this->mail->mail('recurring_events_registration', 'custom', $to, $this->languageManager->getDefaultLanguage()->getId(), $params);
if ($registrant->getWaitlist() == '1') {
......
......@@ -105,7 +105,7 @@ class RegistrantForm extends ContentEntityForm {
$container->get('current_route_match'),
$container->get('entity_type.manager'),
$container->get('cache_tags.invalidator'),
$container->has('content_moderation.moderation_information') ? $container->get('content_moderation.moderation_information') : NULL,
$container->has('content_moderation.moderation_information') ? $container->get('content_moderation.moderation_information') : NULL
);
}
......@@ -312,6 +312,13 @@ class RegistrantForm extends ContentEntityForm {
// we need to prevent caching.
$form['#cache'] = ['max-age' => 0];
$form_state->setCached(FALSE);
$save_label = $this->t('Register');
if ($editing) {
$save_label = $this->t('Update Registration');
}
$form['actions']['submit']['#value'] = $save_label;
return $form;
}
......@@ -407,6 +414,17 @@ class RegistrantForm extends ContentEntityForm {
$entity->setWaitlist($form_state->getValue('add_to_waitlist'));
}
}
$unique_email_address = $this->creationService->registrationUniqueEmailAddress();
if ($unique_email_address) {
$email_address = $form_state->getValue('email');
$ignored_registrant_id = ($entity->isNew() ? NULL : (int) $entity->id());
$existing_registration_id = $this->creationService->hasUserRegisteredByEmail($email_address[0]['value'], $ignored_registrant_id);
if ($existing_registration_id) {
// If a registration already exists for the email display an error.
$form_state->setErrorByName('email', $this->t("You've already registered for this event."));
}
}
}
/**
......
......@@ -232,7 +232,7 @@ class RegistrantResendForm extends FormBase {
'registrant' => $this->registrant,
];
$to = $this->registrant->mail->value;
$to = $this->registrant->email->value;
$this->mail->mail('recurring_events_registration', 'custom', $to, $this->languageManager->getDefaultLanguage()->getId(), $params);
$this->messenger->addMessage($this->t('Registrant email successfully resent.'));
}
......
......@@ -89,6 +89,12 @@ class EventRegistration extends DateRangeItem {
'length' => 255,
];
$schema['columns']['unique_email_address'] = [
'type' => 'int',
'default' => 0,
'unsigned' => TRUE,
];
return $schema;
}
......@@ -108,12 +114,13 @@ class EventRegistration extends DateRangeItem {
$instance_schedule_close_amount = $this->get('instance_schedule_close_amount')->getValue();
$instance_schedule_close_units = $this->get('instance_schedule_close_units')->getValue();
$instance_schedule_close_type = $this->get('instance_schedule_close_type')->getValue();
$unique_email_address = $this->get('unique_email_address')->getValue();
return parent::isEmpty() && empty($registration) && empty($registration_type)
&& empty($registration_dates) && empty($capacity) && empty($waitlist)
&& empty($instance_schedule_open) && empty($instance_schedule_open_amount)
&& empty($instance_schedule_open_units) && empty($instance_schedule_close)
&& empty($instance_schedule_close_amount) && empty($instance_schedule_close_units)
&& empty($instance_schedule_close_type);
&& empty($instance_schedule_close_type) && empty($unique_email_address);
}
/**
......@@ -170,6 +177,10 @@ class EventRegistration extends DateRangeItem {
->setLabel(t('Instance Registration Close Type'))
->setDescription(t('Select when to close registrations (type).'));
$properties['unique_email_address'] = DataDefinition::create('boolean')
->setLabel(t('Restrict registration to once per email address?'))
->setDescription(t('Select whether to prevent a single email address from registering multiple times for the same event.'));
return $properties;
}
......
......@@ -77,11 +77,24 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#default_value' => $items[$delta]->registration ?: '',
];
$element['unique_email_address'] = [
'#type' => 'checkbox',
'#title' => $this->t('Restrict registration to once per email address?'),
'#description' => $this->t('Select this box to only allow an email address to register for an event one time.'),
'#weight' => 1,
'#default_value' => $items[$delta]->unique_email_address ?: '',
'#states' => [
'visible' => [
':input[name="event_registration[0][registration]"]' => ['checked' => TRUE],
],
],
];
$element['registration_type'] = [
'#type' => 'radios',
'#title' => $this->t('Registration Type'),
'#description' => $this->t('Select whether registrations are for the entire series, or for individual instances.'),
'#weight' => 1,
'#weight' => 2,
'#default_value' => $items[$delta]->registration_type ?: 'instance',
'#options' => [
'instance' => $this->t('Individual Event Registration'),
......@@ -98,7 +111,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#type' => 'radios',
'#title' => $this->t('Registration Dates'),
'#description' => $this->t('Choose between open or scheduled registration. Open registration ends when the event begins.'),
'#weight' => 2,
'#weight' => 3,
'#default_value' => $items[$delta]->registration_dates ?: 'open',
'#options' => [
'open' => $this->t('Open Registration'),
......@@ -114,7 +127,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
$element['series_registration'] = [
'#type' => 'fieldset',
'#title' => $this->t('Series Registration'),
'#weight' => 3,
'#weight' => 4,
'#states' => [
'visible' => [
':input[name="event_registration[0][registration]"]' => ['checked' => TRUE],
......@@ -135,7 +148,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
$element['instance_registration'] = [
'#type' => 'fieldset',
'#title' => $this->t('Instance Registration'),
'#weight' => 3,
'#weight' => 4,
'#states' => [
'visible' => [
':input[name="event_registration[0][registration]"]' => ['checked' => TRUE],
......@@ -260,7 +273,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#type' => 'number',
'#title' => $this->t('Total Number of Spaces Available'),
'#description' => $this->t('Maximum number of attendees available for each series, or individual event. Leave blank for unlimited.'),
'#weight' => 5,
'#weight' => 6,
'#default_value' => $items[$delta]->capacity ?: '',
'#min' => 0,
'#states' => [
......@@ -277,7 +290,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#type' => 'checkbox',
'#title' => $this->t('Enable Waiting List'),
'#description' => $this->t('Enable a waiting list if the number of registrations reaches capacity.'),
'#weight' => 6,
'#weight' => 7,
'#default_value' => $waitlist_default_value,
'#states' => [
'visible' => [
......@@ -364,6 +377,10 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
$item['instance_schedule_close_type'] = '';
}
if (empty($item['unique_email_address'])) {
$item['unique_email_address'] = 0;
}
}
$values = parent::massageFormValues($values, $form, $form_state);
return $values;
......
......@@ -672,4 +672,60 @@ class RegistrationCreationService {
return $token_help;
}
/**
* Has the email address registered for this event before.
*
* @param string $email
* The email address of the user.
* @param int $ignored_registrant_id
* The ID of the registrant to ignore during duplicate email checks.
*
* @return bool|int
* Return the registration ID if it exists otherwise return FALSE.
*/
public function hasUserRegisteredByEmail($email, $ignored_registrant_id = NULL) {
// Look through this series' registrations.
$existing_registration_id = FALSE;
$current_series_registrations = $this->retrieveAllSeriesRegisteredParties();
foreach ($current_series_registrations as $registration_id => $registration_record) {
if ($registration_id === $ignored_registrant_id) {
continue;
}
// Compare the event instance ID and email address.
if (($this->eventInstance->id() == $registration_record->get('eventinstance_id')->target_id)
&& ($this->cleanEmailAddress($email) == $this->cleanEmailAddress($registration_record->get('email')->value))) {
// Remember the existing registration ID and stop looking.
$existing_registration_id = $registration_id;
break;
}
}
return $existing_registration_id;
}
/**
* Clean the email address to handle plus- and dot-addressing.
*
* @param string $email
* The email address of the user.
*
* @return string
* The cleaned email address
*/
public function cleanEmailAddress($email) {
$email_address_parts = (isset($email) ? explode('@', $email) : ['', '']);
$email_address_clean = str_replace('.', '', $email_address_parts[0]) . '@' . $email_address_parts[1];
$email_address_clean = preg_replace('/\+.*@/', '@', $email_address_clean);
return (isset($email) ? strtolower($email_address_clean) : NULL);
}
/**
* Do email addresses have to be unique for this event?
*
* @return bool
* Whether or not unique email addresses are enforced for this event.
*/
public function registrationUniqueEmailAddress() {
return $this->eventSeries->event_registration->unique_email_address;
}
}
{#
/**
* @file
* Theme template to display an eventinstance register button.
*
* Available variables:
* - entity: The eventinstance entity.
* - attributes: HTML attributes for the containing element.
* The attributes.class element may contain one or more classes.
*/
#}
{% block content %}
<a {{ attributes }} href="{{ path('entity.registrant.add_form', {'eventinstance': entity.id.value}) }}">{{ 'Register'|t }}</a>
{% endblock %}
......@@ -70,6 +70,93 @@ display:
quantity: 9
style:
type: table
options:
grouping: { }
row_class: ''
default_row_class: true
override: true
sticky: false
caption: ''
summary: ''
description: ''
columns:
title: title
type: type
recur_type: recur_type
eventseries_instance_count: eventseries_instance_count
eventseries_start_date: eventseries_start_date
uid: uid
status: status
created: created
changed: changed
operations: operations
info:
title:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
type:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
recur_type:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
eventseries_instance_count:
align: ''
separator: ''
empty_column: false
responsive: ''
eventseries_start_date:
align: ''
separator: ''
empty_column: false
responsive: ''
uid:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
status:
sortable: true
default_sort_order: desc
align: ''
separator: ''
empty_column: false
responsive: ''
created:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
changed:
sortable: true
default_sort_order: desc
align: ''
separator: ''
empty_column: false
responsive: ''
operations:
align: ''
separator: ''
empty_column: false
responsive: ''
default: changed
empty_table: false
row:
type: fields
fields:
......@@ -998,6 +1085,21 @@ display:
entity_field: status
plugin_id: boolean
sorts:
changed:
id: changed
table: eventseries_field_data
field: changed
relationship: none
group_type: group
admin_label: ''
order: DESC
exposed: false
expose:
label: ''
granularity: second
entity_type: eventseries
entity_field: changed
plugin_id: date
title:
id: title
table: eventseries_field_data
......
......@@ -573,27 +573,29 @@ function recurring_events_recurring_events_event_instances_pre_create_alter(arra
if (!empty($include)) {
foreach ($event_instances as $key => $dates) {
$include_event = FALSE;
$start = $dates['start_date']->getTimestamp();
$end = $dates['end_date']->getTimestamp();
for ($x = 0; $x < (count($include) - 1); $x++) {
for ($x = 0; $x <= (count($include) - 1); $x++) {
$included_start = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $include[$x]['value'] . 'T00:00:00');
$included_start = $included_start->getTimestamp();
$included_end = DrupalDateTime::createFromFormat(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $include[$x]['end_value'] . 'T23:59:59');
$included_end = $included_end->getTimestamp();
if ($start >= $included_start && $start <= $included_end && $end >= $included_start && $end <= $included_end) {
$include_event = TRUE;
// This event is in the inclusion range, so move on to the next one.
break;
}
}
if ($x == (count($include) - 1)) {
$messenger->addMessage(t('Skipping non-included date: @start_date - @end_date', [
'@start_date' => $dates['start_date']->format($config->get('date_format')),
'@end_date' => $dates['end_date']->format($config->get('date_format')),
]));
unset($event_instances[$key]);
}
if (!$include_event) {
$messenger->addMessage(t('Skipping non-included date: @start_date - @end_date', [
'@start_date' => $dates['start_date']->format($config->get('date_format')),
'@end_date' => $dates['end_date']->format($config->get('date_format')),
]));
unset($event_instances[$key]);
}
}
}
......@@ -603,7 +605,7 @@ function recurring_events_recurring_events_event_instances_pre_create_alter(arra
* Implements callback_allowed_values_function().
*/
function recurring_events_allowed_values_function(FieldStorageDefinitionInterface $definition, FieldableEntityInterface $entity = NULL) {
$values = ['custom' => t('Custom Event')];
$values = ['custom' => t('Custom/Single Event')];
$fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('eventseries');
foreach ($fields as $field) {
$field_definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field->getType());
......
......@@ -21,6 +21,7 @@ entity.eventseries.add_form:
requirements:
_entity_create_access: 'eventseries'
options:
_recurring_events_operation_route: TRUE
parameters:
eventseries_type:
type: entity:eventseries_type
......@@ -33,6 +34,8 @@ entity.eventseries.add_page:
_controller: '\Drupal\recurring_events\Controller\EventSeriesController::addPage'
requirements:
_entity_create_access: 'eventseries'
options:
_recurring_events_operation_route: TRUE
# Edit an EventSeries.
entity.eventseries.edit_form:
......@@ -44,6 +47,7 @@ entity.eventseries.edit_form:
_entity_access: 'eventseries.edit'
eventseries: \d+
options:
_recurring_events_operation_route: TRUE
parameters:
eventseries:
type: entity:eventseries
......@@ -58,6 +62,7 @@ entity.eventseries.delete_form:
_entity_access: 'eventseries.delete'
eventseries: \d+
options:
_recurring_events_operation_route: TRUE
parameters:
eventseries:
type: entity:eventseries
......@@ -72,6 +77,7 @@ entity.eventseries.clone_form:
_entity_access: 'eventseries.clone'
eventseries: \d+
options:
_recurring_events_operation_route: TRUE
parameters:
eventseries:
type: entity:eventseries
......@@ -111,6 +117,7 @@ entity.eventinstance.edit_form:
_entity_access: 'eventinstance.edit'
eventinstance: \d+
options:
_recurring_events_operation_route: TRUE
parameters:
eventinstance:
type: entity:eventinstance
......@@ -125,6 +132,7 @@ entity.eventinstance.delete_form:
_entity_access: 'eventinstance.delete'
eventinstance: \d+
options:
_recurring_events_operation_route: TRUE
parameters:
eventinstance:
type: entity:eventinstance
......@@ -139,6 +147,7 @@ entity.eventinstance.clone_form:
requirements:
_entity_access: 'eventinstance.clone'
options:
_recurring_events_operation_route: TRUE
parameters:
eventinstance:
type: entity:eventinstance
......
services:
recurring_events.admin_path.route_subscriber:
class: Drupal\recurring_events\EventSubscriber\RecurringEventsAdminRouteSubscriber
arguments: ['@config.factory', '@router.builder']
tags:
- { name: event_subscriber }
recurring_events.event_creation_service:
class: Drupal\recurring_events\EventCreationService
arguments: ['@string_translation', '@database', '@logger.factory', '@messenger', '@plugin.manager.field.field_type', '@entity_field.manager', '@module_handler', '@entity_type.manager', '@keyvalue']
......@@ -666,7 +666,7 @@ class EventCreationService {
}
}
$recur_fields['custom'] = $this->t('Custom Event');
$recur_fields['custom'] = $this->t('Custom/Single Event');
if ($allow_alter) {
$this->moduleHandler->alter('recurring_events_recur_field_types', $recur_fields);
}
......
<?php
namespace Drupal\recurring_events\EventSubscriber;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Routing\RouteBuilderInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Sets the _admin_route for specific recurring events related routes.
*/
class RecurringEventsAdminRouteSubscriber extends RouteSubscriberBase {
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The router builder.
*
* @var \Drupal\Core\Routing\RouteBuilderInterface
*/
protected $routerBuilder;
/**
* Constructs a new RecurringEventsAdminRouteSubscriber.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\Routing\RouteBuilderInterface $router_builder
* The router builder service.
*/
public function __construct(ConfigFactoryInterface $config_factory, RouteBuilderInterface $router_builder) {
$this->configFactory = $config_factory;
$this->routerBuilder = $router_builder;
}
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
if ($this->configFactory->get('node.settings')->get('use_admin_theme')) {
foreach ($collection->all() as $route) {
if ($route->hasOption('_recurring_events_operation_route')) {
$route->setOption('_admin_route', TRUE);
}
}
}
}
/**
* Rebuilds the router when node.settings:use_admin_theme is changed.
*
* @param \Drupal\Core\Config\ConfigCrudEvent $event
* The config crud event that gets fired.
*/
public function onConfigSave(ConfigCrudEvent $event) {
if ($event->getConfig()->getName() === 'node.settings' && $event->isChanged('use_admin_theme')) {
$this->routerBuilder->setRebuildNeeded();
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = parent::getSubscribedEvents();
$events[ConfigEvents::SAVE][] = ['onConfigSave', 0];
return $events;
}
}
......@@ -9,6 +9,8 @@ use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Url;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
/**
* Provides a form for deleting an eventinstance entity.
......@@ -43,13 +45,17 @@ class EventInstanceDeleteForm extends ContentEntityDeleteForm {
*
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository service.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info interface.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time interface.
* @param \Drupal\Core\Messenger\Messenger $messenger
* The messenger service.
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* The date formatter service.
*/
public function __construct(EntityRepositoryInterface $entity_repository, Messenger $messenger, DateFormatter $date_formatter) {
parent::__construct($entity_repository);
public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL, Messenger $messenger, DateFormatter $date_formatter) {
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
$this->messenger = $messenger;
$this->dateFormatter = $date_formatter;
}
......@@ -60,6 +66,8 @@ class EventInstanceDeleteForm extends ContentEntityDeleteForm {
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.repository'),
$container->get('entity_type.bundle.info'),
$container->get('datetime.time'),
$container->get('messenger'),
$container->get('date.formatter')
);
......
......@@ -10,6 +10,8 @@ use Drupal\Core\Url;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Render\Renderer;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
/**
* Provides a form for deleting an eventseries entity.
......@@ -52,6 +54,8 @@ class EventSeriesDeleteForm extends ContentEntityDeleteForm {
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.repository'),
$container->get('entity_type.bundle.info'),
$container->get('datetime.time'),
$container->get('messenger'),
$container->get('renderer'),
$container->get('config.factory')
......@@ -63,6 +67,10 @@ class EventSeriesDeleteForm extends ContentEntityDeleteForm {
*
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository service.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info interface.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time interface.
* @param \Drupal\Core\Messenger\Messenger $messenger
* The messenger service.
* @param \Drupal\Core\Render\Renderer $renderer
......@@ -70,11 +78,11 @@ class EventSeriesDeleteForm extends ContentEntityDeleteForm {
* @param \Drupal\Core\Config\ConfigFactory $config
* The config factory service.
*/
public function __construct(EntityRepositoryInterface $entity_repository, Messenger $messenger, Renderer $renderer, ConfigFactory $config) {
public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL, Messenger $messenger, Renderer $renderer, ConfigFactory $config) {
$this->messenger = $messenger;
$this->renderer = $renderer;
$this->config = $config;
parent::__construct($entity_repository);
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
}
/**
......@@ -115,7 +123,7 @@ class EventSeriesDeleteForm extends ContentEntityDeleteForm {
foreach ($instances as $instance) {
$date = $instance->date->start_date;
$date->setTimezone($timezone);
$options[] = $instance->toLink($date->format($this->config->get('recurring_events_registration.registrant.config')->get('date_format')));
$options[] = $instance->toLink($date->format($this->config->get('recurring_events.eventseries.config')->get('date_format')));
}
$description['instances'] = [
......
......@@ -184,6 +184,17 @@ class EventSeriesForm extends ContentEntityForm {
],
];
$form['included_dates']['#states'] = [
'visible' => [
':input[name="recur_type"]' => ['!value' => 'custom'],
],
];
$form['excluded_dates']['#states'] = [
'visible' => [
':input[name="recur_type"]' => ['!value' => 'custom'],
],
];
// Get all the available recur type fields. Suppress altering so that we can
// get a list of all the fields, so that after we alter, we can remove the
// necessary fields from the entity form.
......
<?php
namespace Drupal\recurring_events\Plugin\Field\FieldFormatter;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\daterange_compact\DateRangeFormatterInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Implementation of the 'recurring events eventinstance datecompact' formatter.
*
* @FieldFormatter(
* id = "recurring_events_eventinstance_datecompact",
* label = @Translation("EventInstance Date Compact"),
* description = @Translation("Display the date of the referenced eventinstance using Date Compact."),
* field_types = {
* "entity_reference"
* }
* )
*/
class EventInstanceDateCompactFormatter extends EntityReferenceFormatterBase {
use StringTranslationTrait;
/**
* The date range formatter service.
*
* @var \Drupal\daterange_compact\DateRangeFormatterInterface
*/
protected $dateRangeFormatter;
/**
* The date range format entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $dateRangeFormatStorage;
/**
* Constructs a new DateRangeCompactFormatter.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Third party settings.
* @param \Drupal\daterange_compact\DateRangeFormatterInterface $date_range_formatter
* The date formatter service.
* @param \Drupal\Core\Entity\EntityStorageInterface $date_range_format_storage
* The date format entity storage.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateRangeFormatterInterface $date_range_formatter, EntityStorageInterface $date_range_format_storage) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateRangeFormatter = $date_range_formatter;
$this->dateRangeFormatStorage = $date_range_format_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('daterange_compact.date_range.formatter'),
$container->get('entity_type.manager')->getStorage('date_range_format')
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return [
'link' => TRUE,
'format_type' => 'medium',
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements['link'] = [
'#title' => $this->t('Link date to the referenced entity'),
'#type' => 'checkbox',
'#default_value' => $this->getSetting('link'),
];
$format_types = $this->dateRangeFormatStorage->loadMultiple();
$options = [];
foreach ($format_types as $type => $type_info) {
$options[$type] = $type_info->label();
}
$elements['format_type'] = [
'#type' => 'select',
'#title' => $this->t('Date and time range format'),
'#description' => $this->t("Choose a format for displaying the date and time range."),
'#options' => $options,
'#default_value' => $this->getSetting('format_type'),
];
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = [];
$summary[] = $this->getSetting('link') ? $this->t('Link to the referenced entity') : $this->t('No link');
$summary[] = $this->t('Format: @format', ['@format' => $this->getSetting('format_type')]);
return $summary;
}
/**
* {@inheritdoc}
*/
public static function isApplicable(FieldDefinitionInterface $field_definition) {
if ($field_definition->getFieldStorageDefinition()->getSetting('target_type') == 'eventinstance') {
$moduleHandler = \Drupal::service('module_handler');
return ($moduleHandler->moduleExists('daterange_compact'));
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$output_as_link = $this->getSetting('link');
foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
$date_string = '';
$user_timezone = new \DateTimeZone(date_default_timezone_get());
if (!empty($entity->date->start_date) && !empty($entity->date->end_date)) {
/** @var \Drupal\Core\Datetime\DrupalDateTime $start_date */
$start_date = $entity->date->start_date;
/** @var \Drupal\Core\Datetime\DrupalDateTime $end_date */
$end_date = $entity->date->end_date;
$start_date->setTimezone($user_timezone);
$end_date->setTimezone($user_timezone);
$start_timestamp = $start_date->getTimestamp();
$end_timestamp = $end_date->getTimestamp();
$format = $this->getSetting('format_type');
if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) {
$timezone = DateTimeItemInterface::STORAGE_TIMEZONE;
$date_string = $this->dateRangeFormatter->formatDateRange($start_timestamp, $end_timestamp, $format, $timezone);
}
else {
$timezone = date_default_timezone_get();
$date_string = $this->dateRangeFormatter->formatDateTimeRange($start_timestamp, $end_timestamp, $format, $timezone);
}
}
// If the link is to be displayed and the entity has a uri, display a
// link.
if ($output_as_link && !$entity->isNew()) {
try {
$uri = $entity->toUrl();
}
catch (UndefinedLinkTemplateException $e) {
// This exception is thrown by \Drupal\Core\Entity\Entity::urlInfo()
// and it means that the entity type doesn't have a link template nor
// a valid "uri_callback", so don't bother trying to output a link for
// the rest of the referenced entities.
$output_as_link = FALSE;
}
}
if ($output_as_link && isset($uri) && !$entity->isNew()) {
$elements[$delta] = [
'#type' => 'link',
'#title' => $date_string,
'#url' => $uri,
'#options' => $uri->getOptions(),
'#eventinstance' => $entity,
'#cache' => [
'contexts' => [
'timezone',
],
],
];
if (!empty($items[$delta]->_attributes)) {
$elements[$delta]['#options'] += ['attributes' => []];
$elements[$delta]['#options']['attributes'] += $items[$delta]->_attributes;
// Unset field item attributes since they have been included in the
// formatter output and shouldn't be rendered in the field template.
unset($items[$delta]->_attributes);
}
}
else {
$elements[$delta] = [
'#plain_text' => $date_string,
'#eventinstance' => $entity,
];
}
$elements[$delta]['#cache']['tags'] = $entity->getCacheTags();
}
usort($elements, function ($a, $b) {
$a_date = $a['#eventinstance']->date->start_date->getTimestamp();
$b_date = $b['#eventinstance']->date->start_date->getTimestamp();
if ($a_date == $b_date) {
return 0;
}
return ($a_date < $b_date) ? -1 : 1;
});
return $elements;
}
}