Skip to content

Commit

Permalink
Merge pull request #753 from yalesites-org/event-calendar-display
Browse files Browse the repository at this point in the history
JAKALA: Add Calendar display option to Views Block for Events
  • Loading branch information
dblanken-yale authored Oct 1, 2024
2 parents 38ae704 + a212fd4 commit 4916954
Show file tree
Hide file tree
Showing 11 changed files with 569 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Drupal\ys_views_basic\Controller;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Controller\ControllerBase;
use Drupal\ys_views_basic\Service\EventsCalendarInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;

/**
* Returns an Ajax response containing calendar data for the given month.
*/
final class EventsCalendarController extends ControllerBase {

/**
* The controller constructor.
*/
public function __construct(
private EventsCalendarInterface $eventsCalendar,
) {}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): self {
return new self(
$container->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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
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;

Expand All @@ -28,14 +28,14 @@ class ViewsBasicDefaultFormatter extends FormatterBase implements ContainerFacto
*
* @var \Drupal\ys_views_basic\ViewsBasicManager
*/
protected $viewsBasicManager;
protected ViewsBasicManager $viewsBasicManager;

/**
* The renderer service.
* The Events Calendar service.
*
* @var \Drupal\Core\Render\Renderer
* @var \Drupal\ys_views_basic\Service\EventsCalendarInterface
*/
protected $rendererService;
protected EventsCalendarInterface $eventsCalendar;

/**
* Constructs an views basic default formatter object.
Expand All @@ -54,10 +54,10 @@ 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,
Expand All @@ -68,7 +68,7 @@ public function __construct(
string $view_mode,
array $third_party_settings,
ViewsBasicManager $viewsBasicManager,
Renderer $renderer_service,
EventsCalendarInterface $eventsCalendar,
) {
parent::__construct(
$plugin_id,
Expand All @@ -78,20 +78,15 @@ public function __construct(
$label,
$view_mode,
$third_party_settings,
$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,
Expand All @@ -101,33 +96,57 @@ public static function create(
$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')
);
}

/**
* Define how the field type is showed.
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
public function viewElements(FieldItemListInterface $items, $langcode): array {

$elements = [];

foreach ($items as $delta => $item) {
// Get decoded parameters.
$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();

$view = $this->viewsBasicManager->getView('rendered', $item->getValue()['params']);
$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,
'#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 {
$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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ public function formElement(
'#suffix' => '</div>',
];

// 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) : [];
$fieldOptionDefaultValue = $fieldOptionValue ?? ['show_thumbnail' => 'show_thumbnail'];
$isNewForm = str_contains($formState->getCompleteForm()['#id'], 'layout-builder-add-block');
Expand All @@ -234,6 +239,7 @@ public function formElement(
'#title' => $this->t('Field Display Options'),
'#tree' => TRUE,
'#default_value' => ($isNewForm && empty($fieldOptionValue)) ? ['show_thumbnail'] : $fieldOptionDefaultValue,
'#states' => ['invisible' => $calendarViewInvisibleState],
'show_thumbnail' => [
'#states' => [
'visible' => [
Expand All @@ -256,14 +262,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']]],
],
];

Expand All @@ -273,9 +274,8 @@ public function formElement(
'#description' => $this->t("Enter a custom label for the <strong>Category Filter</strong>. This label will be displayed to users as the filter's name. If left blank, the default label <strong>Category</strong> 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,
],
];

Expand All @@ -291,9 +291,8 @@ public function formElement(
'#prefix' => '<div id="edit-category-included-terms">',
'#suffix' => '</div>',
'#states' => [
'visible' => [
$formSelectors['show_category_filter_selector'] => ['checked' => TRUE],
],
'visible' => [$formSelectors['show_category_filter_selector'] => ['checked' => TRUE]],
'invisible' => $calendarViewInvisibleState,
],
];

Expand All @@ -306,6 +305,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'] = [
Expand All @@ -317,6 +317,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'] = [
Expand All @@ -333,7 +334,7 @@ public function formElement(
'term-operator-item',
],
],

'#states' => ['invisible' => $calendarViewInvisibleState],
];

// Gets the view mode options based on Ajax callbacks or initial load.
Expand All @@ -349,6 +350,7 @@ public function formElement(
'#validated' => 'true',
'#prefix' => '<div id="edit-sort-by">',
'#suffix' => '</div>',
'#states' => ['invisible' => $calendarViewInvisibleState],
];

$form['group_user_selection']['entity_specific']['event_time_period'] = [
Expand All @@ -360,13 +362,8 @@ public function formElement(
'all' => $this->t('All Events') . '<img src="/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/event-time-all.svg" alt="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',
],
],
],
'#prefix' => '<div id="edit-event-time-period">',
'#suffix' => '</div>',
];

$displayValue = ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('display', $items[$delta]->params) : 'all';
Expand All @@ -381,6 +378,7 @@ public function formElement(
'limit' => $this->t('Limit to'),
'pager' => $this->t('Pagination after'),
],
'#states' => ['invisible' => $calendarViewInvisibleState],
];

$limitTitle = $this->t('Items');
Expand All @@ -401,6 +399,7 @@ public function formElement(
'#required' => TRUE,
'#prefix' => '<div id="edit-limit">',
'#suffix' => '</div>',
'#states' => ['invisible' => $calendarViewInvisibleState],
];

$form['group_user_selection']['options']['offset'] = [
Expand All @@ -412,6 +411,7 @@ public function formElement(
'#attributes' => [
'placeholder' => 0,
],
'#states' => ['invisible' => $calendarViewInvisibleState],
];

$element['group_params']['params'] = [
Expand All @@ -424,6 +424,7 @@ public function formElement(
'views-basic--params',
],
],
'#states' => ['invisible' => $calendarViewInvisibleState],
];

$form['#attached']['library'][] = 'ys_views_basic/ys_views_basic';
Expand Down
Loading

0 comments on commit 4916954

Please sign in to comment.