From d55d3455b7e0714f228238fac11b732fe32e5acd Mon Sep 17 00:00:00 2001
From: Georgi Mateev <georgi.mateev@sgalinski.de>
Date: Wed, 16 Oct 2024 18:19:55 +0300
Subject: [PATCH] [BUGFIX] Fix frontend filters

---
 Classes/Controller/VimeoController.php | 55 ++++++++++------
 Classes/Filter/DurationFilter.php      | 86 +++++---------------------
 Classes/Filter/FilterInterface.php     |  7 +++
 Classes/Service/VimeoService.php       | 54 ++++++++--------
 README.md                              |  6 +-
 5 files changed, 92 insertions(+), 116 deletions(-)

diff --git a/Classes/Controller/VimeoController.php b/Classes/Controller/VimeoController.php
index 8f565d2..d531076 100644
--- a/Classes/Controller/VimeoController.php
+++ b/Classes/Controller/VimeoController.php
@@ -80,15 +80,10 @@ class VimeoController extends ActionController {
 
 		$vimeoParameters = [
 			'id' => $id,
-			'clientId' => $clientId,
-			'clientSecret' => $clientSecret,
-			'personalAccessToken' => $personalAccessToken,
 			'queryString' => $queryString,
 		];
 
-
 		$filterIds = GeneralUtility::trimExplode(',', $filterIds);
-		$maxResultsWithFilters = (string) ($maxResults + count($filterIds));
 
 		// Get input values
 		$elementId = $this->request->getAttribute('currentContentObject')->data['uid'];
@@ -112,10 +107,13 @@ class VimeoController extends ActionController {
 		// add Filter Values
 		$vimeoParameters['filterValues'] = $filterValues;
 
-		$filterParameterBag =new FilterParameterBag($vimeoParameters,$filterInstances);
+		$filterParameterBag = new FilterParameterBag($vimeoParameters, $filterInstances);
 
 		$vimeoService = GeneralUtility::makeInstance(
 			VimeoService::class,
+			$clientId,
+			$clientSecret,
+			$personalAccessToken,
 			$filterParameterBag,
 			$this->cache
 		);
@@ -126,6 +124,12 @@ class VimeoController extends ActionController {
 
 			// Use the possibly modified parameters
 			$response = $vimeoService->getVimeoData($filterParameterBag);
+			if (count($response['items']) < 1) {
+				$this->view->assignMultiple([
+					'notFound' => TRUE,
+					'pluginContentData' => $this->request->getAttribute('currentContentObject')->data
+				]);
+			}
 
 			if ($response === NULL) {
 				return $this->htmlResponse();
@@ -136,11 +140,11 @@ class VimeoController extends ActionController {
 		}
 
 		// Dispatch the AfterVimeoCallEvent
-		$afterYoutubeCallEvent = new AfterVimeoCallEvent($response);
-		$this->eventDispatcher->dispatch($afterYoutubeCallEvent);
+		$afterVimeoCallEvent = new AfterVimeoCallEvent($response);
+		$this->eventDispatcher->dispatch($afterVimeoCallEvent);
 
 		// Use the possibly modified response
-		$response = $afterYoutubeCallEvent->getResponse();
+		$response = $afterVimeoCallEvent->getResponse();
 
 		if (is_array($response['items'])) {
 			$response['items'] = array_filter($response['items'], function ($item) use ($filterIds) {
@@ -176,16 +180,8 @@ class VimeoController extends ActionController {
 				 * Just checks if the end of `link` is the same as `videoId`, and if not, it replaces `link`.
 				 * This is needed as a workaround so that glightbox can detect the video as a vimeo video correctly in the frontend.
 				 */
-				$endOfLink = substr($item['link'], -strlen($item['videoId']));
-				if (!((int) $endOfLink === $item['videoId'])) {
-					$item['link'] = strstr($item['link'], '.com/', TRUE) . '.com/' . $item['videoId'];
-				}
+				$this->fixVideoLink($item);
 
-				if (isset($item['embed']['html'])) {
-					$embedLink = [];
-					preg_match('/src="([^"]+)"/i', $item['embed']['html'], $embedLink);
-					$item['embedLink'] = html_entity_decode($embedLink[1]);
-				}
 			}
 
 			unset($item);
@@ -275,6 +271,25 @@ class VimeoController extends ActionController {
 		return $response;
 	}
 
+	/**
+	 * Fixes the embed Link format
+	 *
+	 * @param mixed $item
+	 * @return array
+	 */
+	private function fixVideoLink(array &$item): void {
+		$endOfLink = substr($item['link'], -strlen($item['videoId']));
+		if (!((int) $endOfLink === $item['videoId'])) {
+			$item['link'] = strstr($item['link'], '.com/', TRUE) . '.com/' . $item['videoId'];
+		}
+
+		if (isset($item['embed']['html'])) {
+			$embedLink = [];
+			preg_match('/src="([^"]+)"/i', $item['embed']['html'], $embedLink);
+			$item['embedLink'] = html_entity_decode($embedLink[1]);
+		}
+	}
+
 	/**
 	 * Applies the filterIds setting from the plugin configuration
 	 *
@@ -351,7 +366,9 @@ class VimeoController extends ActionController {
 
 			if (class_exists($filterConfig['filterClass'])) {
 				$specificFilterValues = $filterValues[$filterName] ?? [];
-				$filterInstance = GeneralUtility::makeInstance($filterConfig['filterClass'], $specificFilterValues, $filterConfig);
+				$filterInstance = GeneralUtility::makeInstance(
+					$filterConfig['filterClass'], $specificFilterValues, $filterConfig
+				);
 
 				// Register filter to modify the request before API call
 				$filterInstance->setFilterValues($specificFilterValues);
diff --git a/Classes/Filter/DurationFilter.php b/Classes/Filter/DurationFilter.php
index ec90e4f..99f7abf 100644
--- a/Classes/Filter/DurationFilter.php
+++ b/Classes/Filter/DurationFilter.php
@@ -50,16 +50,8 @@ class DurationFilter implements FilterInterface {
 	 */
 	public function modifyRequest(array &$parameters): void {
 		$this->originalParameters = $parameters;
-		if (isset($this->filterValues['duration']) && !empty($this->filterValues['duration'])) {
-			if ($this->filterValues['duration'] === "1") {
-				$parameters['min_duration'] = 0;
-				$parameters['max_duration'] = 5*60;
-			}
-
-			if ($this->filterValues['duration'] === "2") {
-				$parameters['min_duration'] = 5*60;
-			}
-		}
+		$parameters['fields'] .= ',duration';
+		$parameters['per_page'] = 100;
 	}
 
 	/**
@@ -67,76 +59,28 @@ class DurationFilter implements FilterInterface {
 	 * In this case, we won't filter the results, as we're modifying the query.
 	 */
 	public function modifyResponse(array &$data): void {
-		if (!(isset($this->filterValues['duration']) && !empty($this->filterValues['duration']))) {
-			return;
-		}
-
-		$youTubeService = GeneralUtility::makeInstance(YoutubeService::class);
-
-		$count = 0;
-		$filteredItems = [];
-		foreach ($data['items'] as $key => $videoData) {
-			if ($count === (int) $this->originalParameters['maxResults']) {
-				break;
-			}
-
-			$videoId = '';
-			if (isset($videoData['snippet']['resourceId']['videoId'])) {
-				$videoId = trim($videoData['snippet']['resourceId']['videoId']);
-			}
+		$counter = 0;
+		if (isset($this->filterValues['duration']) && !empty($this->filterValues['duration'])) {
+			foreach ($data['items'] as $key => $item) {
+				$counter++;
 
-			if (!$videoId && isset($videoData['id'])) {
-				$videoId = $videoData['id']['videoId'] ?? $videoData['id'];
-				// This is a check, because the $videoData['id'] can be a whole sub-channel-id.
-				if (is_array($videoId)) {
-					continue;
+				if ($counter >= $this->originalParameters['per_page']) {
+					break;
 				}
 
-				$videoId = trim($videoId);
-			}
-
-			if (!$videoId) {
-				continue;
-			}
-
-			$params = $this->originalParameters;
-			$params['id'] = $videoId;
-			$url = $youTubeService->getApiUrl($params, []);
-			$url = str_replace('snippet', 'contentDetails', $url);
-
-			$shouldInclude = FALSE;
-			try {
-				$result = $youTubeService->getJsonAsArray(new FilterParameterBag([
-					'id' => '',
-					'maxResults' => '10',
-					'key' => $this->originalParameters['key'],
-					'url' => $url
-				]));
-
-
-				if (isset($result['items'][0]['contentDetails']['duration'])) {
-					$durationSeconds = $this->youtubeDurationToSeconds(
-						$result['items'][0]['contentDetails']['duration']
-					);
-					if ($this->filterValues['duration'] === "1" && $durationSeconds <= 5 * 60) {
-						$shouldInclude = TRUE;
+				if ($this->filterValues['duration'] === "1") {
+					if ($item['duration'] > 1*60) {
+						unset($data['items'][$key]);
 					}
+				}
 
-					if ($this->filterValues['duration'] === "2" && $durationSeconds > 5 * 60) {
-						$shouldInclude = TRUE;
+				if ($this->filterValues['duration'] === "2") {
+					if ($item['duration'] <= 1*60) {
+						unset($data['items'][$key]);
 					}
 				}
-			} catch (Exception $exception) {
-				// No duration data found
-			}
-
-			if ($shouldInclude) {
-				$filteredItems[] = $videoData;
-				$count++;
 			}
 		}
-
-		$data['items'] = $filteredItems;
 	}
 
 	public function getFilterValues(): array {
diff --git a/Classes/Filter/FilterInterface.php b/Classes/Filter/FilterInterface.php
index a00aa0a..33fc370 100644
--- a/Classes/Filter/FilterInterface.php
+++ b/Classes/Filter/FilterInterface.php
@@ -44,4 +44,11 @@ interface FilterInterface {
 	 * @return void
 	 */
 	public function modifyRequest(array &$parameters): void;
+
+	/**
+	 * Returns the filter values
+	 *
+	 * @return array
+	 */
+	public function getFilterValues(): array;
 }
diff --git a/Classes/Service/VimeoService.php b/Classes/Service/VimeoService.php
index ad8865e..8fa0fd0 100644
--- a/Classes/Service/VimeoService.php
+++ b/Classes/Service/VimeoService.php
@@ -100,14 +100,16 @@ class VimeoService implements LoggerAwareInterface {
 	 * @param string $clientId
 	 * @param string $clientSecret
 	 * @param string $personalAccessToken
+	 * @param FilterParameterBag $filterParameterBag
+	 * @param FrontendInterface $cache
 	 */
 	public function __construct(
+		string $clientId,
+		string $clientSecret,
+		string $personalAccessToken,
 		FilterParameterBag $filterParameterBag,
 		FrontendInterface $cache
 	) {
-		$clientId = $filterParameterBag->get('clientId');
-		$clientSecret = $filterParameterBag->get('clientSecret');
-		$personalAccessToken = $filterParameterBag->get('personalAccessToken');
 		$this->vimeoApiClient = new Vimeo($clientId, $clientSecret, $personalAccessToken);
 		// We only need to request an unauthenticated token, if there is no personal access token provided already.
 		// An authenticated access token with the public scope is identical to an unauthenticated access token,
@@ -134,9 +136,15 @@ class VimeoService implements LoggerAwareInterface {
 			$parameterBag->set('query', $queryString);
 		}
 
+		$filterValues = [];
+		foreach ($filters as $filter) {
+			$filterValues[] = $filter->getFilterValues();
+		}
+		$filtersHash = md5(json_encode($filterValues));
+
 		$response = [];
 		$this->maxResultsPerPage = $maxResults;
-		$cacheKey = 'sg_vimeo' . sha1($vimeoId . $maxResults . $queryString);
+		$cacheKey = 'sg_vimeo' . sha1($vimeoId . $maxResults . $queryString . $filtersHash);
 		$disableVimeoCache = (bool) GeneralUtility::_GP('disableVimeoCache');
 		if (!$disableVimeoCache) {
 			$cachedResult = $this->cache->get($cacheKey);
@@ -145,8 +153,6 @@ class VimeoService implements LoggerAwareInterface {
 			}
 		}
 
-
-
 		if (str_starts_with($vimeoId, 'showcase')) {
 			$showcaseId = explode('/', $vimeoId)[1];
 			$response['items'] = $this->addVideoIdsToResponse($this->getShowcaseVideos((int) $showcaseId, $parameterBag));
@@ -164,7 +170,6 @@ class VimeoService implements LoggerAwareInterface {
 			$filter->modifyResponse($response);
 		}
 
-
 		if (!$disableVimeoCache) {
 			$this->cache->set($cacheKey, $response, [], self::CACHE_LIFETIME_IN_SECONDS);
 		}
@@ -233,7 +238,7 @@ class VimeoService implements LoggerAwareInterface {
 			$this->amountOfVideosFetched += $amountOfVideosInResponse;
 			$this->paginatedResponseData[] = $response['body']['data'];
 			$nextUrl = $response['body']['paging']['next'];
-			if ($this->amountOfVideosFetched === $this->maxResultsPerPage || $nextUrl === NULL) {
+			if ($this->amountOfVideosFetched >= $this->maxResultsPerPage || $nextUrl === NULL) {
 				// return flattened array here, so that we don't end up with one sub array per pagination page
 				return array_merge(...$this->paginatedResponseData);
 			}
@@ -270,14 +275,13 @@ class VimeoService implements LoggerAwareInterface {
 		// use field filtering, to save on quota, see: https://developer.vimeo.com/guidelines/rate-limiting
 		$fieldsToSelect = 'uri,name,description,link,embed,pictures,release_time,width,height';
 
-		$filterParameterBag->set('fieldsToSelect', $fieldsToSelect);
+		$parameters = [];
+		$parameters['fields'] = $fieldsToSelect;
 
 		foreach ($filterParameterBag->getFilterInstances() as $filter) {
-			$filter->modifyRequest($filterParameterBag->all());
+			$filter->modifyRequest($parameters);
 		}
 
-		$parameters = $filterParameterBag->all();
-
 		$query = http_build_query($parameters);
 
 		try {
@@ -301,16 +305,16 @@ class VimeoService implements LoggerAwareInterface {
 		// use field filtering, to save on quota, see: https://developer.vimeo.com/guidelines/rate-limiting
 		$fieldsToSelect = 'uri,name,description,link,embed,pictures,release_time,width,height';
 
-		$filterParameterBag->set('fieldsToSelect', $fieldsToSelect);
-		$filterParameterBag->set('sort', 'default');
-		$filterParameterBag->set('per_page', $this->maxResultsPerPage);
+		$parameters = [
+			'fields' => $fieldsToSelect,
+			'sort'	=> 'default',
+			'per_page'	=> $this->maxResultsPerPage
+		];
 
 		foreach ($filterParameterBag->getFilterInstances() as $filter) {
-			$filter->modifyRequest($filterParameterBag->all());
+			$filter->modifyRequest($parameters);
 		}
 
-		$parameters = $filterParameterBag->all();
-
 		$query = http_build_query($parameters);
 
 		try {
@@ -337,17 +341,17 @@ class VimeoService implements LoggerAwareInterface {
 	public function getShowcaseVideos(string $showcaseId, FilterParameterBag $filterParameterBag): ?array {
 		// use field filtering, to save on quota, see: https://developer.vimeo.com/guidelines/rate-limiting
 		$fieldsToSelect = 'uri,name,description,link,embed,pictures,release_time,width,height';
-		$filterParameterBag->set('fieldsToSelect', $fieldsToSelect);
-		// sort videos by the user-selected default: ?sort=default
-		$filterParameterBag->set('sort', 'default');
-		$filterParameterBag->set('per_page', $this->maxResultsPerPage);
+
+		$parameters = [
+			'fields' => $fieldsToSelect,
+			'sort'	=> 'default',
+			'per_page'	=> $this->maxResultsPerPage
+		];
 
 		foreach ($filterParameterBag->getFilterInstances() as $filter) {
-			$filter->modifyRequest($filterParameterBag->all());
+			$filter->modifyRequest($parameters);
 		}
 
-		$parameters = $filterParameterBag->all();
-
 		$query = http_build_query($parameters);
 
 		try {
diff --git a/README.md b/README.md
index 3a06f7f..1e18240 100644
--- a/README.md
+++ b/README.md
@@ -316,7 +316,11 @@ Once it runs it will create the file and we can commit the new version and use i
 2. Add a new content element and select the Vimeo plugin from the list.
 3. Provide the necessary Vimeo details in the plugin settings (like Vimeo API key, video/channel/playlist ID, max results, etc.).
 
-### **Step 2: Configuring Filters via FlexForm**
+### **Step 2: Disable caching for the extension**
+1. In the TYPO3 backend, go to Settings -> Extensions and open the sg_vimeo settings. Tick the box that says 'uncached' and save.
+2. This way the the extension will not cache the results and the the data will be loaded dynamically based on the filter settings
+
+### **Step 3: Configuring Filters via FlexForm**
 1. In the plugin's FlexForm settings, you will see a section for "Filters."
 2. Depending on your site configuration, filters will be available in a select field. You can select the filters you want to display (like search term, video duration, etc.).
 3. Save your settings and publish the page.
-- 
GitLab