From 9bafdc8e4f62adbb11a03a2f84e5d634a7f2dd62 Mon Sep 17 00:00:00 2001 From: Ion Eftodii Date: Wed, 4 Sep 2024 15:41:01 +0300 Subject: [PATCH 01/10] YOR-11: Create Events Calendar Functional --- .../ViewsBasicDefaultFormatter.php | 65 +++-- .../src/Service/EventsCalendar.php | 268 ++++++++++++++++++ .../src/Service/EventsCalendarInterface.php | 102 +++++++ .../ys_views_basic/src/ViewsBasicManager.php | 5 + .../views-basic-events-calendar.html.twig | 5 + .../ys_views_basic/ys_views_basic.module | 5 + .../ys_views_basic.services.yml | 4 + 7 files changed, 432 insertions(+), 22 deletions(-) create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/templates/views-basic-events-calendar.html.twig diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php index 3428c952e7..f1a2986836 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php @@ -7,6 +7,7 @@ use Drupal\Core\Field\FormatterBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Render\Renderer; +use Drupal\ys_views_basic\Service\EventsCalendarInterface; use Drupal\ys_views_basic\ViewsBasicManager; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -37,6 +38,13 @@ class ViewsBasicDefaultFormatter extends FormatterBase implements ContainerFacto */ protected $rendererService; + /** + * The Events Calendar service. + * + * @var \Drupal\ys_views_basic\Service\EventsCalendarInterface + */ + protected EventsCalendarInterface $eventsCalendar; + /** * Constructs an views basic default formatter object. * @@ -54,14 +62,16 @@ class ViewsBasicDefaultFormatter extends FormatterBase implements ContainerFacto * The view mode. * @param array $third_party_settings * Any third party settings. - * @param \Drupal\ys_views_basic\Plugin\ViewsBasicManager $viewsBasicManager + * @param \Drupal\ys_views_basic\ViewsBasicManager $viewsBasicManager * The views basic manager service. * @param \Drupal\Core\Render\Renderer $renderer_service * Drupal Core renderer service. + * @param \Drupal\ys_views_basic\Service\EventsCalendarInterface $eventsCalendar + * The Events Calendar service. */ public function __construct( string $plugin_id, - $plugin_definition, + $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, string $label, @@ -69,6 +79,7 @@ public function __construct( array $third_party_settings, ViewsBasicManager $viewsBasicManager, Renderer $renderer_service, + EventsCalendarInterface $eventsCalendar, ) { parent::__construct( $plugin_id, @@ -81,17 +92,13 @@ public function __construct( $this->rendererService = $renderer_service, ); $this->viewsBasicManager = $viewsBasicManager; + $this->eventsCalendar = $eventsCalendar; } /** * {@inheritdoc} */ - public static function create( - ContainerInterface $container, - array $configuration, - $plugin_id, - $plugin_definition, - ) { + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $plugin_id, $plugin_definition, @@ -102,6 +109,7 @@ public static function create( $configuration['third_party_settings'], $container->get('ys_views_basic.views_basic_manager'), $container->get('renderer'), + $container->get('ys_views_basic.events_calendar') ); } @@ -111,23 +119,36 @@ public static function create( public function viewElements(FieldItemListInterface $items, $langcode) { $elements = []; + foreach ($items as $delta => $item) { + // Get decoded parameters. + $paramsDecoded = json_decode($item->getValue()['params'], TRUE); - $view = $this->viewsBasicManager->getView('rendered', $item->getValue()['params']); + if ($paramsDecoded['filters']['types'][0] === 'event' && $paramsDecoded['view_mode'] === 'calendar') { + $events_calendar = $this->eventsCalendar + ->getCalendar(date('m'), date('Y')); - $elements[$delta] = [ - '#theme' => 'views_basic_formatter_default', - '#view' => $view, - // Extract exposed filters from the view and place them separately. - // This is necessary because we are conditionally displaying - // specific exposed filters based on field configuration. - // By placing the exposed filters outside of the view rendering - // context, we ensure that they do not get re-rendered - // when AJAX operations are performed on the view, - // allowing for better control over which filters are displayed - // and maintaining the expected user interface behavior. - '#exposed' => $view['#view']->exposed_widgets, - ]; + $elements[$delta] = [ + '#theme' => 'views_basic_events_calendar', + '#month_data' => $events_calendar, + ]; + } + else { + $view = $this->viewsBasicManager->getView('rendered', $item->getValue()['params']); + $elements[$delta] = [ + '#theme' => 'views_basic_formatter_default', + '#view' => $view, + // Extract exposed filters from the view and place them separately. + // This is necessary because we are conditionally displaying + // specific exposed filters based on field configuration. + // By placing the exposed filters outside of the view rendering + // context, we ensure that they do not get re-rendered + // when AJAX operations are performed on the view, + // allowing for better control over which filters are displayed + // and maintaining the expected user interface behavior. + '#exposed' => $view['#view']->exposed_widgets, + ]; + } } return $elements; diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php new file mode 100644 index 0000000000..179c0586e0 --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php @@ -0,0 +1,268 @@ +entityTypeManager = $entity_type_manager; + $this->aliasManager = $alias_manager; + $this->nodeStorage = $entity_type_manager->getStorage('node'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container): static { + return new static( + $container->get('entity_type.manager'), + $container->get('path_alias.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getCalendar(string $month, string $year): array { + // Create a date object for the first day of the given month and year. + $firstDayOfMonth = new DrupalDateTime("$year-$month-01"); + $totalDaysInMonth = (int) $firstDayOfMonth->format('t'); + $startDayOfWeek = (int) $firstDayOfMonth->format('w'); + $lastDayOfMonth = new DrupalDateTime("$year-$month-$totalDaysInMonth"); + $endDayOfWeek = (int) $lastDayOfMonth->format('w'); + + $paddingStart = $startDayOfWeek; + $paddingEnd = 6 - $endDayOfWeek; + $totalCells = $totalDaysInMonth + $paddingStart + $paddingEnd; + $totalRows = (int) ceil($totalCells / 7); + $calendarRows = []; + + // Calculate the previous month and year. + $previousMonthDate = clone $firstDayOfMonth; + $previousMonthDate->modify('-1 month'); + $daysInPreviousMonth = (int) $previousMonthDate->format('t'); + $previousMonth = $previousMonthDate->format('m'); + $previousYear = $previousMonthDate->format('Y'); + + // Calculate the next month and year. + $nextMonthDate = clone $lastDayOfMonth; + $nextMonthDate->modify('+1 month'); + $nextMonth = $nextMonthDate->format('m'); + $nextYear = $nextMonthDate->format('Y'); + + // Load all events for the given month and year. + $monthlyEvents = $this->loadMonthlyEvents($month, $year); + + $currentDay = 1; + + for ($row = 0; $row < $totalRows; $row++) { + $calendarRows[$row] = []; + for ($cell = 0; $cell < 7; $cell++) { + if ($row == 0 && $cell < $paddingStart) { + // Fill in days from the previous month. + $day = $daysInPreviousMonth - ($paddingStart - $cell - 1); + $calendarRows[$row][] = $this->createCalendarCell($day, $previousMonth, $previousYear, $monthlyEvents); + } + elseif ($row == $totalRows - 1 && $cell > $endDayOfWeek) { + // Fill in days from the next month. + $day = $cell - $endDayOfWeek; + $calendarRows[$row][] = $this->createCalendarCell($day, $nextMonth, $nextYear, $monthlyEvents); + } + else { + // Normal date cell within the current month. + $calendarRows[$row][] = $this->createCalendarCell($currentDay, $month, $year, $monthlyEvents); + $currentDay++; + } + } + } + + return $calendarRows; + } + + /** + * {@inheritdoc} + */ + public function createCalendarCell(int $day, string $month, string $year, array $events): array { + return [ + 'date' => [ + 'day' => str_pad($day, 2, '0', STR_PAD_LEFT), + 'month' => $month, + 'year' => $year, + ], + 'events' => $this->getEvents($day, $month, $year, $events), + ]; + } + + /** + * {@inheritdoc} + */ + public function getEvents(int $day, string $month, string $year, array $events): array { + $startDate = new DrupalDateTime("$year-$month-$day 00:00:00"); + $endDate = new DrupalDateTime("$year-$month-$day 23:59:59"); + + $startTimestamp = $startDate->getTimestamp(); + $endTimestamp = $endDate->getTimestamp(); + + $events_data = []; + foreach ($events as $event) { + if (!$event->get('field_event_date')->isEmpty()) { + // Handle recurrence rules if present. + if ($event->field_event_date?->rrule) { + + /** @var \Drupal\smart_date_recur\Entity\SmartDateRule $rule */ + $rule = $this->entityTypeManager->getStorage('smart_date_rule') + ->load($event->field_event_date->rrule); + + if ($rule instanceof SmartDateRule) { + // Iterate over the stored instances to find occurrences for the + // current day. + foreach ($rule->getStoredInstances() as $instance) { + $instanceStartTimestamp = $instance['value']; + $instanceEndTimestamp = $instance['end_value']; + + // Check if the instance overlaps with the current day. + if ($instanceStartTimestamp <= $endTimestamp && $instanceEndTimestamp >= $startTimestamp) { + $time = $this->isAllDay($instanceStartTimestamp, $instanceEndTimestamp) + ? 'All Day' + : date('g:iA', $instanceStartTimestamp) . ' to ' . date('g:iA', $instanceEndTimestamp); + + $events_data[] = $this->createEventArray($event, $time, $instanceStartTimestamp); + } + } + } + } + else { + // Iterate through the nodes to extract event details. + foreach ($event->get('field_event_date')->getValue() as $eventDate) { + $eventStartTimestamp = $eventDate['value']; + $eventEndTimestamp = $eventDate['end_value']; + + // Check if the event overlaps with the current day. + if ($eventStartTimestamp <= $endTimestamp && $eventEndTimestamp >= $startTimestamp) { + $time = $this->isAllDay($eventStartTimestamp, $eventEndTimestamp) + ? 'All Day' + : date('g:iA', $eventStartTimestamp) . ' to ' . date('g:iA', $eventEndTimestamp); + + // Add event to the list if it overlaps with the current day. + $events_data[] = $this->createEventArray($event, $time, $eventStartTimestamp); + } + } + } + } + } + + // Sort events by the start timestamp. + usort($events_data, function ($a, $b) { + return $a['timestamp'] <=> $b['timestamp']; + }); + + return $events_data; + } + + /** + * {@inheritdoc} + */ + public function createEventArray($node, string $time, int $timestamp): array { + // Extract the event's categories. + $categories = implode(' | ', array_map(function ($term) { + return $term->label(); + }, $node->get('field_category')->referencedEntities())); + + // Extract the event's tags. + $tags = implode(' | ', array_map(function ($term) { + return $term->label(); + }, $node->get('field_tags')->referencedEntities())); + + // Build and return the event array. + return [ + 'category' => $categories, + 'title' => $node->label(), + 'url' => $this->aliasManager->getAliasByPath('/node/' . $node->id()), + 'time' => $time, + 'type' => $tags, + 'timestamp' => $timestamp, + ]; + } + + /** + * {@inheritdoc} + */ + public function isAllDay(int $start_ts, int $end_ts, string $timezone = NULL): bool { + if ($timezone) { + $default_tz = date_default_timezone_get(); + date_default_timezone_set($timezone); + } + + $temp_start = date('H:i', $start_ts); + $temp_end = date('H:i', $end_ts); + + if ($timezone) { + date_default_timezone_set($default_tz); + } + + return $temp_start == '00:00' && $temp_end == '23:59'; + } + + /** + * {@inheritdoc} + */ + public function loadMonthlyEvents(string $month, string $year): array { + $startDate = new DrupalDateTime("$year-$month-01 00:00:00"); + $endDate = clone $startDate; + $endDate->modify('last day of this month 23:59:59'); + + // Query to fetch event nodes that overlap with the given month. + $query = $this->nodeStorage->getQuery() + ->accessCheck() + ->condition('type', 'event') + ->condition('status', 1) + ->condition('field_event_date.value', $endDate->getTimestamp(), '<=') + ->condition('field_event_date.end_value', $startDate->getTimestamp(), '>='); + + $nids = $query->execute(); + return $this->nodeStorage->loadMultiple($nids); + } + +} diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php new file mode 100644 index 0000000000..b97fd40b27 --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php @@ -0,0 +1,102 @@ + '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/display-type-condensed.svg', 'img_alt' => 'Icon showing 3 generic list items one on top of the other with no images on the items.', ], + 'calendar' => [ + 'label' => 'Calendar', + 'img' => '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/content-type-event.svg', + 'img_alt' => 'Calendar', + ], ], 'sort_by' => [ 'field_publish_date:DESC' => 'Publish Date - newer first', diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/templates/views-basic-events-calendar.html.twig b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/templates/views-basic-events-calendar.html.twig new file mode 100644 index 0000000000..7d089c2a19 --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/templates/views-basic-events-calendar.html.twig @@ -0,0 +1,5 @@ +{{ attach_library('atomic/calendar') }} + +{% include "@organisms/calendar/yds-calendar.twig" with { + month:month_data +} %} diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module index 1ceecec9d0..1718c5664e 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module @@ -30,6 +30,11 @@ function ys_views_basic_theme($existing, $type, $theme, $path): array { 'contentType' => NULL, ], ], + 'views_basic_events_calendar' => [ + 'variables' => [ + 'month_data' => [], + ], + ], ]; } diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml index 4039bf8a2f..0a37a9f47d 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml @@ -3,3 +3,7 @@ services: ys_views_basic.views_basic_manager: class: Drupal\ys_views_basic\ViewsBasicManager arguments: ['@entity_type.manager', '@entity_display.repository'] + + ys_views_basic.events_calendar: + class: Drupal\ys_views_basic\Service\EventsCalendar + arguments: ['@entity_type.manager', '@path_alias.manager'] From 534708ced817839bfcfe40ee959ba02af8e576f0 Mon Sep 17 00:00:00 2001 From: Ion Eftodii Date: Wed, 4 Sep 2024 15:46:01 +0300 Subject: [PATCH 02/10] YOR-11: Remove the unused renderer service --- .../ViewsBasicDefaultFormatter.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php index f1a2986836..10119a4599 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php @@ -6,7 +6,6 @@ use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FormatterBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\Core\Render\Renderer; use Drupal\ys_views_basic\Service\EventsCalendarInterface; use Drupal\ys_views_basic\ViewsBasicManager; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -29,14 +28,7 @@ class ViewsBasicDefaultFormatter extends FormatterBase implements ContainerFacto * * @var \Drupal\ys_views_basic\ViewsBasicManager */ - protected $viewsBasicManager; - - /** - * The renderer service. - * - * @var \Drupal\Core\Render\Renderer - */ - protected $rendererService; + protected ViewsBasicManager $viewsBasicManager; /** * The Events Calendar service. @@ -64,21 +56,18 @@ class ViewsBasicDefaultFormatter extends FormatterBase implements ContainerFacto * Any third party settings. * @param \Drupal\ys_views_basic\ViewsBasicManager $viewsBasicManager * The views basic manager service. - * @param \Drupal\Core\Render\Renderer $renderer_service - * Drupal Core renderer service. * @param \Drupal\ys_views_basic\Service\EventsCalendarInterface $eventsCalendar * The Events Calendar service. */ public function __construct( string $plugin_id, - $plugin_definition, + $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, string $label, string $view_mode, array $third_party_settings, ViewsBasicManager $viewsBasicManager, - Renderer $renderer_service, EventsCalendarInterface $eventsCalendar, ) { parent::__construct( @@ -89,7 +78,6 @@ public function __construct( $label, $view_mode, $third_party_settings, - $this->rendererService = $renderer_service, ); $this->viewsBasicManager = $viewsBasicManager; $this->eventsCalendar = $eventsCalendar; @@ -108,7 +96,6 @@ public static function create(ContainerInterface $container, array $configuratio $configuration['view_mode'], $configuration['third_party_settings'], $container->get('ys_views_basic.views_basic_manager'), - $container->get('renderer'), $container->get('ys_views_basic.events_calendar') ); } @@ -116,7 +103,7 @@ public static function create(ContainerInterface $container, array $configuratio /** * Define how the field type is showed. */ - public function viewElements(FieldItemListInterface $items, $langcode) { + public function viewElements(FieldItemListInterface $items, $langcode): array { $elements = []; From d2a265650d1bd1dd432b97cd11a0552e9b6a3aba Mon Sep 17 00:00:00 2001 From: Rodica Ciorba Date: Fri, 6 Sep 2024 18:16:24 +0300 Subject: [PATCH 03/10] YOR-11: Deliver tags as an array in the calendar templates --- .../custom/ys_views_basic/src/Service/EventsCalendar.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php index 179c0586e0..2eb2331858 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php @@ -211,9 +211,7 @@ public function createEventArray($node, string $time, int $timestamp): array { }, $node->get('field_category')->referencedEntities())); // Extract the event's tags. - $tags = implode(' | ', array_map(function ($term) { - return $term->label(); - }, $node->get('field_tags')->referencedEntities())); + $tags = array_map(fn($term) => $term->label(), $node->get('field_tags')->referencedEntities()); // Build and return the event array. return [ From 4b72ecd0530bb966e1f536c17e924e8e0077c56a Mon Sep 17 00:00:00 2001 From: Constantin Marjina Date: Wed, 11 Sep 2024 18:06:04 +0300 Subject: [PATCH 04/10] YOR-11: Add ajax events controller --- .../Controller/EventsCalendarController.php | 66 +++++++++++++++++++ .../ViewsBasicDefaultFormatter.php | 11 ++++ .../ys_views_basic/src/ViewsBasicManager.php | 10 +-- .../ys_views_basic/ys_views_basic.routing.yml | 7 ++ 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Controller/EventsCalendarController.php create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.routing.yml diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Controller/EventsCalendarController.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Controller/EventsCalendarController.php new file mode 100644 index 0000000000..e43e8968db --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Controller/EventsCalendarController.php @@ -0,0 +1,66 @@ +get('ys_views_basic.events_calendar'), + ); + } + + /** + * Builds the response. + */ + public function __invoke(Request $request): AjaxResponse { + $response = new AjaxResponse(); + + if (!$request->request->has('calendar_id') || !$request->request->has('month') || !$request->request->has('year')) { + return $response; + } + + // Calendar wrapper that needs to be updated. + $calendar_id = $request->request->get('calendar_id'); + $month = $request->request->get('month'); + $year = $request->request->get('year'); + + $events_calendar = $this->eventsCalendar + ->getCalendar($month, $year); + + $calendar = [ + '#theme' => 'views_basic_events_calendar', + '#month_data' => $events_calendar, + '#cache' => [ + 'tags' => ['node_list:event'], + ], + ]; + + $response->addCommand(new ReplaceCommand($calendar_id, $calendar)); + + return $response; + } + +} diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php index 10119a4599..56ffeb9444 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php @@ -112,12 +112,23 @@ public function viewElements(FieldItemListInterface $items, $langcode): array { $paramsDecoded = json_decode($item->getValue()['params'], TRUE); if ($paramsDecoded['filters']['types'][0] === 'event' && $paramsDecoded['view_mode'] === 'calendar') { + // Calculate the remaining time until the end of the current month. + $now = new \DateTime(); + $end_of_month = new \DateTime('last day of this month 23:59:59'); + $remaining_time_in_seconds = $end_of_month->getTimestamp() - $now->getTimestamp(); + $events_calendar = $this->eventsCalendar ->getCalendar(date('m'), date('Y')); $elements[$delta] = [ '#theme' => 'views_basic_events_calendar', '#month_data' => $events_calendar, + '#cache' => [ + 'tags' => ['node_list:event'], + // Set max-age to the remaining time until the end of the month. + 'max-age' => $remaining_time_in_seconds, + 'contexts' => ['timezone'], + ], ]; } else { diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/ViewsBasicManager.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/ViewsBasicManager.php index a074fc6a01..157d4d5a8c 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/ViewsBasicManager.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/ViewsBasicManager.php @@ -56,11 +56,6 @@ class ViewsBasicManager extends ControllerBase implements ContainerInjectionInte 'img' => '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/display-type-condensed.svg', 'img_alt' => 'Icon showing 3 generic list items one on top of the other with no images on the items.', ], - 'calendar' => [ - 'label' => 'Calendar', - 'img' => '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/content-type-event.svg', - 'img_alt' => 'Calendar', - ], ], 'sort_by' => [ 'field_publish_date:DESC' => 'Publish Date - newer first', @@ -87,6 +82,11 @@ class ViewsBasicManager extends ControllerBase implements ContainerInjectionInte 'img' => '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/display-type-condensed.svg', 'img_alt' => 'Icon showing 3 generic list items one on top of the other with no images on the items.', ], + 'calendar' => [ + 'label' => 'Calendar', + 'img' => '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/content-type-event.svg', + 'img_alt' => 'Calendar', + ], ], 'sort_by' => [ 'field_event_date:DESC' => 'Event Date - newer first', diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.routing.yml b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.routing.yml new file mode 100644 index 0000000000..818b81880f --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.routing.yml @@ -0,0 +1,7 @@ +ys_views_basic.events_calendar: + path: '/events-calendar' + defaults: + _title: 'Events Calendar' + _controller: '\Drupal\ys_views_basic\Controller\EventsCalendarController' + requirements: + _permission: 'access content' From addba4ccd0026640f49e71d828deff377654bed8 Mon Sep 17 00:00:00 2001 From: Ion Eftodii Date: Thu, 12 Sep 2024 16:55:30 +0300 Subject: [PATCH 05/10] YOR-49: Add conditional logic to Views block for Calendar display type --- .../FieldWidget/ViewsBasicDefaultWidget.php | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php index 292c2364d5..a55186b52f 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php @@ -219,6 +219,11 @@ public function formElement( '#suffix' => '', ]; + // Define the state for when the view mode is 'calendar' once. + $calendarViewInvisibleState = [ + $formSelectors['view_mode_input_selector'] => ['value' => 'calendar'], + ]; + $fieldOptionValue = ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('field_options', $items[$delta]->params) : []; $form['group_user_selection']['entity_and_view_mode']['field_options'] = [ '#type' => 'checkboxes', @@ -230,6 +235,7 @@ public function formElement( '#title' => $this->t('Field Display Options'), '#tree' => TRUE, '#default_value' => empty($fieldOptionValue) ? ['show_thumbnail'] : $fieldOptionValue, + '#states' => ['invisible' => $calendarViewInvisibleState], 'show_thumbnail' => [ '#states' => [ 'visible' => [ @@ -252,14 +258,9 @@ public function formElement( '#title' => $this->t('Exposed Filter Options'), '#tree' => TRUE, '#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('exposed_filter_options', $items[$delta]->params) : [], + '#states' => ['invisible' => $calendarViewInvisibleState], 'show_year_filter' => [ - '#states' => [ - 'visible' => [ - $formSelectors['entity_types_ajax'] => [ - 'value' => 'post', - ], - ], - ], + '#states' => ['visible' => [$formSelectors['entity_types_ajax'] => ['value' => 'post']]], ], ]; @@ -269,9 +270,8 @@ public function formElement( '#description' => $this->t("Enter a custom label for the Category Filter. This label will be displayed to users as the filter's name. If left blank, the default label Category will be used."), '#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('category_filter_label', $items[$delta]->params) : NULL, '#states' => [ - 'visible' => [ - $formSelectors['show_category_filter_selector'] => ['checked' => TRUE], - ], + 'visible' => [$formSelectors['show_category_filter_selector'] => ['checked' => TRUE]], + 'invisible' => $calendarViewInvisibleState, ], ]; @@ -287,9 +287,8 @@ public function formElement( '#prefix' => '
', '#suffix' => '
', '#states' => [ - 'visible' => [ - $formSelectors['show_category_filter_selector'] => ['checked' => TRUE], - ], + 'visible' => [$formSelectors['show_category_filter_selector'] => ['checked' => TRUE]], + 'invisible' => $calendarViewInvisibleState, ], ]; @@ -302,6 +301,7 @@ public function formElement( '#tags' => TRUE, '#target_type' => 'taxonomy_term', '#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('terms_include', $items[$delta]->params) : [], + '#states' => ['invisible' => $calendarViewInvisibleState], ]; $form['group_user_selection']['filter_and_sort']['terms_exclude'] = [ @@ -313,6 +313,7 @@ public function formElement( '#tags' => TRUE, '#target_type' => 'taxonomy_term', '#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('terms_exclude', $items[$delta]->params) : [], + '#states' => ['invisible' => $calendarViewInvisibleState], ]; $form['group_user_selection']['filter_and_sort']['term_operator'] = [ @@ -329,7 +330,7 @@ public function formElement( 'term-operator-item', ], ], - + '#states' => ['invisible' => $calendarViewInvisibleState], ]; // Gets the view mode options based on Ajax callbacks or initial load. @@ -345,6 +346,7 @@ public function formElement( '#validated' => 'true', '#prefix' => '
', '#suffix' => '
', + '#states' => ['invisible' => $calendarViewInvisibleState], ]; $form['group_user_selection']['entity_specific']['event_time_period'] = [ @@ -357,11 +359,8 @@ public function formElement( ], '#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('event_time_period', $items[$delta]->params) : 'future', '#states' => [ - 'visible' => [ - $formSelectors['entity_types_ajax'] => [ - 'value' => 'event', - ], - ], + 'visible' => [$formSelectors['entity_types_ajax'] => ['value' => 'event']], + 'invisible' => $calendarViewInvisibleState, ], ]; @@ -377,6 +376,7 @@ public function formElement( 'limit' => $this->t('Limit to'), 'pager' => $this->t('Pagination after'), ], + '#states' => ['invisible' => $calendarViewInvisibleState], ]; $limitTitle = $this->t('Items'); @@ -397,6 +397,7 @@ public function formElement( '#required' => TRUE, '#prefix' => '
', '#suffix' => '
', + '#states' => ['invisible' => $calendarViewInvisibleState], ]; $form['group_user_selection']['options']['offset'] = [ @@ -408,6 +409,7 @@ public function formElement( '#attributes' => [ 'placeholder' => 0, ], + '#states' => ['invisible' => $calendarViewInvisibleState], ]; $element['group_params']['params'] = [ @@ -420,6 +422,7 @@ public function formElement( 'views-basic--params', ], ], + '#states' => ['invisible' => $calendarViewInvisibleState], ]; $form['#attached']['library'][] = 'ys_views_basic/ys_views_basic'; From 185a6c1bdd9e8578b3232a8a90d67728e36d33e3 Mon Sep 17 00:00:00 2001 From: Ion Eftodii Date: Sun, 15 Sep 2024 14:13:26 +0300 Subject: [PATCH 06/10] YOR-49: Fix visibility of Event Time Period --- .../ys_views_basic/assets/js/views-basic.js | 22 +++++++++++++++++++ .../FieldWidget/ViewsBasicDefaultWidget.php | 6 ++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js index c6a8580b13..8dbcc3a121 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js @@ -75,6 +75,28 @@ displayElement.addEventListener('change', updateLimitElement); updateLimitElement(); } + + // Unified selectors to handle both cases + const entityTypesSelector = 'input[name="settings[block_form][group_user_selection][entity_and_view_mode][entity_types]"], input[name="block_form[group_user_selection][entity_and_view_mode][entity_types]"]'; + const viewModeSelector = 'input[name="settings[block_form][group_user_selection][entity_and_view_mode][view_mode]"], input[name="block_form[group_user_selection][entity_and_view_mode][view_mode]"]'; + const eventTimePeriod = document.querySelector('#edit-event-time-period'); + + const entityTypes = document.querySelectorAll(entityTypesSelector); + const viewModes = document.querySelectorAll(viewModeSelector); + + // Function to handle visibility based on conditions + function updateVisibility() { + const entityType = Array.from(entityTypes).find(input => input.checked)?.value; + const viewMode = Array.from(viewModes).find(input => input.checked)?.value; + + if (eventTimePeriod) { + eventTimePeriod.style.display = (entityType === 'event' && viewMode === 'calendar') ? 'none' : ''; + } + } + + entityTypes.forEach(input => input.addEventListener('change', updateVisibility)); + viewModes.forEach(input => input.addEventListener('change', updateVisibility)); + updateVisibility(); }, }; })(Drupal); diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php index a55186b52f..1510d3fb67 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php @@ -358,10 +358,8 @@ public function formElement( 'all' => $this->t('All Events') . 'All Events icon showing a calendar.', ], '#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('event_time_period', $items[$delta]->params) : 'future', - '#states' => [ - 'visible' => [$formSelectors['entity_types_ajax'] => ['value' => 'event']], - 'invisible' => $calendarViewInvisibleState, - ], + '#prefix' => '
', + '#suffix' => '
', ]; $displayValue = ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('display', $items[$delta]->params) : 'all'; From 1aba53ff3ad3a3b12de4056c4ed97a28d7cfab6d Mon Sep 17 00:00:00 2001 From: Ion Eftodii Date: Tue, 17 Sep 2024 14:45:14 +0300 Subject: [PATCH 07/10] YOR-48: Event Calendar - Add Multi-day-Event string --- .../src/Service/EventsCalendar.php | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php index 2eb2331858..7566d4e903 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php @@ -6,6 +6,7 @@ use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\path_alias\AliasManagerInterface; use Drupal\smart_date_recur\Entity\SmartDateRule; @@ -14,6 +15,8 @@ */ class EventsCalendar implements EventsCalendarInterface { + use StringTranslationTrait; + /** * The entity type manager service. * @@ -165,8 +168,11 @@ public function getEvents(int $day, string $month, string $year, array $events): // Check if the instance overlaps with the current day. if ($instanceStartTimestamp <= $endTimestamp && $instanceEndTimestamp >= $startTimestamp) { $time = $this->isAllDay($instanceStartTimestamp, $instanceEndTimestamp) - ? 'All Day' - : date('g:iA', $instanceStartTimestamp) . ' to ' . date('g:iA', $instanceEndTimestamp); + ? $this->t('All Day') + : $this->t('@start to @end', [ + '@start' => date('g:iA', $instanceStartTimestamp), + '@end' => date('g:iA', $instanceEndTimestamp), + ]); $events_data[] = $this->createEventArray($event, $time, $instanceStartTimestamp); } @@ -181,9 +187,17 @@ public function getEvents(int $day, string $month, string $year, array $events): // Check if the event overlaps with the current day. if ($eventStartTimestamp <= $endTimestamp && $eventEndTimestamp >= $startTimestamp) { - $time = $this->isAllDay($eventStartTimestamp, $eventEndTimestamp) - ? 'All Day' - : date('g:iA', $eventStartTimestamp) . ' to ' . date('g:iA', $eventEndTimestamp); + if (date('Y-m-d', $eventStartTimestamp) !== date('Y-m-d', $eventEndTimestamp)) { + $time = $this->t('Multi-day Event'); + } + else { + $time = $this->isAllDay($eventStartTimestamp, $eventEndTimestamp) + ? $this->t('All Day') + : $this->t('@start to @end', [ + '@start' => date('g:iA', $eventStartTimestamp), + '@end' => date('g:iA', $eventEndTimestamp), + ]); + } // Add event to the list if it overlaps with the current day. $events_data[] = $this->createEventArray($event, $time, $eventStartTimestamp); From ba773714881a92ca56d591471c077b3d88e73886 Mon Sep 17 00:00:00 2001 From: Nathan Plowman Date: Tue, 17 Sep 2024 08:49:27 -0600 Subject: [PATCH 08/10] Fixed comment that got mangled after merging code from develop. --- .../src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php index bfe6b79f61..22f69921ac 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php @@ -226,8 +226,8 @@ public function formElement( $fieldOptionValue = ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('field_options', $items[$delta]->params) : []; $isNewForm = str_contains($formState->getCompleteForm()['#id'], 'layout-builder-add-block'); - // Set the default value for 'field_options' to ' - nail' + // Set the default value for 'field_options' to 'show_thumbnnail'. + // when creating a new block. $form['group_user_selection']['entity_and_view_mode']['field_options'] = [ '#type' => 'checkboxes', From 77e420ad6190bfa254ec9992e31ab775b81fc0e0 Mon Sep 17 00:00:00 2001 From: Nathan Plowman Date: Tue, 17 Sep 2024 08:58:09 -0600 Subject: [PATCH 09/10] Fixed code sniffer errors. --- .../src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php index 22f69921ac..e821fca424 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php @@ -226,8 +226,7 @@ public function formElement( $fieldOptionValue = ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('field_options', $items[$delta]->params) : []; $isNewForm = str_contains($formState->getCompleteForm()['#id'], 'layout-builder-add-block'); - // Set the default value for 'field_options' to 'show_thumbnnail'. - + // Set the default value for 'field_options' to 'show_thumbnail' // when creating a new block. $form['group_user_selection']['entity_and_view_mode']['field_options'] = [ '#type' => 'checkboxes', From 5d7e8d067b05dd2345b9b38c9fe23e81e729986f Mon Sep 17 00:00:00 2001 From: Nathan Plowman Date: Wed, 25 Sep 2024 09:40:22 -0600 Subject: [PATCH 10/10] Fixed code sniffer errors. --- .../custom/ys_views_basic/src/Service/EventsCalendar.php | 2 +- .../ys_views_basic/src/Service/EventsCalendarInterface.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php index 7566d4e903..9f54e55849 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php @@ -241,7 +241,7 @@ public function createEventArray($node, string $time, int $timestamp): array { /** * {@inheritdoc} */ - public function isAllDay(int $start_ts, int $end_ts, string $timezone = NULL): bool { + public function isAllDay(int $start_ts, int $end_ts, ?string $timezone = NULL): bool { if ($timezone) { $default_tz = date_default_timezone_get(); date_default_timezone_set($timezone); diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php index b97fd40b27..4c46404786 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php @@ -84,7 +84,7 @@ public function createEventArray(NodeInterface $node, string $time, int $timesta * @return bool * TRUE if the event is all day, FALSE otherwise. */ - public function isAllDay(int $start_ts, int $end_ts, string $timezone = NULL): bool; + public function isAllDay(int $start_ts, int $end_ts, ?string $timezone = NULL): bool; /** * Get monthly events nodes.