Skip to content

Commit

Permalink
#110 Add iCal support for the timetable
Browse files Browse the repository at this point in the history
  • Loading branch information
rotator committed May 26, 2019
1 parent 248f498 commit 270a64d
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 79 deletions.
14 changes: 14 additions & 0 deletions docroot/modules/custom/dckyiv_core/dckyiv_core.module
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ function dckyiv_core_preprocess_html(&$variables) {
->preprocessHtml($variables);
}

/**
* Implements hook_theme().
*/
function dckyiv_core_theme() {
return [
'dckyiv_core_ical' => [
'template' => 'dckyiv-core-ical',
'variables' => [
'info' => [],
],
],
];
}

/**
* Implements hook_entity_type_alter().
*/
Expand Down
11 changes: 10 additions & 1 deletion docroot/modules/custom/dckyiv_core/dckyiv_core.routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,13 @@ dckyiv_core.admin:
_controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
_title: 'DCKyiv'
requirements:
_permission: 'administer content'
_permission: 'administer content'

dckyiv_core.ical:
path: '/add-icalendar/{paragraph}/{node}'
defaults:
_controller: '\Drupal\dckyiv_core\Controller\IcalController::iCalendar'
options:
no_cache: 'TRUE'
requirements:
_permission: 'access content'
4 changes: 4 additions & 0 deletions docroot/modules/custom/dckyiv_core/dckyiv_core.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
dckyive_core.timetable:
class: Drupal\dckyiv_core\TimetableService
arguments: ['@entity_type.manager', '@request_stack']
101 changes: 101 additions & 0 deletions docroot/modules/custom/dckyiv_core/src/Controller/IcalController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

namespace Drupal\dckyiv_core\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\dckyiv_core\TimetableService;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;

/**
* iCalendar controller.
*/
class IcalController extends ControllerBase {

/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;

/**
* Renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;

/**
* The timetable service.
*
* @var \Drupal\dckyiv_core\TimetableService
*/
protected $timetableService;

/**
* iCalendar controller constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Render\RendererInterface $renderer
* Renderer
* @param \Drupal\dckyiv_core\TimetableService $timetable_service
* The timetable service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, TimetableService $timetable_service) {
$this->entityTypeManager = $entity_type_manager;
$this->renderer = $renderer;
$this->timetableService = $timetable_service;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('renderer'),
$container->get('dckyive_core.timetable')
);
}

/**
* Get .ics file for iCalendar
*
* @param $paragraph \Drupal\paragraphs\Entity\Paragraph Presentation paragraph
* @param $node \Drupal\node\Entity\Node Presentation node
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function iCalendar($paragraph, $node) {
if (is_numeric($paragraph)) {
$paragraph = $this->entityTypeManager->getStorage('paragraph')->load($paragraph);
}
if (is_numeric($node)) {
$node = $this->entityTypeManager->getStorage('node')->load($node);
}

$info = $this->timetableService->prepareCalendarInfo($paragraph, $node);
$filename = preg_replace('/[\x00-\x1F]/u', '_', $info['title']);

$build = [
'#theme' => 'dckyiv_core_ical',
'#info' => $info,
'#cache' => ['max-age' => 0],
];
$output = $this->renderer->renderRoot($build);

$response = new Response();
$response->headers->set('Content-Type', 'application/calendar; charset=utf-8');
$response->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '.ics"');
$response->setContent($output);

return $response;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

namespace Drupal\dckyiv_core\Plugin\Field\FieldFormatter;

use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter;
use Drupal\Core\Link;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\Core\Form\FormStateInterface;
use Drupal\dckyiv_core\TimetableService;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Plugin implementation of formatter with add to calendar link.
Expand All @@ -24,6 +30,74 @@ class TimetablePresentationFormatter extends EntityReferenceEntityFormatter {

use StringTranslationTrait;

/**
* The timetable service.
*
* @var \Drupal\dckyiv_core\TimetableService
*/
protected $timetableService;

/**
* The current request.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $currentRequest;

/**
* Constructs a EntityReferenceEntityFormatter instance.
*
* @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
* Any third party settings settings.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger factory.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
* The entity display repository.
* @param \Drupal\dckyiv_core\TimetableService $timetable_service
* The timetable service.
* @param \Symfony\Component\HttpFoundation\RequestStack $current_request
* The current request.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, LoggerChannelFactoryInterface $logger_factory, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, TimetableService $timetable_service, RequestStack $request_stack) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $logger_factory, $entity_type_manager, $entity_display_repository);
$this->timetableService = $timetable_service;
$this->currentRequest = $request_stack->getCurrentRequest();
}

/**
* {@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('logger.factory'),
$container->get('entity_type.manager'),
$container->get('entity_display.repository'),
$container->get('dckyive_core.timetable'),
$container->get('request_stack')
);
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -55,91 +129,74 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
// @TODO Add check paragraph entities has correct type/fields.
$presentation_paragraph = $items->getEntity();
$slot_paragraph = $presentation_paragraph->getParentEntity();
$dateRange = $slot_paragraph->field_time_slot_start_end[0];
$rfc_dates = $this->rfc3339Date($dateRange->value, $dateRange->end_value);
$elements = parent::viewElements($items, $langcode);

/** @var \Drupal\paragraphs\Entity\Paragraph $presentationParagraph */
$presentationParagraph = $items->getEntity();
$nodes = $items->referencedEntities();
$node = reset($nodes);
/** @var \Drupal\node\Entity\Node $presentationNode */
$presentationNode = reset($nodes);
if (empty($presentationParagraph) || empty($presentationNode)) {
return $elements;
}

// @TODO Add check if field value is present.
$title = $node->title[0]->getValue();
$place = $presentation_paragraph->field_place[0]->getValue();
$description = $node->field_presentation_description[0]->getValue();
$info = $this->timetableService->prepareCalendarInfo($presentationParagraph, $presentationNode);
if (empty($info)) {
return $elements;
}

$elements = parent::viewElements($items, $langcode);
$google_url = Link::fromTextAndUrl(
$this->t('Add to My Google calendar'),
Url::fromUri('http://www.google.com/calendar/event', [
'attributes' => [
'target' => '_blank',
'class' => [
'add-to-calendar',
''
$elements[] = [
'#type' => 'container',
'#attributes' => [
'class' => ['calendar-wrapper row'],
],
// My Google Calendar link
'google_calendar' => [
'#type' => 'link',
'#title' => $this->t('Add to My Google calendar'),
'#url' => Url::fromUri('http://www.google.com/calendar/event', [
'attributes' => [
'target' => '_blank',
'class' => [
'add-to-calendar',
'add-to-google-calendar'
],
],
],
'query' => [
'action' => 'TEMPLATE',
'text' => $this->t('Session @title', ['@title' => strip_tags($title['value'])]),
'dates' => $rfc_dates['both'],
'sprop' => 'website:' . $_SERVER['HTTP_HOST'],
'location' => $this->getSetting('address'),
'details' => $this->t("@place \n@description", [
'@place' => strip_tags($place['value']),
'@description' => strip_tags($description['value']),
'query' => [
'action' => 'TEMPLATE',
'text' => $this->t('Session @title', ['@title' => $info['title']]),
'dates' => $info['dates']['both'],
'sprop' => 'website:' . $this->currentRequest->getHttpHost(),
'location' => $this->getSetting('address'),
'details' => $this->t("@place \n@description", [
'@place' => $info['place'],
'@description' => $info['description'],
]),
'website' => Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(),
],
]),
],
// iCalendar link.
'icalendar' => [
'#type' => 'link',
'#title' => $this->t('Add to iCalendar'),
'#url' => Url::fromRoute('dckyiv_core.ical',
[
'paragraph' => $presentationParagraph->id(),
'node' => $presentationNode->id(),
],
[
'attributes' => [
'class' => [
'add-to-calendar',
'add-to-icalendar',
],
],
]),
'website' => Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(),
],
])
)->toString();

$elements[] = [
'#markup' => $google_url
]
];

return $elements;
}

/**
* Returns an array containing RFC 3339 formatted start and end dates.
*
* @param $start
* Start date.
* @param $end
* End date.
*
* @return array
*/
protected function rfc3339Date($start, $end) {
if (!$end) {
$end = $start;
}

$start_timestamp = strtotime($start . 'UTC');
$end_timestamp = strtotime($end . 'UTC');

$diff_timestamp = $end_timestamp - $start_timestamp;

$start_date = gmdate('Ymd', $start_timestamp) . 'T' . gmdate('His', $start_timestamp) . 'Z';
$local_start_date = date('Ymd', $start_timestamp) . 'T' . date('His', $start_timestamp) . '';
$end_date = gmdate('Ymd', $end_timestamp) . 'T' . gmdate('His', $end_timestamp) . 'Z';
$local_end_date = date('Ymd', $end_timestamp) . 'T' . date('His', $end_timestamp) . '';

$diff_hours = str_pad(round(($diff_timestamp / 60) / 60), 2, '0', STR_PAD_LEFT);
$diff_minutes = str_pad(abs(round($diff_timestamp / 60) - ($diff_hours * 60)), 2, '0', STR_PAD_LEFT);

$duration = $diff_hours . $diff_minutes;

return array(
'start' => $start_date,
'end' => $end_date,
'both' => $start_date . '/' . $end_date,
'local_start' => $local_start_date,
'local_end' => $local_end_date,
'duration' => $duration,
);
}

}
Loading

0 comments on commit 270a64d

Please sign in to comment.