Skip to content
Snippets Groups Projects
Commit fd39c9ad authored by Georgi's avatar Georgi
Browse files

[BUGFIX] Code style

parent f393dc92
No related branches found
No related tags found
1 merge request!6Feature make standalone
Showing
with 1296 additions and 1231 deletions
<?php
namespace SGalinski\SgVimeo\Backend;
/***************************************************************
* Copyright notice
*
......@@ -26,39 +24,39 @@ namespace SGalinski\SgVimeo\Backend;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use Exception;
namespace SGalinski\SgVimeo\Backend;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use SGalinski\SgVimeo\Service\LicenceCheckService;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* Class Ajax
*
* @package SGalinski\SgVimeo\Backend
*/
class Ajax {
/**
* Checks whether the license is valid
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @return ResponseInterface
* @throws \InvalidArgumentException
* @throws Exception
*/
public function checkLicense(
ServerRequestInterface $request,
ResponseInterface $response = NULL
) {
if ($response === NULL) {
$response = new Response();
}
class Ajax
{
/**
* Checks whether the license is valid
*
* @param ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface|null $response
* @return ResponseInterface
*/
public function checkLicense(
ServerRequestInterface $request,
ResponseInterface $response = NULL
)
{
if ($response === NULL) {
$response = new Response();
}
LicenceCheckService::setLastAjaxNotificationCheckTimestamp();
$responseData = LicenceCheckService::getLicenseCheckResponseData(TRUE);
$response->getBody()->write(json_encode($responseData));
return $response;
}
LicenceCheckService::setLastAjaxNotificationCheckTimestamp();
$responseData = LicenceCheckService::getLicenseCheckResponseData(TRUE);
$response->getBody()->write(json_encode($responseData));
return $response;
}
}
......@@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\FileReference;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Service\ImageService;
......@@ -124,7 +125,7 @@ class VimeoController extends ActionController {
]
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '12.0.0', '>=')) {
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '12.0.0', '>=')) {
return $this->jsonResponse();
}
}
......
......@@ -17,6 +17,7 @@
* GNU General Public License for more details.
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace SGalinski\SgVimeo\Form\Element;
use SGalinski\SgVimeo\Service\LicenceCheckService;
......@@ -35,20 +36,20 @@ class LicenceStatus extends AbstractFormElement
return [];
}
switch ($responseData['error']) {
case 1:
$errorOrWarning = 'danger';
break;
case 2:
$errorOrWarning = 'warning';
break;
default:
$errorOrWarning = 'success';
}
switch ($responseData['error']) {
case 1:
$errorOrWarning = 'danger';
break;
case 2:
$errorOrWarning = 'warning';
break;
default:
$errorOrWarning = 'success';
}
$message = '<div class="alert alert-'. $errorOrWarning .'" role="'
$message = '<div class="alert alert-' . $errorOrWarning . '" role="'
. $errorOrWarning
. '">' . $responseData['message'] . '</div>';
. '">' . $responseData['message'] . '</div>';
$resultArray['html'] = $message;
return $resultArray;
}
......
<?php
/**
*
* Copyright notice
......@@ -38,22 +39,25 @@ use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
* @package SGalinski\ProjectBase\Hook
* @author Georgi Mateev <georgi.mateev@sgalinski.de>
*/
class LicenceCheckHook {
/**
* Add JavaScript to display the expiring license warning
*/
protected function addAjaxLicenseCheck() {
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$pageRenderer->loadRequireJsModule('TYPO3/CMS/SgVimeo/Backend/LicenseNotification');
}
class LicenceCheckHook
{
/**
* Add JavaScript to display the expiring license warning
*/
protected function addAjaxLicenseCheck()
{
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$pageRenderer->loadRequireJsModule('TYPO3/CMS/SgVimeo/Backend/LicenseNotification');
}
/**
* Checks if the license key is OK
*
* @param array $configuration
* @param BackendController $parentBackendController
*/
public function performLicenseCheck(array $configuration, BackendController $parentBackendController) {
/**
* Checks if the license key is OK
*
* @param array $configuration
* @param BackendController $parentBackendController
*/
public function performLicenseCheck(array $configuration, BackendController $parentBackendController)
{
if (!LicenceCheckService::isTYPO3VersionSupported()
|| !LicenceCheckService::isTimeForNextCheck()
|| LicenceCheckService::isInDevelopmentContext()
......@@ -61,6 +65,6 @@ class LicenceCheckHook {
return;
}
$this->addAjaxLicenseCheck();
}
$this->addAjaxLicenseCheck();
}
}
<?php
namespace SGalinski\SgVimeo\Hooks\PageLayoutView;
/***************************************************************
* Copyright notice
* (c) sgalinski Internet Services (https://www.sgalinski.de)
......@@ -20,6 +18,8 @@ namespace SGalinski\SgVimeo\Hooks\PageLayoutView;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace SGalinski\SgVimeo\Hooks\PageLayoutView;
use SGalinski\SgVimeo\Preview\PreviewService;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\PageLayoutView;
......@@ -124,7 +124,8 @@ if (interface_exists('TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface
}
}
} else {
class PluginRenderer {
class PluginRenderer
{
}
}
......@@ -58,8 +58,7 @@ class PreviewRenderer implements PreviewRendererInterface {
* @return string
*/
public function renderPageModulePreviewContent(GridColumnItem $item): string {
$view = $this->previewService->getPluginView($item->getRecord());
return $view->render();
return $this->previewService->getPluginView($item->getRecord())->render();
}
/**
......
......@@ -17,6 +17,7 @@
* GNU General Public License for more details.
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace SGalinski\SgVimeo\Preview;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -25,58 +26,61 @@ use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* PreviewService, to get Views while we have to duplicate previewCode
*/
class PreviewService {
public const RETURNTYPE_ARR = 'array';
class PreviewService
{
public const RETURNTYPE_ARR = 'array';
public function getPluginView(array $row): StandaloneView {
/** @var StandaloneView $view */
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setPartialRootPaths(['EXT:sg_vimeo/Resources/Private/Partials/Backend']);
$view->setTemplateRootPaths(['EXT:sg_vimeo/Resources/Private/Templates/Vimeo']);
$view->setTemplate('Backend.html');
$view->assign('uid', $row['uid']);
public function getPluginView(array $row): StandaloneView
{
/** @var StandaloneView $view */
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setPartialRootPaths(['EXT:sg_vimeo/Resources/Private/Partials/Backend']);
$view->setTemplateRootPaths(['EXT:sg_vimeo/Resources/Private/Templates/Vimeo']);
$view->setTemplate('Backend.html');
$view->assign('uid', $row['uid']);
// Get available plugin settings and their values from flexform
$pluginConfiguration = GeneralUtility::xml2array(
$row['pi_flexform'],
'T3DataStructure'
)['data']['sDEF']['lDEF'];
// Get available plugin settings and their values from flexform
$pluginConfiguration = GeneralUtility::xml2array(
$row['pi_flexform'],
'T3DataStructure'
)['data']['sDEF']['lDEF'];
$templateData = [
'vimeoId' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.id'),
'maxResults' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.maxResults'),
'showTitle' => (int) ($this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showTitle') ?? 1),
'showDescription' => (int) ($this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showDescription') ?? 1),
'disableLightbox' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.disableLightbox'),
'disableLightboxMobile' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.disableLightboxMobile'),
'aspectRatio' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.aspectRatio'),
'thumbnailType' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.thumbnailType'),
'thumbnailImagesCount' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.thumbnailImages'),
'showApiResult' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showApiResult'),
'urlParameters' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.urlParameters'),
];
$templateData = [
'vimeoId' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.id'),
'maxResults' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.maxResults'),
'showTitle' => (int)($this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showTitle') ?? 1),
'showDescription' => (int)($this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showDescription') ?? 1),
'disableLightbox' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.disableLightbox'),
'disableLightboxMobile' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.disableLightboxMobile'),
'aspectRatio' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.aspectRatio'),
'thumbnailType' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.thumbnailType'),
'thumbnailImagesCount' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.thumbnailImages'),
'showApiResult' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showApiResult'),
'urlParameters' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.urlParameters'),
];
$view->assign('data', $templateData);
return $view;
}
$view->assign('data', $templateData);
return $view;
}
/**
* @param array $conf
* @param string $key
* @param string $returnType
* @return array|mixed|string
*/
private function passVDefOnKeyToTemplate(array $conf, string $key, string $returnType = '') {
if (isset($conf[$key])) {
return $conf[$key]['vDEF'];
}
/**
* @param array $conf
* @param string $key
* @param string $returnType
* @return array|mixed|string
*/
private function passVDefOnKeyToTemplate(array $conf, string $key, string $returnType = '')
{
if (isset($conf[$key])) {
return $conf[$key]['vDEF'];
}
// check if we got a possible returntype:
if ($returnType === self::RETURNTYPE_ARR) {
return [];
}
// check if we got a possible returntype:
if ($returnType === self::RETURNTYPE_ARR) {
return [];
}
return '';
}
return '';
}
}
<?php
namespace SGalinski\SgVimeo\Service;
/***************************************************************
* Copyright notice
*
......@@ -26,73 +24,78 @@ namespace SGalinski\SgVimeo\Service;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace SGalinski\SgVimeo\Service;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
/**
* Class SGalinski\SgVimeo\Service\ExtensionSettingsService
*/
class ExtensionSettingsService {
const SETTING_LICENSE = 'key';
const SETTING_FOLDER = 'folder';
const SETTING_HIDE_MODULE_IN_PRODUCTION_CONTEXT = 'hideModuleInProductionContext';
class ExtensionSettingsService
{
const SETTING_LICENSE = 'key';
const SETTING_FOLDER = 'folder';
const SETTING_HIDE_MODULE_IN_PRODUCTION_CONTEXT = 'hideModuleInProductionContext';
/**
* @var array Default settings mapped to constants.
*/
protected static $defaultValueMap = [
self::SETTING_FOLDER => 'fileadmin/sg_vimeo/',
self::SETTING_HIDE_MODULE_IN_PRODUCTION_CONTEXT => FALSE,
];
/**
* @var array Default settings mapped to constants.
*/
protected static $defaultValueMap = [
self::SETTING_FOLDER => 'fileadmin/sg_vimeo/',
self::SETTING_HIDE_MODULE_IN_PRODUCTION_CONTEXT => FALSE,
];
/**
* Returns the setting of one of the constants of this class.
*
* @param string $settingKey
* @return mixed
*/
public static function getSetting($settingKey) {
if (VersionNumberUtility::convertVersionNumberToInteger(VersionNumberUtility::getCurrentTypo3Version()) < 9000000) {
// the "options" parameter of unserialize exists since PHP 7.0.0
if (version_compare(phpversion(), '7.0.0', '>=')) {
$configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['sg_vimeo'], [FALSE]);
} else {
$configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['sg_vimeo']);
}
} else {
$configuration = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sg_vimeo'] ?? [];
}
/**
* Returns the setting of one of the constants of this class.
*
* @param string $settingKey
* @return mixed
*/
public static function getSetting($settingKey)
{
if (VersionNumberUtility::convertVersionNumberToInteger(VersionNumberUtility::getCurrentTypo3Version()) < 9000000) {
// the "options" parameter of unserialize exists since PHP 7.0.0
if (version_compare(phpversion(), '7.0.0', '>=')) {
$configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['sg_vimeo'], [FALSE]);
} else {
$configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['sg_vimeo']);
}
} else {
$configuration = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sg_vimeo'] ?? [];
}
$setting = '';
if (isset(self::$defaultValueMap[$settingKey])) {
$setting = self::$defaultValueMap[$settingKey];
}
$setting = '';
if (isset(self::$defaultValueMap[$settingKey])) {
$setting = self::$defaultValueMap[$settingKey];
}
if (isset($configuration[$settingKey])) {
$setting = self::postProcessSetting($configuration[$settingKey], $settingKey);
}
if (isset($configuration[$settingKey])) {
$setting = self::postProcessSetting($configuration[$settingKey], $settingKey);
}
return $setting;
}
return $setting;
}
/**
* Post process of the given setting, by the given setting key.
*
* @param mixed $value
* @param string $settingKey
* @return mixed
*/
protected static function postProcessSetting($value, $settingKey) {
if ($settingKey === self::SETTING_FOLDER) {
$value = trim($value, " \t\n\r\0\x0B\/") . '/';
/**
* Post process of the given setting, by the given setting key.
*
* @param mixed $value
* @param string $settingKey
* @return mixed
*/
protected static function postProcessSetting($value, $settingKey)
{
if ($settingKey === self::SETTING_FOLDER) {
$value = trim($value, " \t\n\r\0\x0B\/") . '/';
if (strpos($value, 'EXT:') === 0) {
$value = 'typo3conf/ext/' . substr($value, 4);
}
}
if (strpos($value, 'EXT:') === 0) {
$value = 'typo3conf/ext/' . substr($value, 4);
}
}
// TYPO3 6 stores all settings as strings, some are expected to be booleans, though.
$value = ($value === 'FALSE') ? FALSE : $value;
// TYPO3 6 stores all settings as strings, some are expected to be booleans, though.
$value = ($value === 'FALSE') ? FALSE : $value;
return $value;
}
return $value;
}
}
This diff is collapsed.
......@@ -37,259 +37,269 @@ use Vimeo\Vimeo;
/**
* Vimeo Helper Service
*/
class VimeoService implements LoggerAwareInterface {
use LoggerAwareTrait;
protected const API_CHANNEL = '/channels/';
protected const API_VIDEO = '/videos/';
protected const API_SHOWCASE = '/me/albums/';
/**
* https://developer.vimeo.com/api/authentication#supported-scopes
*/
protected const SCOPE = 'public';
public const CACHE_LIFETIME_IN_SECONDS = 86400;
/**
* @var FrontendInterface
*/
protected $cache;
/**
* @var Vimeo
*/
protected $vimeoApiClient;
/**
* @var array
*/
protected $paginatedResponseData;
/**
* @var int
* Used for the `per_page` param, for the vimeo API requests.
* Use `per_page` to set the number of representations per page from the default value of 25 up to a maximum value of 100.
*/
protected $maxResultsPerPage = 25;
/**
* @var int
* The amount of videos fetched within the current pagination request
*/
protected $amountOfVideosFetched = 0;
/**
* VimeoService constructor.
*
* @param string $clientId
* @param string $clientSecret
* @param string $personalAccessToken
*/
public function __construct(
string $clientId,
string $clientSecret,
string $personalAccessToken,
FrontendInterface $cache
) {
$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,
// except that you can use the /me endpoint to refer to the currently logged-in user.
// Accessing /me with an unauthenticated access token generates an error.
// See also: https://developer.vimeo.com/api/authentication
if ($personalAccessToken === '') {
$this->requestAccessToken();
}
$this->cache = $cache;
}
/**
* @param string $vimeoId can be a video id, showcase id or a channel name
* @param int $maxResults
* @return array|null
*/
public function getVimeoData(string $vimeoId, int $maxResults): ?array {
$response = [];
$this->maxResultsPerPage = $maxResults;
$cacheKey = 'sg_vimeo' . sha1($vimeoId . $maxResults);
$disableVimeoCache = (bool) GeneralUtility::_GP('disableVimeoCache');
if (!$disableVimeoCache) {
$cachedResult = $this->cache->get($cacheKey);
if ($cachedResult) {
return $cachedResult;
}
}
if (strpos($vimeoId, 'showcase') === 0) {
$showcaseId = explode('/', $vimeoId)[1];
$response['items'] = $this->addVideoIdsToResponse($this->getShowcaseVideos((int) $showcaseId));
$response['kind'] = 'showcase';
} elseif (strpos($vimeoId, 'channel') === 0) {
$channelId = explode('/', $vimeoId)[1];
$response['items'] = $this->addVideoIdsToResponse($this->getChannelVideos($channelId));
$response['kind'] = 'channel';
} else {
$response['items'] = $this->addVideoIdsToResponse($this->getVideo((int) $vimeoId));
$response['kind'] = 'video';
}
if (!$disableVimeoCache) {
$this->cache->set($cacheKey, $response, [], self::CACHE_LIFETIME_IN_SECONDS);
}
return $response;
}
/**
* Extracts the video id from the video's canonical relative URI and adds it to each entry with the key 'videoId'
*
* @param array|null $response
* @return array|null
*/
protected function addVideoIdsToResponse(?array $response): ?array {
if (!is_array($response)) {
return NULL;
}
foreach ($response as $index => $item) {
if (array_key_exists('uri', $item)) {
$uri = $item['uri'];
$videoId = (int) str_replace('/videos/', '', $uri);
$response[$index]['videoId'] = $videoId;
}
}
return $response;
}
/**
* Unauthenticated API requests must generate an access token.
* (Access tokens without a user. These tokens can view only public data.)
* You should not generate a new access token for each request.
* Instead, request an access token once and use it forever.
*/
protected function requestAccessToken(): void {
$token = $this->vimeoApiClient->clientCredentials(self::SCOPE);
if (isset($token['body']['access_token'])) {
$this->vimeoApiClient->setToken($token['body']['access_token']);
}
}
/**
* Returns the response body, wrapped in an array if the response contains a single item.
* If the response is a paginated response, all items are fetched until the maxResultsPerPage is reached,
* or no $nextUrl is available anymore (last page reached). Since the flexform allows a max value of 100 currently,
* this function will never go past the first page, since the vimeo API allows a value of 100 as max value for the `per_page` parameter.
*
* @param array|null $response
* @return array|null
*/
protected function preprocessApiResponse(?array $response): ?array {
if (!is_array($response)) {
return NULL;
}
// log error & return here, since the response was not OK
if ($response['status'] !== 200) {
$this->logger->error('sg_vimeo API Request failed, got the following response:', $response);
return [$response['body']];
}
// @TODO: we could check $response['headers‘]['X-RateLimit-Remaining'] here for remaining quota
if (array_key_exists('paging', $response['body'])) {
$amountOfVideosInResponse = is_countable($response['body']['data']) ? count($response['body']['data']) : 0;
$this->amountOfVideosFetched += $amountOfVideosInResponse;
$this->paginatedResponseData[] = $response['body']['data'];
$nextUrl = $response['body']['paging']['next'];
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);
}
$this->fetchPaginatedResult($nextUrl);
}
// wrap response body in an array, so that we can treat all return values the same in the template
return [$response['body']];
}
/**
* @param string $nextUrl
* @return array|null
*/
protected function fetchPaginatedResult(string $nextUrl): ?array {
try {
$response = $this->vimeoApiClient->request($nextUrl);
} catch (VimeoRequestException $e) {
return NULL;
}
return $this->preprocessApiResponse($response);
}
/**
* Returns a single video for the given $videoId
*
* @see https://developer.vimeo.com/api/reference/videos#get_video
* @param int $videoId
* @return array|null
*/
public function getVideo(int $videoId): ?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';
try {
$response = $this->vimeoApiClient->request(self::API_VIDEO . $videoId . '?fields=' . $fieldsToSelect);
} catch (VimeoRequestException $e) {
$response = NULL;
throw $e;
}
return $this->preprocessApiResponse($response);
}
/**
* Returns all videos for the given $channelIdentifier
*
* @see https://developer.vimeo.com/api/reference/channels#get_channel_videos
* @param string $channelIdentifier
* @return array|null
*/
public function getChannelVideos(string $channelIdentifier): ?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';
try {
$response = $this->vimeoApiClient->request(
self::API_CHANNEL . $channelIdentifier . self::API_VIDEO . '?fields=' . $fieldsToSelect . '&per_page=' . $this->maxResultsPerPage
);
} catch (VimeoRequestException $e) {
$response = NULL;
throw $e;
}
return $this->preprocessApiResponse($response);
}
/**
* Returns all videos for the given $showcaseId
*
* @see https://developer.vimeo.com/api/reference/showcases#get_showcase
* @param string $showcaseId
* @return array|null
*/
public function getShowcaseVideos(string $showcaseId): ?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';
// sort videos by the user-selected default: ?sort=default
try {
$response = $this->vimeoApiClient->request(
self::API_SHOWCASE . $showcaseId . self::API_VIDEO . '?sort=default&fields=' . $fieldsToSelect . '&per_page=' . $this->maxResultsPerPage
);
} catch (VimeoRequestException $e) {
$response = NULL;
throw $e;
}
return $this->preprocessApiResponse($response);
}
class VimeoService implements LoggerAwareInterface
{
use LoggerAwareTrait;
protected const API_CHANNEL = '/channels/';
protected const API_VIDEO = '/videos/';
protected const API_SHOWCASE = '/me/albums/';
/**
* https://developer.vimeo.com/api/authentication#supported-scopes
*/
protected const SCOPE = 'public';
public const CACHE_LIFETIME_IN_SECONDS = 86400;
/**
* @var FrontendInterface
*/
protected $cache;
/**
* @var Vimeo
*/
protected $vimeoApiClient;
/**
* @var array
*/
protected $paginatedResponseData;
/**
* @var int
* Used for the `per_page` param, for the vimeo API requests.
* Use `per_page` to set the number of representations per page from the default value of 25 up to a maximum value of 100.
*/
protected $maxResultsPerPage = 25;
/**
* @var int
* The amount of videos fetched within the current pagination request
*/
protected $amountOfVideosFetched = 0;
/**
* VimeoService constructor.
*
* @param string $clientId
* @param string $clientSecret
* @param string $personalAccessToken
*/
public function __construct(
string $clientId,
string $clientSecret,
string $personalAccessToken,
FrontendInterface $cache
)
{
$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,
// except that you can use the /me endpoint to refer to the currently logged-in user.
// Accessing /me with an unauthenticated access token generates an error.
// See also: https://developer.vimeo.com/api/authentication
if ($personalAccessToken === '') {
$this->requestAccessToken();
}
$this->cache = $cache;
}
/**
* @param string $vimeoId can be a video id, showcase id or a channel name
* @param int $maxResults
* @return array|null
*/
public function getVimeoData(string $vimeoId, int $maxResults): ?array
{
$response = [];
$this->maxResultsPerPage = $maxResults;
$cacheKey = 'sg_vimeo' . sha1($vimeoId . $maxResults);
$disableVimeoCache = (bool)GeneralUtility::_GP('disableVimeoCache');
if (!$disableVimeoCache) {
$cachedResult = $this->cache->get($cacheKey);
if ($cachedResult) {
return $cachedResult;
}
}
if (strpos($vimeoId, 'showcase') === 0) {
$showcaseId = explode('/', $vimeoId)[1];
$response['items'] = $this->addVideoIdsToResponse($this->getShowcaseVideos((int)$showcaseId));
$response['kind'] = 'showcase';
} elseif (strpos($vimeoId, 'channel') === 0) {
$channelId = explode('/', $vimeoId)[1];
$response['items'] = $this->addVideoIdsToResponse($this->getChannelVideos($channelId));
$response['kind'] = 'channel';
} else {
$response['items'] = $this->addVideoIdsToResponse($this->getVideo((int)$vimeoId));
$response['kind'] = 'video';
}
if (!$disableVimeoCache) {
$this->cache->set($cacheKey, $response, [], self::CACHE_LIFETIME_IN_SECONDS);
}
return $response;
}
/**
* Extracts the video id from the video's canonical relative URI and adds it to each entry with the key 'videoId'
*
* @param array|null $response
* @return array|null
*/
protected function addVideoIdsToResponse(?array $response): ?array
{
if (!is_array($response)) {
return NULL;
}
foreach ($response as $index => $item) {
if (array_key_exists('uri', $item)) {
$uri = $item['uri'];
$videoId = (int)str_replace('/videos/', '', $uri);
$response[$index]['videoId'] = $videoId;
}
}
return $response;
}
/**
* Unauthenticated API requests must generate an access token.
* (Access tokens without a user. These tokens can view only public data.)
* You should not generate a new access token for each request.
* Instead, request an access token once and use it forever.
*/
protected function requestAccessToken(): void
{
$token = $this->vimeoApiClient->clientCredentials(self::SCOPE);
if (isset($token['body']['access_token'])) {
$this->vimeoApiClient->setToken($token['body']['access_token']);
}
}
/**
* Returns the response body, wrapped in an array if the response contains a single item.
* If the response is a paginated response, all items are fetched until the maxResultsPerPage is reached,
* or no $nextUrl is available anymore (last page reached). Since the flexform allows a max value of 100 currently,
* this function will never go past the first page, since the vimeo API allows a value of 100 as max value for the `per_page` parameter.
*
* @param array|null $response
* @return array|null
*/
protected function preprocessApiResponse(?array $response): ?array
{
if (!is_array($response)) {
return NULL;
}
// log error & return here, since the response was not OK
if ($response['status'] !== 200) {
$this->logger->error('sg_vimeo API Request failed, got the following response:', $response);
return [$response['body']];
}
// @TODO: we could check $response['headers‘]['X-RateLimit-Remaining'] here for remaining quota
if (array_key_exists('paging', $response['body'])) {
$amountOfVideosInResponse = is_countable($response['body']['data']) ? count($response['body']['data']) : 0;
$this->amountOfVideosFetched += $amountOfVideosInResponse;
$this->paginatedResponseData[] = $response['body']['data'];
$nextUrl = $response['body']['paging']['next'];
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);
}
$this->fetchPaginatedResult($nextUrl);
}
// wrap response body in an array, so that we can treat all return values the same in the template
return [$response['body']];
}
/**
* @param string $nextUrl
* @return array|null
*/
protected function fetchPaginatedResult(string $nextUrl): ?array
{
try {
$response = $this->vimeoApiClient->request($nextUrl);
} catch (VimeoRequestException $e) {
return NULL;
}
return $this->preprocessApiResponse($response);
}
/**
* Returns a single video for the given $videoId
*
* @see https://developer.vimeo.com/api/reference/videos#get_video
* @param int $videoId
* @return array|null
*/
public function getVideo(int $videoId): ?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';
try {
$response = $this->vimeoApiClient->request(self::API_VIDEO . $videoId . '?fields=' . $fieldsToSelect);
} catch (VimeoRequestException $e) {
$response = NULL;
throw $e;
}
return $this->preprocessApiResponse($response);
}
/**
* Returns all videos for the given $channelIdentifier
*
* @see https://developer.vimeo.com/api/reference/channels#get_channel_videos
* @param string $channelIdentifier
* @return array|null
*/
public function getChannelVideos(string $channelIdentifier): ?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';
try {
$response = $this->vimeoApiClient->request(
self::API_CHANNEL . $channelIdentifier . self::API_VIDEO . '?fields=' . $fieldsToSelect . '&per_page=' . $this->maxResultsPerPage
);
} catch (VimeoRequestException $e) {
$response = NULL;
throw $e;
}
return $this->preprocessApiResponse($response);
}
/**
* Returns all videos for the given $showcaseId
*
* @see https://developer.vimeo.com/api/reference/showcases#get_showcase
* @param string $showcaseId
* @return array|null
*/
public function getShowcaseVideos(string $showcaseId): ?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';
// sort videos by the user-selected default: ?sort=default
try {
$response = $this->vimeoApiClient->request(
self::API_SHOWCASE . $showcaseId . self::API_VIDEO . '?sort=default&fields=' . $fieldsToSelect . '&per_page=' . $this->maxResultsPerPage
);
} catch (VimeoRequestException $e) {
$response = NULL;
throw $e;
}
return $this->preprocessApiResponse($response);
}
}
......@@ -33,23 +33,25 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
*
* @package SGalinski\SgVimeo\ViewHelpers
*/
class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
class RenderSvgViewHelper extends AbstractTagBasedViewHelper
{
/**
* Name of the tag to be created by this view helper
*
* @var string
* @api
*/
protected $tagName = 'svg';
/**
* Name of the tag to be created by this view helper
*
* @var string
* @api
*/
protected $tagName = 'svg';
protected $directoryPath = __DIR__ . '/../../Resources/Public/Icons/';
/**
* Register the ViewHelper arguments
*/
public function initializeArguments(): void {
parent::initializeArguments();
/**
* Register the ViewHelper arguments
*/
public function initializeArguments(): void
{
parent::initializeArguments();
$this->registerArgument('name', 'string', 'The SVG name', TRUE);
$this->registerArgument('color', 'string', 'The fill color', TRUE);
$this->registerArgument('id', 'string', 'The HTML id attribute', FALSE);
......@@ -60,7 +62,7 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
$this->registerArgument('height', 'string', 'The HTML height attribute', FALSE);
$this->registerArgument('style', 'string', 'Inline CSS styles', FALSE);
$this->registerArgument('use', 'string', 'Inline CSS styles', FALSE);
}
}
/**
......@@ -81,7 +83,7 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
$style = $this->arguments['style'];
$title = $this->arguments['title'];
$src = $path ?? $this->directoryPath . '/' . $name .'.svg';
$src = $path ?? $this->directoryPath . '/' . $name . '.svg';
// Get the content of the SVG file
......@@ -97,7 +99,7 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
// Set the attributes of the SVG element
if ($width > 0) {
$this->addOrReplaceAttribute($svg, 'width', $width);
$this->addOrReplaceAttribute($svg, 'width', $width);
}
if ($height > 0) {
......@@ -117,7 +119,7 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
}
if ($title) {
$this->addOrReplaceAttribute($svg,'title', $title);
$this->addOrReplaceAttribute($svg, 'title', $title);
}
// // Set the ID of the SVG element
......@@ -153,11 +155,11 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
//Todo: we don't really want this, do we?
if (!empty($width) && !empty($height)) {
$resultWithoutCssClass = str_replace($cssClass, '', $result);
$cssClasses = $this->processWrapperClasses($cssClassPlain);
$resultWithoutCssClass = str_replace($cssClass, '', $result);
$cssClasses = $this->processWrapperClasses($cssClassPlain);
return "<span $cssClasses style='width: $width; height: $height;'>$resultWithoutCssClass</span>";
}
return "<span $cssClasses style='width: $width; height: $height;'>$resultWithoutCssClass</span>";
}
if (!empty($width)) {
$resultWithoutCssClass = str_replace($cssClass, '', $result);
......@@ -182,8 +184,9 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
* @param string $cssClass
* @return string
*/
function processWrapperClasses(string $cssClass): string {
return "class='$cssClass svg-wrapper d-inline-flex justify-content-center align-items-center'";
function processWrapperClasses(string $cssClass): string
{
return "class='$cssClass svg-wrapper d-inline-flex justify-content-center align-items-center'";
}
/**
......@@ -194,31 +197,32 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
* @param string $use
* @return string
*/
function processUseWrappers(string $svgContent, string $id, string $use): string {
$svgContent = "<g $id>$svgContent</g>";
function processUseWrappers(string $svgContent, string $id, string $use): string
{
$svgContent = "<g $id>$svgContent</g>";
if ($use) {
$svgContent = "<use href='#$use' />";
}
if ($use) {
$svgContent = "<use href='#$use' />";
}
return $svgContent;
return $svgContent;
}
/**
* Set the fill color of an SVG element.
*
* @param \SimpleXMLElement $element The SVG element
* @param string $fill The fill color to set
*/
protected function setFill(\SimpleXMLElement $element, string $fill): void
{
foreach ($element->children() as $child) {
$this->setFill($child, $fill);
}
if (isset($element->attributes()->fill)) {
$element->attributes()->fill = $fill;
}
}
* Set the fill color of an SVG element.
*
* @param \SimpleXMLElement $element The SVG element
* @param string $fill The fill color to set
*/
protected function setFill(\SimpleXMLElement $element, string $fill): void
{
foreach ($element->children() as $child) {
$this->setFill($child, $fill);
}
if (isset($element->attributes()->fill)) {
$element->attributes()->fill = $fill;
}
}
/**
* Gets the contents of the SVG file
......@@ -250,14 +254,14 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
*/
private function xmlAdopt(\SimpleXMLElement $root, \SimpleXMLElement $newElement): void
{
$node = $root->addChild($newElement->getName(), (string) $newElement);
$node = $root->addChild($newElement->getName(), (string)$newElement);
foreach($newElement->attributes() as $attr => $value) {
foreach ($newElement->attributes() as $attr => $value) {
/** @noinspection NullPointerExceptionInspection */
$node->addAttribute($attr, $value);
}
foreach($newElement->children() as $ch) {
foreach ($newElement->children() as $ch) {
/** @noinspection NullPointerExceptionInspection */
$this->xmlAdopt($node, $ch);
}
......@@ -272,8 +276,8 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
* @return void
*/
private function addOrReplaceAttribute(\SimpleXMLElement $element,
string $attributeName,
string $attributeValue): void
string $attributeName,
string $attributeValue): void
{
if (isset($element[$attributeName])) {
$element[$attributeName] = $attributeValue; // Replace existing attribute value
......
......@@ -33,63 +33,66 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
*
* @package SGalinski\SgVimeo\ViewHelpers
*/
class StructuredVideoDataViewHelper extends AbstractTagBasedViewHelper {
/**
* Name of the tag to be created by this view helper
*
* @var string
* @api
*/
protected $tagName = 'script';
class StructuredVideoDataViewHelper extends AbstractTagBasedViewHelper
{
/**
* Name of the tag to be created by this view helper
*
* @var string
* @api
*/
protected $tagName = 'script';
/**
* Register the ViewHelper arguments
*/
public function initializeArguments(): void {
parent::initializeArguments();
$this->registerArgument('videoArray', 'array', 'An array of videos', TRUE);
$this->registerArgument('arrayType', 'string', 'Either "youtube" or "vimeo"', FALSE, "youtube");
}
/**
* Register the ViewHelper arguments
*/
public function initializeArguments(): void
{
parent::initializeArguments();
$this->registerArgument('videoArray', 'array', 'An array of videos', TRUE);
$this->registerArgument('arrayType', 'string', 'Either "youtube" or "vimeo"', FALSE, "youtube");
}
/**
* Takes the provided array of videos and creates an array for structured data JSON
*
* @return string
*/
public function render(): string {
$this->escapeOutput = FALSE;
$videoArray = $this->arguments['videoArray'];
$arrayType = $this->arguments['arrayType'];
/**
* Takes the provided array of videos and creates an array for structured data JSON
*
* @return string
*/
public function render(): string
{
$this->escapeOutput = FALSE;
$videoArray = $this->arguments['videoArray'];
$arrayType = $this->arguments['arrayType'];
$structuredData = [];
if ($arrayType === "youtube") {
foreach ($videoArray as $video) {
$structuredData[] = [
'@type' => 'VideoObject',
'name' => $video['title'],
'description' => $video['description'],
'thumbnailUrl' => $video['thumbnail'],
'contentUrl' => $video['url'],
'uploadDate' => $video['publishedAt'],
];
}
} else {
foreach ($videoArray as $video) {
$structuredData[] = [
'@type' => 'VideoObject',
'name' => $video['name'],
'description' => $video['description'],
'thumbnailUrl' => $video['pictures']['sizes'][count($video['pictures']['sizes']) - 1]['link'],
'contentUrl' => $video['link'],
'uploadDate' => $video['release_time'],
];
}
}
$structuredData = [];
if ($arrayType === "youtube") {
foreach ($videoArray as $video) {
$structuredData[] = [
'@type' => 'VideoObject',
'name' => $video['title'],
'description' => $video['description'],
'thumbnailUrl' => $video['thumbnail'],
'contentUrl' => $video['url'],
'uploadDate' => $video['publishedAt'],
];
}
} else {
foreach ($videoArray as $video) {
$structuredData[] = [
'@type' => 'VideoObject',
'name' => $video['name'],
'description' => $video['description'],
'thumbnailUrl' => $video['pictures']['sizes'][count($video['pictures']['sizes']) - 1]['link'],
'contentUrl' => $video['link'],
'uploadDate' => $video['release_time'],
];
}
}
$this->tag->addAttribute('type', 'application/ld+json');
$this->tag->setContent(
'{"@context": "http://schema.org", "@type": "WebPage", "video": ' . json_encode($structuredData) . '}'
);
return $this->tag->render();
}
$this->tag->addAttribute('type', 'application/ld+json');
$this->tag->setContent(
'{"@context": "http://schema.org", "@type": "WebPage", "video": ' . json_encode($structuredData) . '}'
);
return $this->tag->render();
}
}
<?php
namespace SGalinski\SgVimeo\ViewHelpers;
/***************************************************************
* Copyright notice
*
......@@ -26,6 +24,8 @@ namespace SGalinski\SgVimeo\ViewHelpers;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace SGalinski\SgVimeo\ViewHelpers;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
......@@ -36,38 +36,41 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
* <vi:urlWithQueryParameters url="https://player.vimeo.com/video/123?h=xyz" parameters="?origin=https://demo.sgalinski.de" />
* Result: https://player.vimeo.com/video/123?h=xyz&origin=https://demo.sgalinski.de
*/
class UrlWithQueryParametersViewHelper extends AbstractViewHelper {
/**
* Register the ViewHelper arguments
*/
public function initializeArguments(): void {
parent::initializeArguments();
$this->registerArgument('url', 'string', 'The url to add the query parameters to', TRUE);
$this->registerArgument('parameters', 'string', 'The query parameters to add', FALSE, '');
}
class UrlWithQueryParametersViewHelper extends AbstractViewHelper
{
/**
* Register the ViewHelper arguments
*/
public function initializeArguments(): void
{
parent::initializeArguments();
$this->registerArgument('url', 'string', 'The url to add the query parameters to', TRUE);
$this->registerArgument('parameters', 'string', 'The query parameters to add', FALSE, '');
}
/**
* Returns the url with the added query parameters
*
* @return string
*/
public function render(): string {
$url = $this->arguments['url'];
$additionalUrlParameters = $this->arguments['parameters'];
/**
* Returns the url with the added query parameters
*
* @return string
*/
public function render(): string
{
$url = $this->arguments['url'];
$additionalUrlParameters = $this->arguments['parameters'];
if ($additionalUrlParameters === '') {
return $url;
}
if ($additionalUrlParameters === '') {
return $url;
}
$beginsWithQuestionMark = $additionalUrlParameters[0] === '?';
$beginsWithAmpersand = $additionalUrlParameters[0] === '&';
$beginsWithQuestionMark = $additionalUrlParameters[0] === '?';
$beginsWithAmpersand = $additionalUrlParameters[0] === '&';
if ($beginsWithQuestionMark || $beginsWithAmpersand) {
$additionalUrlParameters = substr($additionalUrlParameters, 1);
}
if ($beginsWithQuestionMark || $beginsWithAmpersand) {
$additionalUrlParameters = substr($additionalUrlParameters, 1);
}
return strpos($url, '?') !== FALSE
? $url . '&' . $additionalUrlParameters
: $url . '?' . $additionalUrlParameters;
}
return strpos($url, '?') !== FALSE
? $url . '&' . $additionalUrlParameters
: $url . '?' . $additionalUrlParameters;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment