From 0fc14456f29eea8bc77c86e7cd47032ab61b7c25 Mon Sep 17 00:00:00 2001 From: chrode Date: Fri, 16 Aug 2024 14:46:16 +0200 Subject: [PATCH] [FEATURE] Add HTTP-REST API for Events (#105) * Add api for event list --------- Co-authored-by: Andreas Pfeiffer Co-authored-by: Alexander Bigga Co-authored-by: Andreas Pfeiffer <88720542+epx-anpf@users.noreply.github.com> --- Classes/Authentication/ApiAuthentication.php | 149 +++++++++++++++ Classes/Controller/Api/EventController.php | 118 ++++++++++++ .../Controller/Backend/EventController.php | 2 +- .../Backend/SubscriberController.php | 2 +- Classes/Domain/Model/Event.php | 50 +++++ Classes/Domain/Repository/EventRepository.php | 13 +- Classes/Helper/EventHelper.php | 2 +- Classes/Mvc/View/JsonView.php | 140 ++++++++++++++ Classes/Service/ApiService.php | 127 +++++++++++++ Classes/Service/CategoryService.php | 171 ++++++++++++++++++ Classes/Service/DisciplineService.php | 78 ++++++++ Classes/Service/EventService.php | 118 ++++++++++++ Classes/Service/SubscriberService.php | 144 +++++++++++++++ Classes/Utility/TextUtility.php | 2 +- Configuration/TypoScript/constants.typoscript | 14 ++ Configuration/TypoScript/setup.typoscript | 35 ++++ Documentation/Api/Index.rst | 99 ++++++++++ Documentation/Index.rst | 1 + ext_localconf.php | 30 ++- 19 files changed, 1289 insertions(+), 6 deletions(-) create mode 100644 Classes/Authentication/ApiAuthentication.php create mode 100644 Classes/Controller/Api/EventController.php create mode 100644 Classes/Mvc/View/JsonView.php create mode 100644 Classes/Service/ApiService.php create mode 100644 Classes/Service/CategoryService.php create mode 100644 Classes/Service/DisciplineService.php create mode 100644 Classes/Service/EventService.php create mode 100644 Classes/Service/SubscriberService.php create mode 100644 Documentation/Api/Index.rst diff --git a/Classes/Authentication/ApiAuthentication.php b/Classes/Authentication/ApiAuthentication.php new file mode 100644 index 0000000..a609e66 --- /dev/null +++ b/Classes/Authentication/ApiAuthentication.php @@ -0,0 +1,149 @@ + 'Invalid authorization' + ]; + + /** + * @var ConfigurationManagerInterface + */ + protected $configurationManager; + + public function __construct() + { + /** @var ObjectManager $objectManager */ + $objectManager = GeneralUtility::makeInstance(ObjectManager::class); + + $this->configurationManager = $objectManager->get(ConfigurationManagerInterface::class); + } + + /** + * @return bool + */ + public function authenticateUser(): bool + { + $users = $this->findAllUsers(); + $apiUser = $this->getApiUser(); + + return $this->isValiduser($users, $apiUser); + } + + /** + * @param JsonView $view + * @param int $status + * @return JsonView + */ + public function getError(JsonView $view, int $status): JsonView + { + $view->setVariablesToRender(['error']); + $view->assign('error', [ + 'error' => [ + 'status' => $status, + 'message' => $this->error[$status] + ] + ]); + + return $view; + } + + /** + * @param array $users + * @param array $apiUser + * @return bool + */ + protected function isValidUser(array $users, array $apiUser): bool + { + if (count($users) === 0 || count($apiUser) === 0) { + return false; + } + + foreach ($users as $user) { + // Security risk if there is a user with empty username and empty password + // Well, close the api in general + if (empty($user['username']) || empty($user['password'])) { + return false; + } + + if ($user['username'] === $apiUser['username'] && + $user['password'] === $apiUser['password'] + ) { + return true; + } + } + + return false; + } + + /** + * @return array + */ + protected function getApiUser(): array + { + $user = []; + $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? null; + + if (stripos($authorization, 'Basic ') === 0) { + $user = GeneralUtility::trimExplode(':', base64_decode(substr($authorization, 6)), 2); + } + + if (count($user) === 2) { + return [ + 'username' => $user[0], + 'password' => $user[1] + ]; + } + + return []; + } + + /** + * @return array + */ + protected function findAllUsers(): array + { + return $this->getExtensionSettings()['api']['users'] ?? []; + } + + /** + * @return array + */ + protected function getExtensionSettings(): array + { + return (array)$this->configurationManager->getConfiguration( + ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, + self::EXTENSION_NAME + ); + } +} diff --git a/Classes/Controller/Api/EventController.php b/Classes/Controller/Api/EventController.php new file mode 100644 index 0000000..9cc4440 --- /dev/null +++ b/Classes/Controller/Api/EventController.php @@ -0,0 +1,118 @@ +apiService = $objectManager->get(ApiService::class); + $this->apiAuthentication = $objectManager->get(ApiAuthentication::class); + $this->eventService = $objectManager->get(EventService::class); + + $this->allowApiAccess = $this->apiAuthentication->authenticateUser(); + } + + /** + * @param ViewInterface $view + */ + public function initializeView(ViewInterface $view): void + { + parent::initializeView($view); + + if (!$this->allowApiAccess) { + $this->view = $this->apiAuthentication->getError($this->view, 401); + } + } + + /** + * @return void + */ + public function listAction(): void + { + if ($this->allowApiAccess) { + $arguments = $this->apiService->prepareArgumentsDefault($this->request->getArguments()); + $events = $this->eventService->findAllBySettings($arguments); + + $this->view->setVariablesToRender(['events']); + $this->view->assign('events', $events); + } + } + + /** + * @return void + */ + public function listUserAction(): void + { + if ($this->allowApiAccess) { + $arguments = $this->apiService->prepareArgumentsUser($this->request->getArguments()); + $events = $arguments['user'] === 0 ? [] : $this->eventService->findAllBySettings($arguments); + $eventsUser = $this->eventService->prepareForUser($arguments['user'], $events, $this->settings); + + $this->view->setVariablesToRender(['eventsUser']); + $this->view->assign('eventsUser', $eventsUser); + } + } +} diff --git a/Classes/Controller/Backend/EventController.php b/Classes/Controller/Backend/EventController.php index 2982624..4de87e0 100755 --- a/Classes/Controller/Backend/EventController.php +++ b/Classes/Controller/Backend/EventController.php @@ -5,7 +5,7 @@ * This file is part of the TYPO3 CMS project. * * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 + * the terms of the GNU General Public License, either version 3 * of the License, or any later version. * * For the full copyright and license information, please read the diff --git a/Classes/Controller/Backend/SubscriberController.php b/Classes/Controller/Backend/SubscriberController.php index 6dd0ec5..7c9e808 100755 --- a/Classes/Controller/Backend/SubscriberController.php +++ b/Classes/Controller/Backend/SubscriberController.php @@ -5,7 +5,7 @@ * This file is part of the TYPO3 CMS project. * * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 + * the terms of the GNU General Public License, either version 3 * of the License, or any later version. * * For the full copyright and license information, please read the diff --git a/Classes/Domain/Model/Event.php b/Classes/Domain/Model/Event.php index 21c3d80..d85f934 100755 --- a/Classes/Domain/Model/Event.php +++ b/Classes/Domain/Model/Event.php @@ -57,6 +57,11 @@ class Event extends AbstractEntity { */ protected $parent; + /** + * @var array + */ + protected $rootCategories = []; + /** * startDateTime * @@ -250,6 +255,13 @@ class Event extends AbstractEntity { */ protected $recurringOptions; + /** + * The unsubscribe url + * + * @var string + */ + protected $unsubscribeUrl; + /** * The recurring end dateTime * @@ -452,6 +464,22 @@ public function setParent( \Slub\SlubEvents\Domain\Model\Event $parent ) { $this->parent = $parent; } + /** + * @return array $rootCategories + */ + public function getRootCategories(): array + { + return $this->rootCategories; + } + + /** + * @param array $rootCategories + */ + public function setRootCategories(array $rootCategories): void + { + $this->rootCategories = $rootCategories; + } + /** * Returns the minSubscriber * @@ -984,6 +1012,28 @@ public function setRecurringEndDateTime( $recurringEndDateTime ) { $this->recurringEndDateTime = $recurringEndDateTime; } + /** + * Returns the unsubscribe url value + * + * @return string $unsubscribeUrl + */ + public function getUnsubscribeUrl() + { + return $this->unsubscribeUrl; + } + + /** + * Sets the unsubscribe url state + * + * @param string $unsubscribeUrl + * + * @return void + */ + public function setUnsubscribeUrl($unsubscribeUrl) + { + $this->unsubscribeUrl = $unsubscribeUrl; + } + /** * Get CategoryStats * diff --git a/Classes/Domain/Repository/EventRepository.php b/Classes/Domain/Repository/EventRepository.php index 3ae462c..4dde1d2 100644 --- a/Classes/Domain/Repository/EventRepository.php +++ b/Classes/Domain/Repository/EventRepository.php @@ -183,6 +183,18 @@ public function findAllBySettings($settings, $geniusBar = 0) // we don't want genius_bar events here $constraints[] = $query->equals('genius_bar', $geniusBar); + // is user / subscriber given + if ((int)$settings['user'] > 0) { + $constraints[] = $query->logicalAnd( + [ + $query->equals('subscribers.customerid', $settings['user']), + $query->logicalNot( + $query->equals('subscribers.editcode', '') + ) + ] + ); + } + // are categories selected? if (is_array($settings['categoryList']) && count($settings['categoryList']) > 0) { $constraints[] = $query->in('categories.uid', $settings['categoryList']); @@ -365,7 +377,6 @@ public function findAllByDateInterval($startDateStamp, $stopDateStamp) return $query->execute(); } - /** * Finds all datasets by MM relation categories * diff --git a/Classes/Helper/EventHelper.php b/Classes/Helper/EventHelper.php index 9dc616e..c05a4c4 100644 --- a/Classes/Helper/EventHelper.php +++ b/Classes/Helper/EventHelper.php @@ -5,7 +5,7 @@ * This file is part of the TYPO3 CMS project. * * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 + * the terms of the GNU General Public License, either version 3 * of the License, or any later version. * * For the full copyright and license information, please read the diff --git a/Classes/Mvc/View/JsonView.php b/Classes/Mvc/View/JsonView.php new file mode 100644 index 0000000..73b2963 --- /dev/null +++ b/Classes/Mvc/View/JsonView.php @@ -0,0 +1,140 @@ + [ + ], + 'events' => [ + '_descendAll' => [ + '_exclude' => ['pid', 'recurring', 'recurringOptions', 'recurringEndDateTime'], + '_descend' => [ + 'categories' => [ + '_descendAll' => [ + '_only' => ['uid', 'title'] + ] + ], + 'contact' => [ + '_only' => ['name', 'email'] + ], + 'discipline' => [ + '_descendAll' => [ + '_exclude' => ['pid'] + ], + ], + 'endDateTime' => [], + 'location' => [ + '_exclude' => ['pid'] + ], + 'parent' => [ + '_only' => ['uid', 'title'] + ], + 'rootCategories' => [ + '_descendAll' => [ + '_only' => ['uid', 'title'] + ] + ], + 'startDateTime' => [], + 'subscribers' => [ + '_descendAll' => [ + '_only' => ['uid', 'customerid', 'number'] + ] + ] + ] + ] + ], + 'eventsUser' => [ + '_descendAll' => [ + '_exclude' => ['pid'], + '_descend' => [ + 'categories' => [ + '_descendAll' => [ + '_only' => ['uid', 'title'] + ] + ], + 'contact' => [ + '_exclude' => ['pid'] + ], + 'discipline' => [ + '_descendAll' => [ + '_exclude' => ['pid'] + ], + ], + 'endDateTime' => [], + 'location' => [ + '_exclude' => ['pid'] + ], + 'parent' => [ + '_only' => ['uid', 'title'] + ], + 'recurringOptions' => [], + 'recurringEndDateTime' => [], + 'rootCategories' => [ + '_descendAll' => [ + '_only' => ['uid', 'title'] + ] + ], + 'startDateTime' => [] + ] + ] + ] + ]; + + /** + * Always transforming object storages to arrays for the JSON view + * + * @param mixed $value + * @param array $configuration + * @return mixed + */ + protected function transformValue($value, array $configuration, $firstLevel = false) + { + if ($value instanceof ObjectStorage) { + $value = $value->toArray(); + } + + if ($value instanceof DateTime) { + return [ + 'format' => $value->format('c'), + 'timestamp' => $value->getTimestamp() + ]; + } + + // "recurringOptions" is written as serialized string with "weekday" and "interval". + // Use them as key words to identify and return as array. If not it fails + if (is_array($value) && (isset($value['weekday'], $value['interval']))) { + return [ + 'weekday' => $value['weekday'], + 'interval' => $value['interval'] + ]; + } + + return parent::transformValue($value, $configuration); + } +} diff --git a/Classes/Service/ApiService.php b/Classes/Service/ApiService.php new file mode 100644 index 0000000..9f49a38 --- /dev/null +++ b/Classes/Service/ApiService.php @@ -0,0 +1,127 @@ +categoryService = GeneralUtility::makeInstance(CategoryService::class); + + /** @var DisciplineService $disciplineService */ + $this->disciplineService = GeneralUtility::makeInstance(DisciplineService::class); + } + + /** + * @param array $arguments + * @return array + */ + public function prepareArgumentsDefault($arguments = []): array + { + $preparedArguments = []; + + if (count($arguments) === 0) { + return $preparedArguments; + } + + if ($arguments['category']) { + $preparedArguments['categoryList'] = $this->categoryService->getCategoryIds( + $arguments['category'], + (bool)$arguments['categoryRecursive'] + ); + } + + if ($arguments['discipline']) { + $preparedArguments['disciplineList'] = $this->disciplineService->getDisciplineIds( + $arguments['discipline'], + (bool)$arguments['disciplineRecursive'] + ); + } + + if ($arguments['contact']) { + $preparedArguments['contactsSelection'] = $arguments['contact']; + } + + if ($arguments['showPastEvents']) { + $preparedArguments['showPastEvents'] = (bool)$arguments['showPastEvents']; + } + + if ($arguments['showEventsFromNow']) { + $preparedArguments['showEventsFromNow'] = (bool)$arguments['showEventsFromNow']; + } + + if ($arguments['limitByNextWeeks']) { + $preparedArguments['limitByNextWeeks'] = (int)$arguments['limitByNextWeeks']; + } + + if ($arguments['startTimestamp']) { + $preparedArguments['startTimestamp'] = (int)$arguments['startTimestamp']; + } + + if ($arguments['stopTimestamp']) { + $preparedArguments['stopTimestamp'] = (int)$arguments['stopTimestamp']; + } + + if ($arguments['sorting'] === 'desc') { + $preparedArguments['eventOrdering'] = 'DESC'; + } + + if ($arguments['limit']) { + $preparedArguments['limit'] = (int)$arguments['limit']; + } + + return $preparedArguments; + } + + /** + * @param array $arguments + * @return array + */ + public function prepareArgumentsUser($arguments = []): array + { + if (count($arguments) === 0) { + return ['user' => 0]; + } + + $preparedArguments = $this->prepareArgumentsDefault($arguments); + + if ($arguments['user']) { + $preparedArguments['user'] = (int)$arguments['user']; + } + + return $preparedArguments; + } +} diff --git a/Classes/Service/CategoryService.php b/Classes/Service/CategoryService.php new file mode 100644 index 0000000..f0069c2 --- /dev/null +++ b/Classes/Service/CategoryService.php @@ -0,0 +1,171 @@ +categoryRepository = $objectManager->get(CategoryRepository::class); + + /** @var CacheManager $cacheManager */ + $cacheManager = GeneralUtility::makeInstance(CacheManager::class); + + /** @var FrontendInterface $cache */ + $this->cache = $cacheManager->getCache(self::CACHE_IDENTIFIER); + } + + /** + * @param int $uid + * @return Category|null + */ + public function findByUid(int $uid): ?Category + { + return $this->categoryRepository->findByUid($uid); + } + + /** + * @param string $category + * @param false $recursive + * @return array + */ + public function getCategoryIds($category = '', $recursive = false): array + { + $categoryIds = GeneralUtility::intExplode(',', $category, true); + + if ($recursive === true) { + foreach ($categoryIds as $categoryId) { + $categoryIds = $this->addChildCategories($categoryId, $categoryIds); + } + } + + return $categoryIds; + } + + /** + * @param array $categories + * @return array|null + */ + public function getRoots(array $categories): ?array + { + if (count($categories) === 0) { + return null; + } + + $roots = []; + $rootIds = []; + + /** @var Category $category */ + foreach ($categories as $category) { + $rootLine = $this->getRootLine($category); + $rootUid = (int)GeneralUtility::trimExplode(',', $rootLine)[0]; + $root = $this->categoryRepository->findByUid($rootUid); + + if ($root instanceof Category && !in_array($rootUid, $rootIds, true)) { + $roots[] = $root; + $rootIds[] = $rootUid; + } + } + + return $roots; + } + + /** + * @param Category $category + * @return string + */ + protected function getRootLine(Category $category): string + { + $cacheIdentifier = sha1('parent-' . $category); + $rootLine = $this->cache->get($cacheIdentifier); + + if (!$rootLine) { + $rootLine = $this->getRootLineRecursive($category); + $this->cache->set($cacheIdentifier, $rootLine); + } + + return $rootLine; + } + + /** + * @param Category $category + * @param array $result + * @return string + */ + protected function getRootLineRecursive(Category $category, $result = []): string + { + $result[] = $category->getUid(); + $parents = $category->getParent(); + + if (count($parents) > 0) { + foreach ($parents as $parent) { + if ($parent instanceof Category) { + return $this->getRootLineRecursive($parent, $result); + } + } + } + + krsort($result); + + return implode(',', $result); + } + + /** + * @param int $categoryId + * @param array $categoryIds + * @return array + */ + protected function addChildCategories($categoryId = 0, $categoryIds = []): array + { + $childCategories = $this->categoryRepository->findAllChildCategories($categoryId); + + if (count($childCategories) > 0) { + return array_merge($childCategories, $categoryIds); + } + + return $categoryIds; + } +} diff --git a/Classes/Service/DisciplineService.php b/Classes/Service/DisciplineService.php new file mode 100644 index 0000000..7e08a64 --- /dev/null +++ b/Classes/Service/DisciplineService.php @@ -0,0 +1,78 @@ +disciplineRepository = $objectManager->get(DisciplineRepository::class); + } + + /** + * @param string $discipline + * @param false $recursive + * @return array + */ + public function getDisciplineIds($discipline = '', $recursive = false): array + { + $disciplineIds = GeneralUtility::intExplode(',', $discipline, true); + + if ($recursive === true) { + foreach ($disciplineIds as $disciplineId) { + $disciplineIds = $this->addChildDisciplines($disciplineId, $disciplineIds); + } + } + + return $disciplineIds; + } + + /** + * @param int $disciplineId + * @param array $disciplineIds + * @return array + */ + protected function addChildDisciplines($disciplineId = 0, $disciplineIds = []): array + { + $childDisciplines = $this->disciplineRepository->findAllChildDisciplines($disciplineId); + + if (count($childDisciplines) > 0) { + return array_merge($childDisciplines, $disciplineIds); + } + + return $disciplineIds; + } +} diff --git a/Classes/Service/EventService.php b/Classes/Service/EventService.php new file mode 100644 index 0000000..7eb4a19 --- /dev/null +++ b/Classes/Service/EventService.php @@ -0,0 +1,118 @@ +categoryService = $objectManager->get(CategoryService::class); + $this->subscriberService = $objectManager->get(SubscriberService::class); + $this->eventRepository = $objectManager->get(EventRepository::class); + } + + /** + * @param array $arguments + * @return array + */ + public function findAllBySettings(array $arguments): array + { + $events = $this->eventRepository->findAllBySettings($arguments)->toArray(); + + return $this->addRootCategoriesToEvents($events); + } + + /** + * @param int $user + * @param array $events + * @param array $settings + * @return array + */ + public function prepareForUser(int $user, $events = [], $settings = []): array + { + if ($user === 0 || count($events) === 0) { + return []; + } + + return $this->subscriberService->addUnsubscribeUrl( + $user, + $events, + (int)$settings['unsubscribePid'] + ); + } + + /** + * @param array $events + * @return array + */ + protected function addRootCategoriesToEvents(array $events): array + { + $withRootCategory = []; + + if (count($events) > 0) { + /** @var Event $event */ + foreach ($events as $event) { + $withRootCategory[] = $this->addRootCategoriesToEvent($event); + } + } + + return $withRootCategory; + } + + /** + * @param Event $event + * @return Event + */ + protected function addRootCategoriesToEvent(Event $event): Event + { + $categories = $event->getCategories()->toArray(); + $rootCategories = $this->categoryService->getRoots($categories); + + $event->setRootCategories($rootCategories); + + return $event; + } +} diff --git a/Classes/Service/SubscriberService.php b/Classes/Service/SubscriberService.php new file mode 100644 index 0000000..2eb96c2 --- /dev/null +++ b/Classes/Service/SubscriberService.php @@ -0,0 +1,144 @@ +uriBuilder = $objectManager->get(UriBuilder::class); + } + + /** + * @param int $user + * @param array $events + * @param int $unsubscribePid + * @return array + */ + public function addUnsubscribeUrl(int $user, array $events, int $unsubscribePid): array + { + if ($unsubscribePid === 0) { + return $events; + } + + $preparedEvents = []; + $unsubscribeUrl = $this->getUnsubscribeUrl($unsubscribePid); + + /** @var Event $event */ + foreach ($events as $event) { + $preparedUnsubscribeParameter = $this->prepareUnsubscribeParameter($user, $event); + + if ($preparedUnsubscribeParameter === null) { + continue; + } + + $event->setUnsubscribeUrl($unsubscribeUrl . $preparedUnsubscribeParameter); + + $preparedEvents[] = $event; + } + + return $preparedEvents; + } + + /** + * @param int $unsubscribePid + * @return string + */ + protected function getUnsubscribeUrl(int $unsubscribePid): string + { + return $this->uriBuilder + ->reset() + ->setTargetPageUid($unsubscribePid) + ->setCreateAbsoluteUri(true) + ->setArguments([ + $this->argumentPrefix => [ + 'action' => 'delete' + ] + ]) + ->build(); + } + + /** + * @param int $user + * @param Event $event + * @return string|null + */ + protected function prepareUnsubscribeParameter(int $user, Event $event): ?string + { + $editCode = $this->getEditCode($user, $event); + + if ($editCode === null) { + return null; + } + + return GeneralUtility::implodeArrayForUrl( + $this->argumentPrefix, + [ + 'editcode' => $editCode, + 'event' => $event->getUid() + ] + ); + } + + /** + * @param int $user + * @param Event $event + * @return string|null + */ + protected function getEditCode(int $user, Event $event): ?string + { + $subscribers = $event->getSubscribers(); + + if (count($subscribers) === 0) { + return null; + } + + /** @var Subscriber $subscriber */ + foreach ($subscribers as $subscriber) { + if (!empty($subscriber->getEditcode()) && (int)$subscriber->getCustomerid() === $user) { + return $subscriber->getEditcode(); + } + } + + return null; + } +} diff --git a/Classes/Utility/TextUtility.php b/Classes/Utility/TextUtility.php index 6d0743e..f7b4d82 100644 --- a/Classes/Utility/TextUtility.php +++ b/Classes/Utility/TextUtility.php @@ -5,7 +5,7 @@ * This file is part of the TYPO3 CMS project. * * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 + * the terms of the GNU General Public License, either version 3 * of the License, or any later version. * * For the full copyright and license information, please read the diff --git a/Configuration/TypoScript/constants.typoscript b/Configuration/TypoScript/constants.typoscript index db7c005..cc0c2fe 100755 --- a/Configuration/TypoScript/constants.typoscript +++ b/Configuration/TypoScript/constants.typoscript @@ -12,6 +12,20 @@ plugin.tx_slubevents { # cat=plugin.tx_slubevents/100/a; type=int+; label=Default storage PID storagePid = } + + settings.api.users.0 { + # cat=plugin.tx_slubevents/api; type=string; label=Api username (0) + username = + # cat=plugin.tx_slubevents/api; type=string; label=Api password (0) + password = + } +} + +plugin.tx_slubevents_apieventlistuser { + settings { + # cat=plugin.tx_slubevents_apieventlistuser/settings; type=int; label=Unsubscribe PID + unsubscribePid = + } } module.tx_slubevents { diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript index e7d5429..2a7fdf8 100755 --- a/Configuration/TypoScript/setup.typoscript +++ b/Configuration/TypoScript/setup.typoscript @@ -67,6 +67,11 @@ plugin.tx_slubevents { # set baseURL in Emails - needed for images in description field #baseURL = http://www.example.com/ + + api.users.0 { + username = {$plugin.tx_slubevents.settings.api.users.0.username} + password = {$plugin.tx_slubevents.settings.api.users.0.password} + } } } @@ -101,6 +106,12 @@ module.tx_slubevents { } } +plugin.tx_slubevents_apieventlistuser { + settings { + unsubscribePid = {$plugin.tx_slubevents_apieventlistuser.settings.unsubscribePid} + } +} + # iCal export printCal = PAGE printCal { @@ -128,3 +139,27 @@ printCal { admPanel = 0 } } + +apiEventList = PAGE +apiEventList { + typeNum = 1452982642 + config { + # deactivate Standard-Header + disableAllHeaderCode = 1 + # disable cache notice + debug = 0 + } + 10 < tt_content.list.20.slubevents_apieventlist +} + +apiEventListUser = PAGE +apiEventListUser { + typeNum = 1452982643 + config { + # deactivate Standard-Header + disableAllHeaderCode = 1 + # disable cache notice + debug = 0 + } + 10 < tt_content.list.20.slubevents_apieventlistuser +} diff --git a/Documentation/Api/Index.rst b/Documentation/Api/Index.rst new file mode 100644 index 0000000..fcef26d --- /dev/null +++ b/Documentation/Api/Index.rst @@ -0,0 +1,99 @@ +.. ================================================== +.. FOR YOUR INFORMATION +.. -------------------------------------------------- +.. -*- coding: utf-8 -*- with BOM. + +.. include:: ../Includes.txt + +.. _api: + +API +=== + +.. contents:: + :local: + :depth: 1 + +Authorization +------------- + +To have access to the api, an authorization is required. You can configure users via typoscript. This configuration +is set global (plugin.tx_slubevents.settings.api.users) in the plugin and not for a specific plugin. + +============================================= ==================== ================================================ +Constants Type Comment +============================================= ==================== ================================================ +settings.api.users.0.username String Username to authorize access +settings.api.users.0.password String Password to authorize access +============================================= ==================== ================================================ + +Feel free to add further users. If you have one user with empty username or empty password, the authorization will fail. +``In default these values are empty for security.`` So, if you get no data, check this configuration. + +Event list +---------- + +The API delivers a json formatted list with events. You can manipulate the list with additional parameter. + +You have to call this API with a special page type. Just attach "?type=1452982642" to your project url and +typoscript calls the extension "slubevents" and the plugin "apieventlist". + +Additional parameter +^^^^^^^^^^^^^^^^^^^^ + +============================================= ==================== ================================================ +Parameter Type Comment +============================================= ==================== ================================================ +tx_slubevents_apieventlist[category] String|Integer Comma separated list of category ids +tx_slubevents_apieventlist[discipline] String|Integer Comma separated list of discipline ids +tx_slubevents_apieventlist[contact] String|Integer Comma separated list of contact ids +tx_slubevents_apieventlist[showPastEvents] Integer (0|1) Default is to show events beginning with today +tx_slubevents_apieventlist[showEventsFromNow] Integer (0|1) Additional setting for "showPastEvents" +tx_slubevents_apieventlist[limitByNextWeeks] Integer Set a limit for the next weeks +tx_slubevents_apieventlist[startTimestamp] Integer (Timestamp) Influence the start date, works together with stopTimestamp +tx_slubevents_apieventlist[stopTimestamp] Integer (Timestamp) Influence the stop date, works together with startTimestamp +tx_slubevents_apieventlist[sorting] String (asc|desc) Default value is ascending +tx_slubevents_apieventlist[limit] Integer Limit quantity of result data +============================================= ==================== ================================================ + +If you use these parameter and have trouble add "tx_slubevents_apieventlist" in [FE][cacheHash][cachedParametersWhiteList] and +[FE][cacheHash][excludedParameters]. + +Typoscript constants +^^^^^^^^^^^^^^^^^^^^ + +============================================= ==================== ================================================ +Constant Type Comment +============================================= ==================== ================================================ +settings.unsubscribePid Integer Set the page to unsubscribe an event. If not set, link to unsubscribe not generated +============================================= ==================== ================================================ + +Event list user +--------------- + +The API delivers a json formatted list with events subscribed by a specific user. You can manipulate the list with additional parameter. + +As extra parameter you have to specify the user. This api is in general separated from event list to be more flexible. +It has her own result structure. Compared with event list, a user event does not show the subscribers (it is the given user) but +has an unsubscribe link. + +You have to call this API with a special page type. Just attach "?type=1452982643" to your project url and +typoscript calls the extension "slubevents" and the plugin "apieventlist". + +Additional parameter +^^^^^^^^^^^^^^^^^^^^ + +You can manipulate the list with the same additional parameter like "event list". Just use different prefix +"tx_slubevents_apieventlist**user**" instead of "tx_slubevents_apieventlist". + +Necessary parameter +^^^^^^^^^^^^^^^^^^^ + +============================================= ==================== ================================================ +Parameter Type Comment +============================================= ==================== ================================================ +tx_slubevents_apieventlistuser[user] Integer Event -> subscribers -> customerid +============================================= ==================== ================================================ + +If you use these parameter and have trouble add "tx_slubevents_apieventlistuser" in [FE][cacheHash][cachedParametersWhiteList] and +[FE][cacheHash][excludedParameters]. diff --git a/Documentation/Index.rst b/Documentation/Index.rst index d7b0603..36d3865 100755 --- a/Documentation/Index.rst +++ b/Documentation/Index.rst @@ -58,6 +58,7 @@ slub_events Introduction/Index UsersManual/Index Plugins/Index + Api/Index AdministratorManual/Index KnownProblems/Index Support/Index diff --git a/ext_localconf.php b/ext_localconf.php index f2ae98a..9fb05d0 100755 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -50,6 +50,35 @@ ] ); +\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( + 'Slub.SlubEvents', + 'Apieventlist', + [ + \Slub\SlubEvents\Controller\Api\EventController::class => 'list', + ], + // non-cacheable actions + [ + \Slub\SlubEvents\Controller\Api\EventController::class => 'list', + ] +); + +\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( + 'Slub.SlubEvents', + 'Apieventlistuser', + [ + \Slub\SlubEvents\Controller\Api\EventController::class => 'listUser', + ], + // non-cacheable actions + [ + \Slub\SlubEvents\Controller\Api\EventController::class => 'listUser', + ] +); + +// Custom cache for category +if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['slubevents_category'])) { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['slubevents_category'] = []; +} + /** * Set storagePid by default to detect not configured page tree sections */ @@ -114,5 +143,4 @@ // register update wizard $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['slubEventsFileLocationUpdater'] = Slub\SlubEvents\Updates\FileLocationUpdater::class; - }