diff --git a/Classes/Controller/AdministrationController.php b/Classes/Controller/AdministrationController.php index d66ffc5e..ac6c9e51 100644 --- a/Classes/Controller/AdministrationController.php +++ b/Classes/Controller/AdministrationController.php @@ -12,18 +12,22 @@ use Pixelant\PxaSocialFeed\Domain\Repository\ConfigurationRepository; use Pixelant\PxaSocialFeed\Domain\Repository\FeedRepository; use Pixelant\PxaSocialFeed\Domain\Repository\TokenRepository; +use Pixelant\PxaSocialFeed\Domain\Validation\Validator\ConfigurationValidator; +use Pixelant\PxaSocialFeed\Domain\Validation\Validator\TokenValidator; use Pixelant\PxaSocialFeed\Service\Task\ImportFeedsTaskService; use Pixelant\PxaSocialFeed\Utility\ConfigurationUtility; +use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Backend\Routing\UriBuilder as BackendUriBuilder; -use TYPO3\CMS\Backend\View\BackendTemplateView; +use TYPO3\CMS\Backend\Template\ModuleTemplate; +use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Messaging\FlashMessage; +use TYPO3\CMS\Core\Http\RedirectResponse; use TYPO3\CMS\Core\Page\PageRenderer; +use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Extbase\Annotation as Extbase; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface; use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; @@ -78,29 +82,30 @@ class AdministrationController extends ActionController * @var BackendUserGroupRepository */ protected $backendUserGroupRepository; - /** - * BackendTemplateContainer - * - * @var BackendTemplateView + * Summary of moduleTemplateFactory + * @var ModuleTemplateFactory */ - protected $view; + protected ModuleTemplateFactory $moduleTemplateFactory; /** - * Backend Template Container - * - * @var BackendTemplateView + * @var ModuleTemplate */ - protected $defaultViewObjectName = BackendTemplateView::class; + protected ModuleTemplate $moduleTemplate; /** * @param BackendUserGroupRepository $backendUserGroupRepository */ - public function __construct(BackendUserGroupRepository $backendUserGroupRepository) + public function __construct(BackendUserGroupRepository $backendUserGroupRepository, private ModuleTemplateFactory $moduleTemplateFactor, private readonly PageRenderer $pageRenderer) { $this->backendUserGroupRepository = $backendUserGroupRepository; } + public function injectModuleTemplateFactory(ModuleTemplateFactory $moduleTemplateFactory): void + { + $this->moduleTemplateFactory = $moduleTemplateFactory; + } + /** * @param ConfigurationRepository $configurationRepository */ @@ -125,24 +130,12 @@ public function injectFeedRepository(FeedRepository $feedRepository): void $this->feedRepository = $feedRepository; } - /** - * Set up the doc header properly here - * - * @param ViewInterface $view - */ - protected function initializeView(ViewInterface $view): void + protected function initializeView() { - /** @var BackendTemplateView $view */ - parent::initializeView($view); - - // create select box menu - $this->createMenu(); - - $pageRenderer = $this->view->getModuleTemplate() - ? $this->view->getModuleTemplate()->getPageRenderer() - : GeneralUtility::makeInstance(PageRenderer::class); + // $this->pageRenderer->addCssFile ( 'EXT:pxa_social_feed/Resources/Public/Css/Backend/SocialFeedModule.css' ); + // $this->pageRenderer->loadJavaScriptModule ( '@pixelant/pxa-social-feed/social-feed-administration-module.js' ); - $pageRenderer->addRequireJsConfiguration( + $this->pageRenderer->addRequireJsConfiguration( [ 'paths' => [ 'clipboard' => PathUtility::getAbsoluteWebPath( @@ -158,38 +151,47 @@ protected function initializeView(ViewInterface $view): void ] ); - $pageRenderer->loadRequireJsModule( + $this->pageRenderer->loadRequireJsModule( 'TYPO3/CMS/PxaSocialFeed/Backend/SocialFeedModule', "function(socialFeedModule) { socialFeedModule.getInstance({$this->getInlineSettings()}).run() }" ); } + public function initializeAction() + { + $this->moduleTemplate = $this->moduleTemplateFactory->create($this->request); + $this->createMenu(); + } + /** * Index action to show all configurations and tokens * * @param bool $activeTokenTab */ - public function indexAction($activeTokenTab = false): void + public function indexAction($activeTokenTab = false): ResponseInterface { $tokens = $this->findAllByRepository($this->tokenRepository); - $this->view->assignMultiple([ - 'tokens' => $tokens, + 'tokens' => $tokens, 'configurations' => $this->findAllByRepository($this->configurationRepository), 'activeTokenTab' => $activeTokenTab, 'isTokensValid' => $this->isTokensValid($tokens), 'isAdmin' => $GLOBALS['BE_USER']->isAdmin(), ]); + + $this->moduleTemplate->setContent ( $this->view->render () ); + return $this->htmlResponse($this->moduleTemplate->renderContent()); } /** * Edit token form * - * @param Token $token + * @param Token|null $tokenToEdit * @param int $type */ - public function editTokenAction(Token $token = null, int $type = Token::FACEBOOK): void + public function editTokenAction(Token $tokenToEdit = null, int $type = Token::FACEBOOK): ResponseInterface { + $token = $tokenToEdit; $isNew = $token === null; if (!$isNew) { @@ -205,21 +207,30 @@ public function editTokenAction(Token $token = null, int $type = Token::FACEBOOK $this->view->assignMultiple(compact('token', 'type', 'isNew', 'availableTypes')); $this->assignBEGroups(); + + $this->moduleTemplate->setContent ( $this->view->render () ); + return $this->htmlResponse ( $this->moduleTemplate->renderContent () ); } /** * Save token changes * - * @param Token $token - * @Extbase\Validate("\Pixelant\PxaSocialFeed\Domain\Validation\Validator\TokenValidator", param="token") + * @param Token $tokenToEdit */ - public function updateTokenAction(Token $token): void + #[Extbase\Validate(['validator' => TokenValidator::class, 'param' => 'tokenToEdit'])] + public function updateTokenAction(Token $tokenToEdit): RedirectResponse { - $isNew = $token->getUid() === null; + $isNew = $tokenToEdit->getUid() === null; - $this->tokenRepository->{$isNew ? 'add' : 'update'}($token); + $this->tokenRepository->{$isNew ? 'add' : 'update'}($tokenToEdit); + + $this->addFlashMessage( + $this->translate('action_changes_saved'), + '', + ContextualFeedbackSeverity::INFO, + ); - $this->redirectToIndexTokenTab($this->translate('action_changes_saved')); + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed') . '&activeTokenTab=1'); } /** @@ -227,40 +238,59 @@ public function updateTokenAction(Token $token): void * * @param Token $token */ - public function resetAccessTokenAction(Token $token): void + public function resetAccessTokenAction ( Token $resetToken ) : RedirectResponse { - $token->setAccessToken(''); - $this->tokenRepository->update($token); + $resetToken->setAccessToken ( '' ); + $this->tokenRepository->update ( $resetToken ); - $this->redirectToIndexTokenTab(); + $this->addFlashMessage( + 'Access token was reset', + '', + ContextualFeedbackSeverity::INFO, + ); + + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed') . '&activeTokenTab=1'); } /** * Delete token * - * @param Token $token + * @param Token $tokenToDelete */ - public function deleteTokenAction(Token $token): void + public function deleteTokenAction ( Token $tokenToDelete ) : RedirectResponse { - $tokenConfigurations = $this->configurationRepository->findConfigurationByToken($token); + $tokenConfigurations = $this->configurationRepository->findConfigurationByToken ( $tokenToDelete ); if ($tokenConfigurations->count() === 0) { - $this->tokenRepository->remove($token); + $this->tokenRepository->remove ( $tokenToDelete ); - if ($token->getType() === Token::FACEBOOK) { + if ( $tokenToDelete->getType () === Token::FACEBOOK ) + { // Remove all page access tokens created by this token $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getConnectionForTable('tx_pxasocialfeed_domain_model_token'); - $queryBuilder->delete('tx_pxasocialfeed_domain_model_token', ['parent_token' => $token->getUid()]); + $queryBuilder->delete ( 'tx_pxasocialfeed_domain_model_token', [ 'parent_token' => $tokenToDelete->getUid () ] ); } - $this->redirectToIndexTokenTab($this->translate('action_delete')); + $this->addFlashMessage( + $this->translate('action_delete'), + '', + ContextualFeedbackSeverity::INFO, + ); + + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed') . '&activeTokenTab=1'); } - $this->redirectToIndexTokenTab( - $this->translate('error_token_configuration_exist', [$tokenConfigurations->getFirst()->getName()]), - FlashMessage::ERROR + $this->addFlashMessage( + $this->translate( + 'error_token_configuration_exist', + [ $tokenConfigurations->getFirst()->getName() ], + ), + '', + ContextualFeedbackSeverity::ERROR, ); + + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed') . '&activeTokenTab=1'); } /** @@ -268,21 +298,25 @@ public function deleteTokenAction(Token $token): void * * @param Configuration $configuration */ - public function editConfigurationAction(Configuration $configuration = null): void + public function editConfigurationAction(Configuration $configuration = null): ResponseInterface { $tokens = $this->findAllByRepository($this->tokenRepository); $this->view->assignMultiple(compact('configuration', 'tokens')); $this->assignBEGroups(); + + + $this->moduleTemplate->setContent ( $this->view->render () ); + return $this->htmlResponse ( $this->moduleTemplate->renderContent () ); } /** * Update configuration * * @param Configuration $configuration - * @Extbase\Validate("\Pixelant\PxaSocialFeed\Domain\Validation\Validator\ConfigurationValidator", param="configuration") */ - public function updateConfigurationAction(Configuration $configuration): void + #[Extbase\Validate(['validator' => ConfigurationValidator::class, 'param' => 'configuration'])] + public function updateConfigurationAction(Configuration $configuration): RedirectResponse { $isNew = $configuration->getUid() === null; @@ -298,10 +332,16 @@ public function updateConfigurationAction(Configuration $configuration): void GeneralUtility::makeInstance(PersistenceManagerInterface::class)->persistAll(); // Redirect back to edit view, so user can now provide social ID according to selected token - $this->redirect('editConfiguration', null, null, ['configuration' => $configuration]); + return new RedirectResponse($this->uriBuilder->reset()->uriFor('editConfiguration', [ 'configuration' => $configuration ], 'Administration', 'PxaSocialFeed')); } - $this->redirectToIndex($this->translate('action_changes_saved')); + $this->addFlashMessage( + $this->translate('action_changes_saved'), + '', + ContextualFeedbackSeverity::OK, + ); + + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed')); } /** @@ -309,7 +349,7 @@ public function updateConfigurationAction(Configuration $configuration): void * * @param Configuration $configuration */ - public function deleteConfigurationAction(Configuration $configuration): void + public function deleteConfigurationAction(Configuration $configuration): RedirectResponse { // Remove all feeds $feeds = $this->feedRepository->findByConfiguration($configuration); @@ -320,7 +360,13 @@ public function deleteConfigurationAction(Configuration $configuration): void $this->configurationRepository->remove($configuration); - $this->redirectToIndex($this->translate('action_delete')); + $this->addFlashMessage( + $this->translate('action_delete'), + '', + ContextualFeedbackSeverity::WARNING, + ); + + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed')); } /** @@ -328,16 +374,26 @@ public function deleteConfigurationAction(Configuration $configuration): void * * @param Configuration $configuration */ - public function runConfigurationAction(Configuration $configuration) + public function runConfigurationAction(Configuration $configuration): RedirectResponse { $importService = GeneralUtility::makeInstance(ImportFeedsTaskService::class); try { $importService->import([ $configuration->getUid() ]); } catch (\Exception $e) { - $this->redirectToIndex($e->getMessage(), FlashMessage::ERROR); + $this->addFlashMessage( + $e->getMessage(), + '', + ContextualFeedbackSeverity::ERROR, + ); } - $this->redirectToIndex($this->translate('single_import_end')); + $this->addFlashMessage( + $this->translate('single_import_end'), + '', + ContextualFeedbackSeverity::WARNING, + ); + + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed')); } /** @@ -366,7 +422,7 @@ protected function assignBEGroups() $excludeGroups = $this->getExcludeGroups(); - if ($GLOBALS['BE_USER']->isAdmin()) { + if ($GLOBALS[ 'BE_USER' ]->isAdmin()) { $groups = $this->backendUserGroupRepository->findAll($excludeGroups); } else { $groups = array_filter($GLOBALS['BE_USER']->userGroups, function ($group) use ($excludeGroups) { @@ -396,32 +452,23 @@ protected function translate(string $key, array $arguments = null): ?string */ protected function createMenu(): void { - // if view was found - if ($this->view->getModuleTemplate() !== null) { - /** @var UriBuilder $uriBuilder */ - $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); - $uriBuilder->setRequest($this->request); - - $menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu(); - $menu->setIdentifier('pxa_social_feed'); - - $actions = [ - 'index', - 'editConfiguration', - 'editToken', - ]; - - foreach ($actions as $action) { - $item = $menu->makeMenuItem() - ->setTitle($this->translate($action . 'Action')) - ->setHref($uriBuilder->reset()->uriFor($action, [], 'Administration')) - ->setActive($this->request->getControllerActionName() === $action); - - $menu->addMenuItem($item); - } + /** @var UriBuilder $uriBuilder */ + $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); + $uriBuilder->setRequest($this->request); + + $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu(); + $menu->setIdentifier('pxa_social_feed'); - $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu); + $actions = [ 'index', 'editConfiguration', 'editToken' ]; + + foreach ($actions as $action) { + $item = $menu->makeMenuItem() + ->setTitle($this->translate($action . 'Action')) + ->setHref($uriBuilder->reset()->uriFor($action, [], 'Administration')) + ->setActive($this->request->getControllerActionName() === $action); + $menu->addMenuItem($item); } + $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu); } /** @@ -479,7 +526,7 @@ protected function getInlineSettings(): string * @param string|null $message * @param int $severity */ - protected function redirectToIndexTokenTab(string $message = null, int $severity = FlashMessage::OK) + protected function redirectToIndexTokenTab(string $message = null, int $severity = ContextualFeedbackSeverity::OK): RedirectResponse { if (!empty($message)) { $this->addFlashMessage( @@ -489,7 +536,7 @@ protected function redirectToIndexTokenTab(string $message = null, int $severity ); } - $this->redirect('index', null, null, ['activeTokenTab' => true]); + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed') . '&activeTokenTab=1'); } /** @@ -498,7 +545,7 @@ protected function redirectToIndexTokenTab(string $message = null, int $severity * @param string|null $message * @param int $severity */ - protected function redirectToIndex(string $message = null, int $severity = FlashMessage::OK) + protected function redirectToIndex(string $message = null, int $severity = ContextualFeedbackSeverity::OK): RedirectResponse { if (!empty($message)) { $this->addFlashMessage( @@ -508,7 +555,7 @@ protected function redirectToIndex(string $message = null, int $severity = Flash ); } - $this->redirect('index'); + return new RedirectResponse($this->uriBuilder->reset()->uriFor('index', [], 'Administration', 'PxaSocialFeed')); } /** @@ -522,7 +569,6 @@ protected function getExcludeGroups() if (isset($configuration['excludeBackendUserGroups'])) { return GeneralUtility::intExplode(',', $configuration['excludeBackendUserGroups'], true); } - return []; } } diff --git a/Classes/Controller/EidController.php b/Classes/Controller/EidController.php index cb2bcb9e..a1272ca3 100644 --- a/Classes/Controller/EidController.php +++ b/Classes/Controller/EidController.php @@ -21,7 +21,7 @@ */ class EidController { - const IDENTIFIER = 'pxa_social_feed_fb_access_token'; + public const IDENTIFIER = 'pxa_social_feed_fb_access_token'; /** * @var TokenRepository diff --git a/Classes/Controller/FeedsController.php b/Classes/Controller/FeedsController.php index a2f9cca9..3ed0f64f 100644 --- a/Classes/Controller/FeedsController.php +++ b/Classes/Controller/FeedsController.php @@ -3,6 +3,7 @@ namespace Pixelant\PxaSocialFeed\Controller; use Pixelant\PxaSocialFeed\Domain\Repository\FeedRepository; +use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; @@ -49,10 +50,12 @@ public function injectFeedRepository(FeedRepository $feedRepository): void $this->feedRepository = $feedRepository; } + protected function initializeView($view) {} + /** * List action */ - public function listAction() + public function listAction(): ResponseInterface { $limit = $this->settings['feedsLimit'] ? (int)($this->settings['feedsLimit']) : 10; $configurations = GeneralUtility::intExplode(',', $this->settings['configuration'], true); @@ -60,14 +63,16 @@ public function listAction() $feeds = $this->feedRepository->findByConfigurations($configurations, $limit); $this->view->assign('feeds', $feeds); + return $this->htmlResponse(); } /** * List ajax action * Prepare view for later ajax request */ - public function listAjaxAction() + public function listAjaxAction(): ResponseInterface { + return $this->htmlResponse(); } /** diff --git a/Classes/Database/Query/Restriction/BackendGroupRestriction.php b/Classes/Database/Query/Restriction/BackendGroupRestriction.php index 26e88807..5dbafdba 100644 --- a/Classes/Database/Query/Restriction/BackendGroupRestriction.php +++ b/Classes/Database/Query/Restriction/BackendGroupRestriction.php @@ -61,6 +61,6 @@ public function buildExpression(array $queriedTables, ExpressionBuilder $express } } - return $expressionBuilder->orX(...$constraints); + return $expressionBuilder->or(...$constraints); } } diff --git a/Classes/Domain/Model/BackendUserGroup.php b/Classes/Domain/Model/BackendUserGroup.php new file mode 100644 index 00000000..710a28a1 --- /dev/null +++ b/Classes/Domain/Model/BackendUserGroup.php @@ -0,0 +1,526 @@ + + */ + protected $subGroups; + + /** + * @var string + */ + protected $modules = ''; + + /** + * @var string + */ + protected $tablesListening = ''; + + /** + * @var string + */ + protected $tablesModify = ''; + + /** + * @var string + */ + protected $pageTypes = ''; + + /** + * @var string + */ + protected $allowedExcludeFields = ''; + + /** + * @var string + */ + protected $explicitlyAllowAndDeny = ''; + + /** + * @var string + */ + protected $allowedLanguages = ''; + + /** + * @var bool + */ + protected $workspacePermission = false; + + /** + * @var string + */ + protected $databaseMounts = ''; + + /** + * @var int + */ + protected $fileOperationPermissions = 0; + + /** + * @var string + */ + protected $tsConfig = ''; + + /** + * Constructs this backend usergroup + */ + public function __construct() + { + $this->subGroups = new ObjectStorage(); + } + + /** + * Setter for title + * + * @param string $title + */ + public function setTitle($title) + { + $this->title = $title; + } + + /** + * Getter for title + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Setter for description + * + * @param string $description + */ + public function setDescription($description) + { + $this->description = $description; + } + + /** + * Getter for description + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Setter for the sub groups + * + * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $subGroups + */ + public function setSubGroups(ObjectStorage $subGroups) + { + $this->subGroups = $subGroups; + } + + /** + * Adds a sub group to this backend user group + * + * @param \Pixelant\PxaSocialFeed\Domain\Model\BackendUserGroup $beGroup + */ + public function addSubGroup(\Pixelant\PxaSocialFeed\Domain\Model\BackendUserGroup $beGroup) + { + $this->subGroups->attach($beGroup); + } + + /** + * Removes sub group from this backend user group + * + * @param \Pixelant\PxaSocialFeed\Domain\Model\BackendUserGroup $groupToDelete + */ + public function removeSubGroup(\Pixelant\PxaSocialFeed\Domain\Model\BackendUserGroup $groupToDelete) + { + $this->subGroups->detach($groupToDelete); + } + + /** + * Remove all sub groups from this backend user group + */ + public function removeAllSubGroups() + { + $subGroups = clone $this->subGroups; + $this->subGroups->removeAll($subGroups); + } + + /** + * Getter of sub groups + * + * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage + */ + public function getSubGroups() + { + return $this->subGroups; + } + + /** + * Setter for modules + * + * @param string $modules + */ + public function setModules($modules) + { + $this->modules = $modules; + } + + /** + * Getter for modules + * + * @return string + */ + public function getModules() + { + return $this->modules; + } + + /** + * Setter for tables listening + * + * @param string $tablesListening + */ + public function setTablesListening($tablesListening) + { + $this->tablesListening = $tablesListening; + } + + /** + * Getter for tables listening + * + * @return string + */ + public function getTablesListening() + { + return $this->tablesListening; + } + + /** + * Setter for tables modify + * + * @param string $tablesModify + */ + public function setTablesModify($tablesModify) + { + $this->tablesModify = $tablesModify; + } + + /** + * Getter for tables modify + * + * @return string + */ + public function getTablesModify() + { + return $this->tablesModify; + } + + /** + * Setter for page types + * + * @param string $pageTypes + */ + public function setPageTypes($pageTypes) + { + $this->pageTypes = $pageTypes; + } + + /** + * Getter for page types + * + * @return string + */ + public function getPageTypes() + { + return $this->pageTypes; + } + + /** + * Setter for allowed exclude fields + * + * @param string $allowedExcludeFields + */ + public function setAllowedExcludeFields($allowedExcludeFields) + { + $this->allowedExcludeFields = $allowedExcludeFields; + } + + /** + * Getter for allowed exclude fields + * + * @return string + */ + public function getAllowedExcludeFields() + { + return $this->allowedExcludeFields; + } + + /** + * Setter for explicitly allow and deny + * + * @param string $explicitlyAllowAndDeny + */ + public function setExplicitlyAllowAndDeny($explicitlyAllowAndDeny) + { + $this->explicitlyAllowAndDeny = $explicitlyAllowAndDeny; + } + + /** + * Getter for explicitly allow and deny + * + * @return string + */ + public function getExplicitlyAllowAndDeny() + { + return $this->explicitlyAllowAndDeny; + } + + /** + * Setter for allowed languages + * + * @param string $allowedLanguages + */ + public function setAllowedLanguages($allowedLanguages) + { + $this->allowedLanguages = $allowedLanguages; + } + + /** + * Getter for allowed languages + * + * @return string + */ + public function getAllowedLanguages() + { + return $this->allowedLanguages; + } + + /** + * Setter for workspace permission + * + * @param bool $workspacePermission + */ + public function setWorkspacePermissions($workspacePermission) + { + $this->workspacePermission = $workspacePermission; + } + + /** + * Getter for workspace permission + * + * @return bool + */ + public function getWorkspacePermission() + { + return $this->workspacePermission; + } + + /** + * Setter for database mounts + * + * @param string $databaseMounts + */ + public function setDatabaseMounts($databaseMounts) + { + $this->databaseMounts = $databaseMounts; + } + + /** + * Getter for database mounts + * + * @return string + */ + public function getDatabaseMounts() + { + return $this->databaseMounts; + } + + /** + * Getter for file operation permissions + * + * @param int $fileOperationPermissions + */ + public function setFileOperationPermissions($fileOperationPermissions) + { + $this->fileOperationPermissions = $fileOperationPermissions; + } + + /** + * Getter for file operation permissions + * + * @return int + */ + public function getFileOperationPermissions() + { + return $this->fileOperationPermissions; + } + + /** + * Check if file operations like upload, copy, move, delete, rename, new and + * edit files is allowed. + * + * @return bool + */ + public function isFileOperationAllowed() + { + return $this->isPermissionSet(self::FILE_OPPERATIONS); + } + + /** + * Set the the bit for file operations are allowed. + * + * @param bool $value + */ + public function setFileOperationAllowed($value) + { + $this->setPermission(self::FILE_OPPERATIONS, $value); + } + + /** + * Check if folder operations like move, delete, rename, and new are allowed. + * + * @return bool + */ + public function isDirectoryOperationAllowed() + { + return $this->isPermissionSet(self::DIRECTORY_OPPERATIONS); + } + + /** + * Set the the bit for directory operations are allowed. + * + * @param bool $value + */ + public function setDirectoryOperationAllowed($value) + { + $this->setPermission(self::DIRECTORY_OPPERATIONS, $value); + } + + /** + * Check if it is allowed to copy folders. + * + * @return bool + */ + public function isDirectoryCopyAllowed() + { + return $this->isPermissionSet(self::DIRECTORY_COPY); + } + + /** + * Set the the bit for copy directories. + * + * @param bool $value + */ + public function setDirectoryCopyAllowed($value) + { + $this->setPermission(self::DIRECTORY_COPY, $value); + } + + /** + * Check if it is allowed to remove folders recursively. + * + * @return bool + */ + public function isDirectoryRemoveRecursivelyAllowed() + { + return $this->isPermissionSet(self::DIRECTORY_REMOVE_RECURSIVELY); + } + + /** + * Set the the bit for remove directories recursively. + * + * @param bool $value + */ + public function setDirectoryRemoveRecursivelyAllowed($value) + { + $this->setPermission(self::DIRECTORY_REMOVE_RECURSIVELY, $value); + } + + /** + * Setter for ts config + * + * @param string $tsConfig + */ + public function setTsConfig($tsConfig) + { + $this->tsConfig = $tsConfig; + } + + /** + * Getter for ts config + * + * @return string + */ + public function getTsConfig() + { + return $this->tsConfig; + } + + /** + * Helper method for checking the permissions bitwise. + * + * @param int $permission + * @return bool + */ + protected function isPermissionSet($permission) + { + return ($this->fileOperationPermissions & $permission) == $permission; + } + + /** + * Helper method for setting permissions bitwise. + * + * @param int $permission + * @param bool $value + */ + protected function setPermission($permission, $value) + { + if ($value) { + $this->fileOperationPermissions |= $permission; + } else { + $this->fileOperationPermissions &= ~$permission; + } + } +} diff --git a/Classes/Domain/Model/Configuration.php b/Classes/Domain/Model/Configuration.php index 549e7315..1a6791df 100644 --- a/Classes/Domain/Model/Configuration.php +++ b/Classes/Domain/Model/Configuration.php @@ -28,9 +28,8 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ - use TYPO3\CMS\Backend\Utility\BackendUtility; -use TYPO3\CMS\Extbase\Domain\Model\BackendUserGroup; +use TYPO3\CMS\Extbase\Annotation\ORM\Lazy; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy; use TYPO3\CMS\Extbase\Persistence\ObjectStorage; @@ -97,14 +96,14 @@ class Configuration extends AbstractEntity /** * @var Token - * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy */ + #[Lazy] protected $token; /** - * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage - * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy + * @var ObjectStorage */ + #[Lazy] protected $beGroup; /** @@ -283,8 +282,8 @@ public function setPerformCleanUp(bool $performCleanUp): void /** * @return bool */ - public function getPerformCleanUp(): bool - { - return $this->performCleanUp; - } + public function getPerformCleanUp(): bool + { + return $this->performCleanUp; + } } diff --git a/Classes/Domain/Model/Feed.php b/Classes/Domain/Model/Feed.php index 2e33c67e..870d7d14 100644 --- a/Classes/Domain/Model/Feed.php +++ b/Classes/Domain/Model/Feed.php @@ -28,7 +28,7 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ - +use TYPO3\CMS\Extbase\Annotation\ORM\Lazy; use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy; @@ -42,12 +42,12 @@ class Feed extends AbstractEntity /** * image media type */ - const IMAGE = 1; + public const IMAGE = 1; /** * video media type */ - const VIDEO = 2; + public const VIDEO = 2; /** * pid @@ -131,17 +131,17 @@ class Feed extends AbstractEntity /** * token * - * @var \Pixelant\PxaSocialFeed\Domain\Model\Configuration - * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy + * @var Configuration */ + #[Lazy] protected $configuration; /** * Fal media items * * @var ObjectStorage<\Pixelant\PxaSocialFeed\Domain\Model\FileReference> - * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy */ + #[Lazy] protected $falMedia; /** diff --git a/Classes/Domain/Model/Token.php b/Classes/Domain/Model/Token.php index 1d7c6c91..3c6f3991 100644 --- a/Classes/Domain/Model/Token.php +++ b/Classes/Domain/Model/Token.php @@ -28,7 +28,6 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ - use League\OAuth2\Client\Provider\Exception\FacebookProviderException; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use League\OAuth2\Client\Token\AccessToken; @@ -44,37 +43,37 @@ */ class Token extends AbstractEntity { - use EmitSignalTrait; + // use EmitSignalTrait; /** * facebook user token */ - const FACEBOOK = 1; + public const FACEBOOK = 1; /** * instagram_oauth2 */ - const INSTAGRAM = 2; + public const INSTAGRAM = 2; /** * twitter token */ - const TWITTER = 3; + public const TWITTER = 3; /** * youtube token */ - const YOUTUBE = 4; + public const YOUTUBE = 4; /** * facebook page token */ - const FACEBOOK_PAGE = 5; + public const FACEBOOK_PAGE = 5; /** * twitter token v2 API */ - const TWITTER_V2 = 6; + public const TWITTER_V2 = 6; /** * Default PID @@ -84,7 +83,7 @@ class Token extends AbstractEntity protected $pid = 0; /** - * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\BackendUserGroup> + * @var ObjectStorage|null * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy */ protected $beGroup; @@ -145,7 +144,7 @@ class Token extends AbstractEntity protected string $fbSocialId = ''; /** - * @var Token|null + * @var Token */ protected $parentToken; @@ -310,9 +309,9 @@ public function setBearerToken(string $bearerToken): void } /** - * @return ObjectStorage + * @return ObjectStorage|null */ - public function getBeGroup(): ObjectStorage + public function getBeGroup(): ?ObjectStorage { return $this->beGroup; } @@ -320,7 +319,7 @@ public function getBeGroup(): ObjectStorage /** * @param ObjectStorage $beGroup */ - public function setBeGroup(ObjectStorage $beGroup): void + public function setBeGroup($beGroup): void { $this->beGroup = $beGroup; } @@ -448,11 +447,11 @@ public function getFacebookPagesIds(): array */ public function getParentToken(): ?Token { - if ($this->parentToken instanceof Token) { + if ( $this->parentToken instanceof Token ) + { return $this->parentToken; - } - - return null; + } + return NULL; } /** diff --git a/Classes/Domain/Repository/AbstractBackendRepository.php b/Classes/Domain/Repository/AbstractBackendRepository.php index 3f511d78..69e73552 100644 --- a/Classes/Domain/Repository/AbstractBackendRepository.php +++ b/Classes/Domain/Repository/AbstractBackendRepository.php @@ -26,11 +26,11 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ - use Pixelant\PxaSocialFeed\Database\Query\Restriction\BackendGroupRestriction; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser; use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; +use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; use TYPO3\CMS\Extbase\Persistence\Repository; /** @@ -57,7 +57,7 @@ public function initializeObject() /** * Find all records with backend user group restriction * - * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface + * @return QueryResultInterface */ public function findAllBackendGroupRestriction() { diff --git a/Classes/Domain/Repository/FeedRepository.php b/Classes/Domain/Repository/FeedRepository.php index 577eb48a..73b80dc4 100644 --- a/Classes/Domain/Repository/FeedRepository.php +++ b/Classes/Domain/Repository/FeedRepository.php @@ -4,11 +4,14 @@ use Pixelant\PxaSocialFeed\Domain\Model\Configuration; use Pixelant\PxaSocialFeed\Domain\Model\Feed; +use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\Http\ApplicationType; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult; use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; use TYPO3\CMS\Extbase\Persistence\ObjectStorage; use TYPO3\CMS\Extbase\Persistence\QueryInterface; +use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; use TYPO3\CMS\Extbase\Persistence\Repository; /*************************************************************** @@ -58,10 +61,10 @@ public function initializeObject() // Don't respect storage $defaultQuerySettings->setRespectStoragePage(false); - if (TYPO3_MODE === 'BE' || TYPO3_MODE === 'CLI') { + if (ApplicationType::fromRequest($GLOBALS[ 'TYPO3_REQUEST' ])->isBackend() || Environment::isCli()) { // don't add fields from enable columns constraint $defaultQuerySettings->setIgnoreEnableFields(true); - $defaultQuerySettings->setEnableFieldsToBeIgnored(['disabled']); + $defaultQuerySettings->setEnableFieldsToBeIgnored([ 'disabled' ]); } $this->setDefaultQuerySettings($defaultQuerySettings); @@ -72,17 +75,17 @@ public function initializeObject() * * @param ObjectStorage $storage * @param Configuration $configuration - * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface + * @return QueryResultInterface */ public function findNotInStorage(ObjectStorage $storage, Configuration $configuration) { $query = $this->createQuery(); $query->matching( - $query->logicalAnd([ + $query->logicalAnd( $query->logicalNot($query->in('uid', $storage)), $query->equals('configuration', $configuration), - ]) + ) ); return $query->execute(); @@ -126,12 +129,12 @@ public function findOneByExternalIdentifier(string $externalIdentifier, int $pid { $query = $this->createQuery(); - $logicalAnd = [ - $query->equals('pid', $pid), - $query->equals('externalIdentifier', $externalIdentifier), - ]; - - $query->matching($query->logicalAnd($logicalAnd)); + $query->matching( + $query->logicalAnd( + $query->equals('pid', $pid), + $query->equals('externalIdentifier', $externalIdentifier), + ), + ); return $query->execute()->getFirst(); } diff --git a/Classes/Domain/Repository/TokenRepository.php b/Classes/Domain/Repository/TokenRepository.php index b03abb49..06deae77 100644 --- a/Classes/Domain/Repository/TokenRepository.php +++ b/Classes/Domain/Repository/TokenRepository.php @@ -26,12 +26,12 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ - use Pixelant\PxaSocialFeed\Domain\Model\Token; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult; use TYPO3\CMS\Extbase\Persistence\QueryInterface; +use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; /** * The repository for Feeds @@ -51,18 +51,20 @@ class TokenRepository extends AbstractBackendRepository * @param Token $token * @param string $fbSocialId * - * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface + * @return QueryResultInterface */ public function findFacebookPageToken(Token $token, string $fbSocialId) { $query = $this->createQuery(); - $query->getQuerySettings()->setIgnoreEnableFields(true); - + $query->getQuerySettings ()->setIgnoreEnableFields ( TRUE ); + //TODO: handle Facebook Social ID and parent token + // $query->equals('parentToken', $token->getParentToken()), + // $query->equals('fbSocialId', $fbSocialId), $query->matching( - $query->logicalAnd([ - $query->equals('parentToken', $token->getParentToken()), - $query->equals('fbSocialId', $fbSocialId), - ]) + $query->logicalAnd( + $query->equals ( 'parentToken', 0 ), + $query->equals ( 'fbSocialId', '' ), + ) ); $query->setLimit(1); diff --git a/Classes/Domain/Validation/Validator/ConfigurationValidator.php b/Classes/Domain/Validation/Validator/ConfigurationValidator.php index 76c64f5b..e6560412 100644 --- a/Classes/Domain/Validation/Validator/ConfigurationValidator.php +++ b/Classes/Domain/Validation/Validator/ConfigurationValidator.php @@ -38,7 +38,7 @@ class ConfigurationValidator extends AbstractValidator * * @return bool */ - public function isValid($configuration) + public function isValid($configuration): void { // do trim $this->trimObjectProperties($configuration); @@ -62,10 +62,6 @@ public function isValid($configuration) $this->translateErrorMessage('validator.error.' . $errorCode, 'PxaSocialFeed'), $errorCode ); - - return false; } - - return true; } } diff --git a/Classes/Domain/Validation/Validator/TokenValidator.php b/Classes/Domain/Validation/Validator/TokenValidator.php index 4600df77..71842e62 100644 --- a/Classes/Domain/Validation/Validator/TokenValidator.php +++ b/Classes/Domain/Validation/Validator/TokenValidator.php @@ -36,10 +36,8 @@ class TokenValidator extends AbstractValidator * Validates tokens * * @param Token $token - * - * @return bool */ - protected function isValid($token) + protected function isValid($token): void { if (!in_array($token->getType(), Token::getAvailableTokensTypes())) { $this->addError( @@ -49,8 +47,6 @@ protected function isValid($token) ), 1562851281828 ); - - return false; } switch (true) { @@ -84,11 +80,7 @@ protected function isValid($token) ), 1221559976 ); - - return false; } } - - return true; } } diff --git a/Classes/Event/BeforeReturnTwitterQueryFieldsEvent.php b/Classes/Event/BeforeReturnTwitterQueryFieldsEvent.php new file mode 100644 index 00000000..f6c9e6ec --- /dev/null +++ b/Classes/Event/BeforeReturnTwitterQueryFieldsEvent.php @@ -0,0 +1,25 @@ +fields = $fields; + } + + public function getFields() + { + return $this->fields; + } + + public function setFields($fields): void + { + $this->fields = $fields; + } +} diff --git a/Classes/Event/BeforeUpdateFacebookFeedEvent.php b/Classes/Event/BeforeUpdateFacebookFeedEvent.php new file mode 100644 index 00000000..8116a2ad --- /dev/null +++ b/Classes/Event/BeforeUpdateFacebookFeedEvent.php @@ -0,0 +1,49 @@ +feedItem = $feedItem; + $this->rawData = $rawData; + $this->configuration = $configuration; + } + + public function getFeedItem() + { + return $this->feedItem; + } + + public function getRawData() + { + return $this->rawData; + } + + public function getConfiguration() + { + return $this->configuration; + } + + public function setFeedItem($feedItem): void + { + $this->feedItem = $feedItem; + } + + public function setRawData($rawData): void + { + $this->rawData = $rawData; + } + + public function setConfiguration($configuration): void + { + $this->configuration = $configuration; + } +} diff --git a/Classes/Event/BeforeUpdateInstagramFeedEvent.php b/Classes/Event/BeforeUpdateInstagramFeedEvent.php new file mode 100644 index 00000000..3900d6f4 --- /dev/null +++ b/Classes/Event/BeforeUpdateInstagramFeedEvent.php @@ -0,0 +1,49 @@ +feedItem = $feedItem; + $this->rawData = $rawData; + $this->configuration = $configuration; + } + + public function getFeedItem() + { + return $this->feedItem; + } + + public function getRawData() + { + return $this->rawData; + } + + public function getConfiguration() + { + return $this->configuration; + } + + public function setFeedItem($feedItem): void + { + $this->feedItem = $feedItem; + } + + public function setRawData($rawData): void + { + $this->rawData = $rawData; + } + + public function setConfiguration($configuration): void + { + $this->configuration = $configuration; + } +} diff --git a/Classes/Event/BeforeUpdateTwitterFeedEvent.php b/Classes/Event/BeforeUpdateTwitterFeedEvent.php new file mode 100644 index 00000000..475a155f --- /dev/null +++ b/Classes/Event/BeforeUpdateTwitterFeedEvent.php @@ -0,0 +1,49 @@ +feedItem = $feedItem; + $this->rawData = $rawData; + $this->configuration = $configuration; + } + + public function getFeedItem() + { + return $this->feedItem; + } + + public function getRawData() + { + return $this->rawData; + } + + public function getConfiguration() + { + return $this->configuration; + } + + public function setFeedItem($feedItem): void + { + $this->feedItem = $feedItem; + } + + public function setRawData($rawData): void + { + $this->rawData = $rawData; + } + + public function setConfiguration($configuration): void + { + $this->configuration = $configuration; + } +} diff --git a/Classes/Event/BeforeUpdateTwitterV2FeedEvent.php b/Classes/Event/BeforeUpdateTwitterV2FeedEvent.php new file mode 100644 index 00000000..45eeb085 --- /dev/null +++ b/Classes/Event/BeforeUpdateTwitterV2FeedEvent.php @@ -0,0 +1,59 @@ +feedItem = $feedItem; + $this->rawData = $rawData; + $this->configuration = $configuration; + $this->includes = $includes; + } + + public function getFeedItem() + { + return $this->feedItem; + } + + public function getRawData() + { + return $this->rawData; + } + + public function getConfiguration() + { + return $this->configuration; + } + public function getIncludes() + { + return $this->includes; + } + + public function setFeedItem($feedItem): void + { + $this->feedItem = $feedItem; + } + + public function setRawData($rawData): void + { + $this->rawData = $rawData; + } + + public function setConfiguration($configuration): void + { + $this->configuration = $configuration; + } + public function setIncludes($includes): void + { + $this->includes = $includes; + } +} diff --git a/Classes/Event/BeforeUpdateYoutubeFeedEvent.php b/Classes/Event/BeforeUpdateYoutubeFeedEvent.php new file mode 100644 index 00000000..8f804e16 --- /dev/null +++ b/Classes/Event/BeforeUpdateYoutubeFeedEvent.php @@ -0,0 +1,49 @@ +feedItem = $feedItem; + $this->rawData = $rawData; + $this->configuration = $configuration; + } + + public function getFeedItem() + { + return $this->feedItem; + } + + public function getRawData() + { + return $this->rawData; + } + + public function getConfiguration() + { + return $this->configuration; + } + + public function setFeedItem($feedItem): void + { + $this->feedItem = $feedItem; + } + + public function setRawData($rawData): void + { + $this->rawData = $rawData; + } + + public function setConfiguration($configuration): void + { + $this->configuration = $configuration; + } +} diff --git a/Classes/Event/ChangedFeedItemEvent.php b/Classes/Event/ChangedFeedItemEvent.php new file mode 100644 index 00000000..7ff60df0 --- /dev/null +++ b/Classes/Event/ChangedFeedItemEvent.php @@ -0,0 +1,24 @@ +feed = $feed; + } + public function getFeed() + { + return $this->feed; + } + + public function setFeed($feed): void + { + $this->feed = $feed; + } +} diff --git a/Classes/Event/FacebookEndPointEvent.php b/Classes/Event/FacebookEndPointEvent.php new file mode 100644 index 00000000..8a29c12f --- /dev/null +++ b/Classes/Event/FacebookEndPointEvent.php @@ -0,0 +1,33 @@ +endPoint = $endPoint; + } + + /** + * @return mixed + */ + public function getEndPoint() + { + return $this->endPoint; + } + + /** + * @param mixed $endPoint + * @return self + */ + public function setEndPoint($endPoint): self + { + $this->endPoint = $endPoint; + return $this; + } +} diff --git a/Classes/Event/FacebookEndPointRequestFieldsEvent.php b/Classes/Event/FacebookEndPointRequestFieldsEvent.php new file mode 100644 index 00000000..92c36c51 --- /dev/null +++ b/Classes/Event/FacebookEndPointRequestFieldsEvent.php @@ -0,0 +1,25 @@ +fields = $fields; + } + + public function getFields() + { + return $this->fields; + } + + public function setFields($fields): void + { + $this->fields = $fields; + } +} diff --git a/Classes/Event/RemovedFeedItemEvent.php b/Classes/Event/RemovedFeedItemEvent.php new file mode 100644 index 00000000..3e8d4f2b --- /dev/null +++ b/Classes/Event/RemovedFeedItemEvent.php @@ -0,0 +1,27 @@ +feed = $feed; + } + + public function getFeed(): Feed + { + return $this->feed; + } + + public function setFeed(Feed $feed): void + { + $this->feed = $feed; + } +} diff --git a/Classes/Event/YoutubeEndPointRequestFieldsEvent.php b/Classes/Event/YoutubeEndPointRequestFieldsEvent.php new file mode 100644 index 00000000..5682dfaf --- /dev/null +++ b/Classes/Event/YoutubeEndPointRequestFieldsEvent.php @@ -0,0 +1,25 @@ +fields = $fields; + } + + public function getFields() + { + return $this->fields; + } + + public function setFields($fields): void + { + $this->fields = $fields; + } +} diff --git a/Classes/Exception/BadResponseException.php b/Classes/Exception/BadResponseException.php index 25e4171e..4a4f496c 100644 --- a/Classes/Exception/BadResponseException.php +++ b/Classes/Exception/BadResponseException.php @@ -7,6 +7,4 @@ /** * Class BadResponseException */ -class BadResponseException extends \Exception -{ -} +class BadResponseException extends \Exception {} diff --git a/Classes/Exception/FailedExecutingImportException.php b/Classes/Exception/FailedExecutingImportException.php index ef38261e..3ed4b359 100644 --- a/Classes/Exception/FailedExecutingImportException.php +++ b/Classes/Exception/FailedExecutingImportException.php @@ -4,6 +4,4 @@ namespace Pixelant\PxaSocialFeed\Exception; -class FailedExecutingImportException extends \Exception -{ -} +class FailedExecutingImportException extends \Exception {} diff --git a/Classes/Exception/InvalidFeedSourceData.php b/Classes/Exception/InvalidFeedSourceData.php index 9a1cbe58..f1b1965a 100644 --- a/Classes/Exception/InvalidFeedSourceData.php +++ b/Classes/Exception/InvalidFeedSourceData.php @@ -7,6 +7,4 @@ /** * Class InvalidFeedSourceData */ -class InvalidFeedSourceData extends \Exception -{ -} +class InvalidFeedSourceData extends \Exception {} diff --git a/Classes/Exception/UnsupportedTokenType.php b/Classes/Exception/UnsupportedTokenType.php index 1a94802a..c008b5cc 100644 --- a/Classes/Exception/UnsupportedTokenType.php +++ b/Classes/Exception/UnsupportedTokenType.php @@ -7,6 +7,4 @@ /** * Class UnsupportedTokenType */ -class UnsupportedTokenType extends \Exception -{ -} +class UnsupportedTokenType extends \Exception {} diff --git a/Classes/Feed/Source/BaseFacebookSource.php b/Classes/Feed/Source/BaseFacebookSource.php index 2ecb4df2..5d400d1a 100644 --- a/Classes/Feed/Source/BaseFacebookSource.php +++ b/Classes/Feed/Source/BaseFacebookSource.php @@ -4,14 +4,18 @@ namespace Pixelant\PxaSocialFeed\Feed\Source; +use Pixelant\PxaSocialFeed\Event\FacebookEndPointEvent; +use Pixelant\PxaSocialFeed\Event\FacebookEndPointRequestFieldsEvent; use Pixelant\PxaSocialFeed\Exception\InvalidFeedSourceData; +use Psr\EventDispatcher\EventDispatcherInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Class BaseFacebookSource */ abstract class BaseFacebookSource extends BaseSource { - const GRAPH_VERSION = 'v12.0'; + public const GRAPH_VERSION = 'v12.0'; /** * Generate facebook endpoint @@ -22,18 +26,17 @@ abstract class BaseFacebookSource extends BaseSource */ protected function generateEndPoint(string $id, string $endPointEntry): string { - $limit = $this->getConfiguration()->getMaxItems(); - - $fields = $this->getEndPointFields(); - - list($fields) = $this->emitSignal('facebookEndPointRequestFields', [$fields]); - + $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); + $limit = $this->getConfiguration()->getMaxItems(); + $fields = $this->getEndPointFields(); + $event = $eventDispatcher->dispatch(new FacebookEndPointRequestFieldsEvent($fields)); + $fieldsArray = $event->getFields(); $url = $id . '/' . $endPointEntry; $queryParams = [ - 'fields' => implode(',', $fields), - 'limit' => $limit, - 'access_token' => $this->getConfiguration()->getToken()->getAccessToken(), + 'fields' => implode(',', $fieldsArray), + 'limit' => $limit, + 'access_token' => $this->getConfiguration()->getToken()->getAccessToken(), 'appsecret_proof' => hash_hmac( 'sha256', $this->getConfiguration()->getToken()->getAccessToken(), @@ -42,10 +45,14 @@ protected function generateEndPoint(string $id, string $endPointEntry): string ]; $endPoint = $this->addFieldsAsGetParametersToUrl($url, $queryParams); + $event = $eventDispatcher->dispatch(new FacebookEndPointEvent($endPoint)); + + // dd ( $event->getEndPoint () ); + // $endPointArray = $event->getEndPoint (); - list($endPoint) = $this->emitSignal('faceBookEndPoint', [$endPoint]); + // list( $endPoint ) = $endPointArray; - return $endPoint; + return $event->getEndPoint(); } /** diff --git a/Classes/Feed/Source/BaseSource.php b/Classes/Feed/Source/BaseSource.php index 6490be44..cfcf88f0 100644 --- a/Classes/Feed/Source/BaseSource.php +++ b/Classes/Feed/Source/BaseSource.php @@ -6,7 +6,6 @@ use Pixelant\PxaSocialFeed\Domain\Model\Configuration; use Pixelant\PxaSocialFeed\Exception\BadResponseException; -use Pixelant\PxaSocialFeed\SignalSlot\EmitSignalTrait; use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Core\Http\RequestFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -16,8 +15,6 @@ */ abstract class BaseSource implements FeedSourceInterface { - use EmitSignalTrait; - /** * @var Configuration */ diff --git a/Classes/Feed/Source/InstagramSource.php b/Classes/Feed/Source/InstagramSource.php index 310204ae..bf01899f 100644 --- a/Classes/Feed/Source/InstagramSource.php +++ b/Classes/Feed/Source/InstagramSource.php @@ -9,7 +9,7 @@ */ class InstagramSource extends BaseFacebookSource { - const BASE_INSTAGRAM_GRAPH_URL = 'https://graph.facebook.com/'; + public const BASE_INSTAGRAM_GRAPH_URL = 'https://graph.facebook.com/'; /** * Load feed source diff --git a/Classes/Feed/Source/TwitterSource.php b/Classes/Feed/Source/TwitterSource.php index e10af4fb..2fc398df 100644 --- a/Classes/Feed/Source/TwitterSource.php +++ b/Classes/Feed/Source/TwitterSource.php @@ -4,9 +4,12 @@ namespace Pixelant\PxaSocialFeed\Feed\Source; +use Pixelant\PxaSocialFeed\Event\BeforeReturnTwitterQueryFieldsEvent; use Pixelant\PxaSocialFeed\Exception\BadResponseException; use Pixelant\PxaSocialFeed\Exception\InvalidFeedSourceData; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\ResponseInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Class TwitterSource @@ -16,7 +19,7 @@ class TwitterSource extends BaseSource /** * Twitter api */ - const API_URL = 'https://api.twitter.com/1.1/'; + public const API_URL = 'https://api.twitter.com/1.1/'; /** * Load feed source @@ -99,16 +102,17 @@ protected function getFields(): array // Important to pass field value as string, because it's encoded with rawurlencode $fields = [ - 'screen_name' => $configuration->getSocialId(), - 'count' => (string)$configuration->getMaxItems(), - 'tweet_mode' => 'extended', + 'screen_name' => $configuration->getSocialId(), + 'count' => (string)$configuration->getMaxItems(), + 'tweet_mode' => 'extended', 'exclude_replies' => '1', - 'include_rts' => '1', + 'include_rts' => '1', ]; - list($fields) = $this->emitSignal('beforeReturnTwitterQueryFields', [$fields]); + $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); + $event = $eventDispatcher->dispatch(new BeforeReturnTwitterQueryFieldsEvent($fields)); - return $fields; + return $event->getFields(); } /** diff --git a/Classes/Feed/Source/TwitterV2Source.php b/Classes/Feed/Source/TwitterV2Source.php index 8b5648e4..035dcc00 100644 --- a/Classes/Feed/Source/TwitterV2Source.php +++ b/Classes/Feed/Source/TwitterV2Source.php @@ -4,8 +4,10 @@ namespace Pixelant\PxaSocialFeed\Feed\Source; +use Pixelant\PxaSocialFeed\Event\BeforeReturnTwitterQueryFieldsEvent; use Pixelant\PxaSocialFeed\Exception\BadResponseException; use Pixelant\PxaSocialFeed\Exception\InvalidFeedSourceData; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\ResponseInterface; /** @@ -16,7 +18,11 @@ class TwitterV2Source extends BaseSource /** * Twitter api */ - const API_URL = 'https://api.twitter.com/2/'; + public const API_URL = 'https://api.twitter.com/2/'; + + public function __construct( + private readonly EventDispatcherInterface $eventDispatcher, + ) {} /** * Load feed source @@ -117,7 +123,7 @@ protected function getFields(): array 'exclude' => 'replies', ]; - [$fields] = $this->emitSignal('beforeReturnTwitterQueryFields', [$fields]); + [ $fields ] = $this->eventDispatcher->dispatch(new BeforeReturnTwitterQueryFieldsEvent($fields)); return $fields; } diff --git a/Classes/Feed/Source/YoutubeSource.php b/Classes/Feed/Source/YoutubeSource.php index 78a7ad2b..1da3b467 100644 --- a/Classes/Feed/Source/YoutubeSource.php +++ b/Classes/Feed/Source/YoutubeSource.php @@ -5,16 +5,19 @@ namespace Pixelant\PxaSocialFeed\Feed\Source; use Pixelant\PxaSocialFeed\Domain\Model\Configuration; +use Pixelant\PxaSocialFeed\Event\YoutubeEndPointRequestFieldsEvent; use Pixelant\PxaSocialFeed\Exception\BadResponseException; use Pixelant\PxaSocialFeed\Exception\InvalidFeedSourceData; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\ResponseInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Class YoutubeSource */ class YoutubeSource extends BaseSource { - const API_URL = 'https://www.googleapis.com/youtube/v3/'; + public const API_URL = 'https://www.googleapis.com/youtube/v3/'; /** * Load feed source @@ -94,8 +97,10 @@ protected function getFields(Configuration $configuration): array 'key' => $configuration->getToken()->getApiKey(), ]; - list($fields) = $this->emitSignal('youtubeEndPointRequestFields', [$fields]); + $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); + $event = $eventDispatcher->dispatch(new YoutubeEndPointRequestFieldsEvent($fields)); + $fieldsArray = $event->getFields(); - return $fields; + return $fieldsArray; } } diff --git a/Classes/Feed/Update/BaseUpdater.php b/Classes/Feed/Update/BaseUpdater.php index 7ff0d9de..927d3773 100644 --- a/Classes/Feed/Update/BaseUpdater.php +++ b/Classes/Feed/Update/BaseUpdater.php @@ -4,9 +4,8 @@ namespace Pixelant\PxaSocialFeed\Feed\Update; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\Exception as DriverException; -use Exception; +use Doctrine\DBAL\Exception; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use InvalidArgumentException; @@ -14,7 +13,9 @@ use Pixelant\PxaSocialFeed\Domain\Model\Feed; use Pixelant\PxaSocialFeed\Domain\Model\FileReference; use Pixelant\PxaSocialFeed\Domain\Repository\FeedRepository; -use Pixelant\PxaSocialFeed\SignalSlot\EmitSignalTrait; +use Pixelant\PxaSocialFeed\Event\ChangedFeedItemEvent; +use Pixelant\PxaSocialFeed\Event\RemovedFeedItemEvent; +use Psr\EventDispatcher\EventDispatcherInterface; use RuntimeException; use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException; use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException; @@ -37,8 +38,6 @@ */ abstract class BaseUpdater implements FeedUpdaterInterface { - use EmitSignalTrait; - /** * @var FeedRepository */ @@ -81,14 +80,12 @@ public function persist(): void */ public function cleanUp(Configuration $configuration): void { + $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); if (count($this->feeds) > 0) { /** @var Feed $feedToRemove */ foreach ($this->feedRepository->findNotInStorage($this->feeds, $configuration) as $feedToRemove) { - // todo: remove in next major version - /** @deprecated The call to changedFeedItem is deprecated and will be removed in version 4 */ - $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'changedFeedItem', [$feedToRemove]); - - $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'removedFeedItem', [$feedToRemove]); + $eventDispatcher->dispatch(new ChangedFeedItemEvent($feedToRemove)); + $eventDispatcher->dispatch(new RemovedFeedItemEvent($feedToRemove)); $this->feedRepository->remove($feedToRemove); } } @@ -102,9 +99,10 @@ public function cleanUp(Configuration $configuration): void */ protected function addOrUpdateFeedItem(Feed $feed): void { + $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); // Check if $feed is new or modified and emit change event if ($feed->_isDirty() || $feed->_isNew()) { - $this->getSignalSlotDispatcher()->dispatch(__CLASS__, 'changedFeedItem', [$feed]); + $eventDispatcher->dispatch(new ChangedFeedItemEvent($feed)); } $this->feeds->attach($feed); @@ -182,7 +180,7 @@ protected function storeImg(string $url, Feed $feed): ?FileReference * @param Configuration $configuration * @return File|null * @throws InvalidArgumentException - * @throws DBALException + * @throws Exception * @throws DriverException * @throws InsufficientFolderAccessPermissionsException * @throws ExistingTargetFolderException diff --git a/Classes/Feed/Update/FacebookFeedUpdater.php b/Classes/Feed/Update/FacebookFeedUpdater.php index 39fc2a65..72897d01 100644 --- a/Classes/Feed/Update/FacebookFeedUpdater.php +++ b/Classes/Feed/Update/FacebookFeedUpdater.php @@ -7,14 +7,16 @@ use Pixelant\PxaSocialFeed\Domain\Model\Configuration; use Pixelant\PxaSocialFeed\Domain\Model\Feed; use Pixelant\PxaSocialFeed\Domain\Model\Token; +use Pixelant\PxaSocialFeed\Event\BeforeUpdateFacebookFeedEvent; use Pixelant\PxaSocialFeed\Feed\Source\FeedSourceInterface; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Class FacebookFeedUpdater */ class FacebookFeedUpdater extends BaseUpdater -{ + { /** * Create/Update feed items * @@ -56,11 +58,10 @@ protected function updateFeedItem(Feed $feedItem, array $rawData): void $feedItem->setUpdateDate((new \DateTime())->setTimestamp($updated)); } - $feedItem->setLikes((int)($rawData['reactions']['summary']['total_count'])); - - // Call hook - $this->emitSignal('beforeUpdateFacebookFeed', [$feedItem, $rawData, $feedItem->getConfiguration()]); - + $feedItem->setLikes((int)($rawData[ 'reactions' ][ 'summary' ][ 'total_count' ])); + // dispatch event + $eventDispatcher = GeneralUtility::makeInstance ( EventDispatcherInterface::class); + $eventDispatcher->dispatch ( new BeforeUpdateFacebookFeedEvent ( $feedItem, $rawData, $feedItem->getConfiguration () ) ); $this->addOrUpdateFeedItem($feedItem); } diff --git a/Classes/Feed/Update/InstagramFeedUpdater.php b/Classes/Feed/Update/InstagramFeedUpdater.php index f94d25ae..e8a77939 100644 --- a/Classes/Feed/Update/InstagramFeedUpdater.php +++ b/Classes/Feed/Update/InstagramFeedUpdater.php @@ -7,7 +7,9 @@ use Pixelant\PxaSocialFeed\Domain\Model\Configuration; use Pixelant\PxaSocialFeed\Domain\Model\Feed; use Pixelant\PxaSocialFeed\Domain\Model\Token; +use Pixelant\PxaSocialFeed\Event\BeforeUpdateInstagramFeedEvent; use Pixelant\PxaSocialFeed\Feed\Source\FeedSourceInterface; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -38,10 +40,9 @@ public function update(FeedSourceInterface $source): void // Add/update instagram feed data gotten from facebook $this->populateGraphInstagramFeed($feedItem, $rawData); - - // Call hook - $this->emitSignal('beforeUpdateInstagramFeed', [$feedItem, $rawData, $source->getConfiguration()]); - + // dispatch event + $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); + $eventDispatcher->dispatch(new BeforeUpdateInstagramFeedEvent($feedItem, $rawData, $source->getConfiguration())); // Add/update $this->addOrUpdateFeedItem($feedItem); } diff --git a/Classes/Feed/Update/TwitterFeedUpdater.php b/Classes/Feed/Update/TwitterFeedUpdater.php index f54fa140..81b2cfb4 100644 --- a/Classes/Feed/Update/TwitterFeedUpdater.php +++ b/Classes/Feed/Update/TwitterFeedUpdater.php @@ -7,7 +7,9 @@ use Pixelant\PxaSocialFeed\Domain\Model\Configuration; use Pixelant\PxaSocialFeed\Domain\Model\Feed; use Pixelant\PxaSocialFeed\Domain\Model\Token; +use Pixelant\PxaSocialFeed\Event\BeforeUpdateTwitterFeedEvent; use Pixelant\PxaSocialFeed\Feed\Source\FeedSourceInterface; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -15,6 +17,15 @@ */ class TwitterFeedUpdater extends BaseUpdater { + /** + * @var EventDispatcherInterface + */ + private EventDispatcherInterface $eventDispatcher; + + public function injectEventDispatcher(EventDispatcherInterface $eventDispatcher): void + { + $this->eventDispatcher = $eventDispatcher; + } /** * Create/Update feed items * @@ -35,10 +46,7 @@ public function update(FeedSourceInterface $source): void } $this->updateFeedItem($feedItem, $rawData); - - // Call hook - $this->emitSignal('beforeUpdateTwitterFeed', [$feedItem, $rawData, $source->getConfiguration()]); - + $this->eventDispatcher->dispatch(new BeforeUpdateTwitterFeedEvent($feedItem, $rawData, $source->getConfiguration())); $this->addOrUpdateFeedItem($feedItem); } } diff --git a/Classes/Feed/Update/TwitterV2FeedUpdater.php b/Classes/Feed/Update/TwitterV2FeedUpdater.php index b2d45f52..220594d3 100644 --- a/Classes/Feed/Update/TwitterV2FeedUpdater.php +++ b/Classes/Feed/Update/TwitterV2FeedUpdater.php @@ -7,6 +7,7 @@ use Pixelant\PxaSocialFeed\Domain\Model\Configuration; use Pixelant\PxaSocialFeed\Domain\Model\Feed; use Pixelant\PxaSocialFeed\Domain\Model\Token; +use Pixelant\PxaSocialFeed\Event\BeforeUpdateTwitterV2FeedEvent; use Pixelant\PxaSocialFeed\Feed\Source\FeedSourceInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -38,10 +39,8 @@ public function update(FeedSourceInterface $source): void } $this->updateFeedItem($feedItem, $rawData, $includes); - - // Call hook - $this->emitSignal('beforeUpdateTwitterFeed', [$feedItem, $rawData, $source->getConfiguration(), $includes]); - + // dispatch event + $this->eventDispatcher->dispatch(new BeforeUpdateTwitterV2FeedEvent($feedItem, $rawData, $source->getConfiguration(), $includes)); $this->addOrUpdateFeedItem($feedItem); } } diff --git a/Classes/Feed/Update/YoutubeFeedUpdater.php b/Classes/Feed/Update/YoutubeFeedUpdater.php index 8e933344..8dc4f1e8 100644 --- a/Classes/Feed/Update/YoutubeFeedUpdater.php +++ b/Classes/Feed/Update/YoutubeFeedUpdater.php @@ -7,7 +7,9 @@ use Pixelant\PxaSocialFeed\Domain\Model\Configuration; use Pixelant\PxaSocialFeed\Domain\Model\Feed; use Pixelant\PxaSocialFeed\Domain\Model\Token; +use Pixelant\PxaSocialFeed\Event\BeforeUpdateYoutubeFeedEvent; use Pixelant\PxaSocialFeed\Feed\Source\FeedSourceInterface; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -35,10 +37,9 @@ public function update(FeedSourceInterface $source): void } $this->updateFeedItem($feedItem, $rawData); - - // Call hook - $this->emitSignal('beforeUpdateYoutubeFeed', [$feedItem, $rawData, $source->getConfiguration()]); - + // dispatch event + $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); + $eventDispatcher->dispatch(new BeforeUpdateYoutubeFeedEvent($feedItem, $rawData, $source->getConfiguration())); $this->addOrUpdateFeedItem($feedItem); } } diff --git a/Classes/Hooks/PageLayoutView.php b/Classes/Hooks/PageLayoutView.php index 09821472..bb2954e3 100644 --- a/Classes/Hooks/PageLayoutView.php +++ b/Classes/Hooks/PageLayoutView.php @@ -26,9 +26,9 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ - use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Fluid\View\StandaloneView; @@ -70,8 +70,7 @@ public function getExtensionInformation($params): string $configurations = $queryBuilder ->select('name') - ->from('tx_pxasocialfeed_domain_model_configuration') - ->where( + ->from('tx_pxasocialfeed_domain_model_configuration')->where( $queryBuilder->expr()->in( 'uid', $queryBuilder->createNamedParameter( @@ -79,9 +78,8 @@ public function getExtensionInformation($params): string Connection::PARAM_INT_ARRAY ) ) - ) - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN); + )->executeQuery() + ->fetchAllAssociative(); if (is_array($configurations)) { $configurations = implode(', ', $configurations); @@ -112,10 +110,10 @@ protected function getView(): StandaloneView } /** - * @return \TYPO3\CMS\Core\Service\FlexFormService + * @return FlexFormService */ protected function getFlexFormService() { - return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Service\FlexFormService::class); + return GeneralUtility::makeInstance(FlexFormService::class); } } diff --git a/Classes/Task/AdditionalFieldProviderTrait.php b/Classes/Task/AdditionalFieldProviderTrait.php index eed16c69..956bcb8e 100644 --- a/Classes/Task/AdditionalFieldProviderTrait.php +++ b/Classes/Task/AdditionalFieldProviderTrait.php @@ -4,9 +4,10 @@ namespace Pixelant\PxaSocialFeed\Task; -use TYPO3\CMS\Core\Messaging\AbstractMessage; +use TYPO3\CMS\Core\Messaging\FlashMessage; use TYPO3\CMS\Core\Messaging\FlashMessageQueue; use TYPO3\CMS\Core\Messaging\FlashMessageService; +use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Scheduler\Controller\SchedulerModuleController; @@ -30,29 +31,30 @@ protected function getAction(SchedulerModuleController $schedulerModuleControlle { return method_exists($schedulerModuleController, 'getCurrentAction') ? (string)$schedulerModuleController->getCurrentAction() - : $schedulerModuleController->CMD; + : (string)$schedulerModuleController->getCurrentAction(); } /** - * Creates a Message object and adds it to the FlashMessageQueue. + * Add a flash message * - * @param string $messageBody The message - * @param int $severity Optional severity, must be one of \TYPO3\CMS\Core\Messaging\FlashMessage constants - * @throws \InvalidArgumentException if the message body is no string + * @param string $message the flash message content + * @param value-of|ContextualFeedbackSeverity $severity the flash message severity + * + * @todo: Change $severity to allow ContextualFeedbackSeverity only in v13 */ - protected function addMessage(string $messageBody, int $severity = AbstractMessage::OK): void + protected function addMessage(string $message, int|ContextualFeedbackSeverity $severity = ContextualFeedbackSeverity::OK): void { - if (!is_string($messageBody)) { + if (!is_string($message)) { throw new \InvalidArgumentException( - 'The message body must be of type string, "' . gettype($messageBody) . '" given.', + 'The message body must be of type string, "' . gettype($message) . '" given.', 1548921638461 ); } /* @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */ $flashMessage = GeneralUtility::makeInstance( - \TYPO3\CMS\Core\Messaging\FlashMessage::class, - $messageBody, + FlashMessage::class, + $message, '', $severity, true diff --git a/Classes/Task/ImportTaskAdditionalFieldProvider.php b/Classes/Task/ImportTaskAdditionalFieldProvider.php index 33e6a004..8a76826c 100644 --- a/Classes/Task/ImportTaskAdditionalFieldProvider.php +++ b/Classes/Task/ImportTaskAdditionalFieldProvider.php @@ -5,9 +5,9 @@ namespace Pixelant\PxaSocialFeed\Task; use Pixelant\PxaSocialFeed\Utility\SchedulerUtility; -use TYPO3\CMS\Core\Messaging\FlashMessage; +use TYPO3\CMS\Core\Messaging\AbstractMessage; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface; +use TYPO3\CMS\Scheduler\AbstractAdditionalFieldProvider; use TYPO3\CMS\Scheduler\Controller\SchedulerModuleController; use TYPO3\CMS\Scheduler\Task\AbstractTask; @@ -35,10 +35,9 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -class ImportTaskAdditionalFieldProvider implements AdditionalFieldProviderInterface +class ImportTaskAdditionalFieldProvider extends AbstractAdditionalFieldProvider { use AdditionalFieldProviderTrait; - /** * @param array $taskInfo * @param ImportTask $task @@ -97,7 +96,6 @@ public function getAdditionalFields( return $additionalFields; } - /** * @param array $submittedData * @param SchedulerModuleController $parentObject @@ -113,18 +111,17 @@ public function validateAdditionalFields( if (!isset($submittedData['pxasocialfeed_run_all_configs']) && !isset($submittedData['pxasocialfeed_configs']) ) { - $this->addMessage('Wrong configurations select', FlashMessage::ERROR); + $this->addMessage('Wrong configurations select', AbstractMessage::ERROR); } elseif (!$this->isValidEmail($submittedData['pxasocialfeed_sender_email']) || !$this->isValidEmail($submittedData['pxasocialfeed_receiver_email']) ) { - $this->addMessage('Please provide a valid email address.', FlashMessage::ERROR); + $this->addMessage('Please provide a valid email address.', AbstractMessage::ERROR); } else { $valid = true; } return $valid; } - /** * @param array $submittedData * @param ImportTask $task @@ -136,7 +133,6 @@ public function saveAdditionalFields(array $submittedData, AbstractTask $task) $task->setSenderEmail($submittedData['pxasocialfeed_sender_email']); $task->setRunAllConfigurations((bool)($submittedData['pxasocialfeed_run_all_configs'] ?? false)); } - /** * Input field code * @@ -153,7 +149,6 @@ protected function getInputField(string $fieldName, string $value): string htmlspecialchars($value) ); } - /** * Validate email * diff --git a/Classes/Utility/ConfigurationUtility.php b/Classes/Utility/ConfigurationUtility.php index d838a1ec..c9baf50a 100644 --- a/Classes/Utility/ConfigurationUtility.php +++ b/Classes/Utility/ConfigurationUtility.php @@ -4,6 +4,8 @@ namespace Pixelant\PxaSocialFeed\Utility; +use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException; +use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -16,8 +18,8 @@ class ConfigurationUtility * Get extension configuration * * @return array - * @throws \TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException - * @throws \TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException + * @throws ExtensionConfigurationExtensionNotConfiguredException + * @throws ExtensionConfigurationPathDoesNotExistException */ public static function getExtensionConfiguration(): array { diff --git a/Classes/Utility/LoggerUtility.php b/Classes/Utility/LoggerUtility.php index 51247713..d5cf4469 100644 --- a/Classes/Utility/LoggerUtility.php +++ b/Classes/Utility/LoggerUtility.php @@ -14,8 +14,8 @@ class LoggerUtility /** * Message type */ - const INFO = 0; - const ERROR = 1; + public const INFO = 0; + public const ERROR = 1; /** * Log error diff --git a/Classes/Utility/SchedulerUtility.php b/Classes/Utility/SchedulerUtility.php index e7b4f7c6..81437e0d 100644 --- a/Classes/Utility/SchedulerUtility.php +++ b/Classes/Utility/SchedulerUtility.php @@ -87,8 +87,7 @@ public static function getSelectedConfigurationsInfo(array $configurations, bool $statement = $queryBuilder ->select('uid', 'name') - ->from('tx_pxasocialfeed_domain_model_configuration') - ->where( + ->from('tx_pxasocialfeed_domain_model_configuration')->where( $queryBuilder->expr()->in( 'uid', $queryBuilder->createNamedParameter( @@ -96,12 +95,11 @@ public static function getSelectedConfigurationsInfo(array $configurations, bool Connection::PARAM_INT_ARRAY ) ) - ) - ->execute(); + )->executeQuery(); $info = 'Feeds: '; - while ($config = $statement->fetch()) { + while ($config = $statement->fetchAssociative()) { $info .= $config['name'] . ' [UID: ' . $config['uid'] . ']; '; } diff --git a/Classes/ViewHelpers/FacebookLoginUrlViewHelper.php b/Classes/ViewHelpers/FacebookLoginUrlViewHelper.php index aabd5c77..213c367f 100644 --- a/Classes/ViewHelpers/FacebookLoginUrlViewHelper.php +++ b/Classes/ViewHelpers/FacebookLoginUrlViewHelper.php @@ -103,7 +103,7 @@ public static function renderStatic( $variableProvider->add($redirectUrlAs, $redirectUrl); - if (strpos($url, 'redirect_uri=&') !== false) { + if (str_contains($url, 'redirect_uri=&')) { $urlStructure = explode('redirect_uri=&', $url); $url = sprintf( '%sredirect_uri=%s&%s', diff --git a/Configuration/Backend/Modules.php b/Configuration/Backend/Modules.php new file mode 100644 index 00000000..2e9f49a9 --- /dev/null +++ b/Configuration/Backend/Modules.php @@ -0,0 +1,43 @@ + [ + 'parent' => 'tools', + 'position' => [ 'after' => 'extensions' ], + 'access' => 'user,group', + 'workspaces' => '*', + 'path' => '/module/tools/PxaSocialFeedPxasocialfeed', + 'iconIdentifier' => 'ext-pxasocialfeed-wizard-icon', + 'labels' => 'LLL:EXT:pxa_social_feed/Resources/Private/Language/locallang_be.xlf', + 'extensionName' => 'PxaSocialFeed', + 'controllerActions' => [ + AdministrationController::class => [ + 'index', + 'editToken', + 'updateToken', + 'resetAccess', + 'deleteToken', + 'editConfiguration', + 'updateConfiguration', + 'deleteConfiguration', + 'runConfiguration', + ], + ], + 'routes' => [ + '_default' => [ + 'target' => AdministrationController::class . '::index', + ], + 'editConfiguration' => [ + 'path' => '/Administration/editConfiguration', + 'target' => AdministrationController::class . '::editConfiguration', + ], + 'editToken' => [ + 'path' => '/Administration/editToken', + 'target' => AdministrationController::class . '::editToken', + ], + ], + ], +]; diff --git a/Configuration/Extbase/Persistence/Classes.php b/Configuration/Extbase/Persistence/Classes.php index ec122d5b..2c576c6a 100644 --- a/Configuration/Extbase/Persistence/Classes.php +++ b/Configuration/Extbase/Persistence/Classes.php @@ -6,4 +6,7 @@ \Pixelant\PxaSocialFeed\Domain\Model\FileReference::class => [ 'tableName' => 'sys_file_reference', ], + \Pixelant\PxaSocialFeed\Domain\Model\BackendUserGroup::class => [ + 'tableName' => 'be_groups', + ], ]; diff --git a/Configuration/Icons.php b/Configuration/Icons.php new file mode 100644 index 00000000..3b0eba25 --- /dev/null +++ b/Configuration/Icons.php @@ -0,0 +1,32 @@ + [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/feed.svg', + ], + 'ext-pxasocialfeed-model-icon' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/feed.svg', + ], + 'ext-pxasocialfeed-model-icon-facebook' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/facebook.svg', + ], + 'ext-pxasocialfeed-model-icon-instagram' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/instagram.svg', + ], + 'ext-pxasocialfeed-model-icon-twitter' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/twitter.svg', + ], + 'ext-pxasocialfeed-model-icon-youtube' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/youtube.svg', + ], +]; diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php new file mode 100644 index 00000000..ef525ad2 --- /dev/null +++ b/Configuration/JavaScriptModules.php @@ -0,0 +1,8 @@ + [ 'backend' ], + 'imports' => [ + '@pixelant/pxa-social-feed/' => 'EXT:pxa_social_feed/Resources/Public/JavaScript/Backend/', + ], +]; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 6bbcde3f..ca66c4c1 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -1,4 +1,3 @@ -# Configuration/Services.yaml services: _defaults: autowire: true @@ -9,4 +8,5 @@ services: resource: '../Classes/*' exclude: - '../Classes/Domain/Model/*' - - '../Classes/Controller/AdministrationController.php' + Pixelant\PxaSocialFeed\Controller\AdministrationController: + tags: ['backend.controller'] diff --git a/Configuration/TCA/Overrides/sys_template.php b/Configuration/TCA/Overrides/sys_template.php index e3c525b1..e36bf3ab 100644 --- a/Configuration/TCA/Overrides/sys_template.php +++ b/Configuration/TCA/Overrides/sys_template.php @@ -1,6 +1,6 @@ 'name', 'tstamp' => 'tstamp', 'crdate' => 'crdate', - 'cruser_id' => 'cruser_id', 'default_sortby' => 'crdate DESC', 'delete' => 'deleted', @@ -27,9 +26,6 @@ 'rootLevel' => 1, ], - 'interface' => [ - 'showRecordFieldList' => 'hidden, name, social_id, end_point_entry, token, max_items, storage, be_group', - ], 'types' => [ '1' => ['showitem' => '--palette--;;1, name, social_id, end_point_entry, max_items, storage' . $accessTab], ], @@ -50,7 +46,8 @@ 'config' => [ 'type' => 'input', 'size' => 30, - 'eval' => 'trim,required', + 'eval' => 'trim', + 'required' => true, ], ], 'image_size' => [ @@ -59,7 +56,8 @@ 'config' => [ 'type' => 'input', 'size' => 30, - 'eval' => 'trim,required', + 'eval' => 'trim', + 'required' => true, ], ], 'social_id' => [ @@ -68,7 +66,8 @@ 'config' => [ 'type' => 'input', 'size' => 30, - 'eval' => 'trim,required', + 'eval' => 'trim', + 'required' => true, ], ], 'end_point_entry' => [ @@ -101,7 +100,8 @@ 'label' => $ll . '.storage', 'config' => [ 'type' => 'input', - 'eval' => 'int,required', + 'eval' => 'int', + 'required' => true, ], ], 'token' => [ @@ -126,9 +126,11 @@ 'maxitems' => 20, 'foreign_table' => 'be_groups', 'foreign_table_where' => 'ORDER BY be_groups.title', - 'enableMultiSelectFilterTextfield' => true, ], ], ], + 'security' => [ + 'ignorePageTypeRestriction' => true, + ], ]; })(); diff --git a/Configuration/TCA/tx_pxasocialfeed_domain_model_feed.php b/Configuration/TCA/tx_pxasocialfeed_domain_model_feed.php index 37f6c9cf..4382d926 100644 --- a/Configuration/TCA/tx_pxasocialfeed_domain_model_feed.php +++ b/Configuration/TCA/tx_pxasocialfeed_domain_model_feed.php @@ -1,6 +1,6 @@ 'message,post_url', 'tstamp' => 'tstamp', 'crdate' => 'crdate', - 'cruser_id' => 'cruser_id', 'default_sortby' => 'crdate DESC', 'type' => 'type', @@ -31,10 +30,6 @@ ], 'searchFields' => 'post_url,message,image,title,config,', ], - // @codingStandardsIgnoreStart - 'interface' => [ - 'showRecordFieldList' => 'hidden, post_date, post_url, message, media_type, likes, title, configuration, update_date, external_identifier, type', - ], 'types' => [ '0' => ['showitem' => '--palette--;;core, --palette--;;main,--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.media, fal_media,fal_related_files'], '1' => ['showitem' => '--palette--;;core, --palette--;;main,--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.media, fal_media,fal_related_files'], @@ -62,10 +57,10 @@ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => [ - [$ll . 'tx_pxasocialfeed_domain_model_feeds.type.1', 1, 'ext-pxasocialfeed-model-icon-facebook'], - [$ll . 'tx_pxasocialfeed_domain_model_feeds.type.2', 2, 'ext-pxasocialfeed-model-icon-instagram'], - [$ll . 'tx_pxasocialfeed_domain_model_feeds.type.3', 3, 'ext-pxasocialfeed-model-icon-twitter'], - [$ll . 'tx_pxasocialfeed_domain_model_feeds.type.4', 4, 'ext-pxasocialfeed-model-icon-youtube'], + ['label' => $ll . 'tx_pxasocialfeed_domain_model_feeds.type.1', 'value' => 1, 'icon' => 'ext-pxasocialfeed-model-icon-facebook'], + ['label' => $ll . 'tx_pxasocialfeed_domain_model_feeds.type.2', 'value' => 2, 'icon' => 'ext-pxasocialfeed-model-icon-instagram'], + ['label' => $ll . 'tx_pxasocialfeed_domain_model_feeds.type.3', 'value' => 3, 'icon' => 'ext-pxasocialfeed-model-icon-twitter'], + ['label' => $ll . 'tx_pxasocialfeed_domain_model_feeds.type.4', 'value' => 4, 'icon' => 'ext-pxasocialfeed-model-icon-youtube'], ], 'fieldWizard' => [ 'selectIcons' => [ @@ -128,28 +123,27 @@ 'exclude' => true, 'label' => $ll . 'tx_pxasocialfeed_domain_model_feeds.fal_media', 'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig( - 'fal_media', - [ - 'appearance' => [ - 'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference', - ], - 'foreign_match_fields' => [ - 'fieldname' => 'fal_media', - 'tablenames' => 'tx_pxasocialfeed_domain_model_feed', - 'table_local' => 'sys_file', - ], - 'overrideChildTca' => [ - 'types' => [ - \TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:lang/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, + 'fal_media', + [ + 'appearance' => [ + 'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference', + ], + 'foreign_match_fields' => [ + 'fieldname' => 'fal_media', + 'tablenames' => 'tx_pxasocialfeed_domain_model_feed', + ], + 'overrideChildTca' => [ + 'types' => [ + \TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [ + 'showitem' => ' + --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, --palette--;;filePalette ', + ], ], ], ], - ], - 'jpg,jpeg,png,gif,svg' + 'jpg,jpeg,png,gif,svg' ), ], 'likes' => [ @@ -216,5 +210,8 @@ ], ], ], + 'security' => [ + 'ignorePageTypeRestriction' => true, + ], ]; })(); diff --git a/Configuration/TCA/tx_pxasocialfeed_domain_model_token.php b/Configuration/TCA/tx_pxasocialfeed_domain_model_token.php index 1d9b5850..4a51ba14 100644 --- a/Configuration/TCA/tx_pxasocialfeed_domain_model_token.php +++ b/Configuration/TCA/tx_pxasocialfeed_domain_model_token.php @@ -1,6 +1,6 @@ 'uid', 'tstamp' => 'tstamp', 'crdate' => 'crdate', - 'cruser_id' => 'cruser_id', 'default_sortby' => 'crdate DESC', 'delete' => 'deleted', @@ -29,9 +28,6 @@ 'type' => 'type', 'rootLevel' => 1, ], - 'interface' => [ - 'showRecordFieldList' => 'hidden, name, type, app_id, app_secret, be_group', - ], 'types' => [ \Pixelant\PxaSocialFeed\Domain\Model\Token::FACEBOOK => ['showitem' => 'name, type, --palette--;;paletteGraphApi' . $accessTab], \Pixelant\PxaSocialFeed\Domain\Model\Token::INSTAGRAM => ['showitem' => 'name, type, --palette--;;paletteGraphApi' . $accessTab], @@ -80,11 +76,11 @@ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => [ - [$ll . '.type.type.1', \Pixelant\PxaSocialFeed\Domain\Model\Token::FACEBOOK], - [$ll . '.type.type.2', \Pixelant\PxaSocialFeed\Domain\Model\Token::INSTAGRAM], - [$ll . '.type.type.3', \Pixelant\PxaSocialFeed\Domain\Model\Token::TWITTER], - [$ll . '.type.type.6', \Pixelant\PxaSocialFeed\Domain\Model\Token::TWITTER_V2], - [$ll . '.type.type.4', \Pixelant\PxaSocialFeed\Domain\Model\Token::YOUTUBE], + ['label' => $ll . '.type.type.1', 'value' => \Pixelant\PxaSocialFeed\Domain\Model\Token::FACEBOOK], + ['label' => $ll . '.type.type.2', 'value' => \Pixelant\PxaSocialFeed\Domain\Model\Token::INSTAGRAM], + ['label' => $ll . '.type.type.3', 'value' => \Pixelant\PxaSocialFeed\Domain\Model\Token::TWITTER], + ['label' => $ll . '.type.type.6', 'value' => \Pixelant\PxaSocialFeed\Domain\Model\Token::TWITTER_V2], + ['label' => $ll . '.type.type.4', 'value' => \Pixelant\PxaSocialFeed\Domain\Model\Token::YOUTUBE], ], ], ], @@ -105,7 +101,7 @@ 'renderType' => 'selectSingle', 'default' => 0, 'items' => [ - ['', 0], + ['label' => '', 'value' => 0], ], 'foreign_table' => 'tx_pxasocialfeed_domain_model_token', 'foreign_table_where' => 'AND {#tx_pxasocialfeed_domain_model_token}.{#pid}=###CURRENT_PID### AND {#tx_pxasocialfeed_domain_model_token}.{#sys_language_uid} IN (-1,0)', @@ -124,7 +120,8 @@ 'label' => $ll . '.app_id', 'config' => [ 'type' => 'input', - 'eval' => 'required,trim', + 'eval' => 'trim', + 'required' => true, ], ], 'app_secret' => [ @@ -132,7 +129,8 @@ 'label' => $ll . '.app_secret', 'config' => [ 'type' => 'input', - 'eval' => 'required,trim', + 'eval' => 'trim', + 'required' => true, ], ], 'access_token' => [ @@ -148,7 +146,8 @@ 'label' => $ll . '.api_key', 'config' => [ 'type' => 'input', - 'eval' => 'trim,required', + 'eval' => 'trim', + 'required' => true, ], ], 'api_secret_key' => [ @@ -156,7 +155,8 @@ 'label' => $ll . '.api_secret_key', 'config' => [ 'type' => 'input', - 'eval' => 'trim,required', + 'eval' => 'trim', + 'required' => true, ], ], 'access_token_secret' => [ @@ -164,7 +164,8 @@ 'label' => $ll . '.access_token_secret', 'config' => [ 'type' => 'input', - 'eval' => 'trim,required', + 'eval' => 'trim', + 'required' => true, ], ], 'bearer_token' => [ @@ -172,8 +173,9 @@ 'label' => $ll . '.bearer_token', 'config' => [ 'type' => 'input', - 'eval' => 'trim,required' - ] + 'eval' => 'trim', + 'required' => true, + ], ], 'be_group' => [ 'exclude' => true, @@ -186,9 +188,11 @@ 'maxitems' => 20, 'foreign_table' => 'be_groups', 'foreign_table_where' => 'ORDER BY be_groups.title', - 'enableMultiSelectFilterTextfield' => true, ], ], ], + 'security' => [ + 'ignorePageTypeRestriction' => true, + ], ]; })(); diff --git a/Resources/Private/Partials/Backend/ListConfigurations.html b/Resources/Private/Partials/Backend/ListConfigurations.html index 5b6a5a25..d8b61896 100644 --- a/Resources/Private/Partials/Backend/ListConfigurations.html +++ b/Resources/Private/Partials/Backend/ListConfigurations.html @@ -33,7 +33,7 @@ - + @@ -45,10 +45,10 @@ - + - + diff --git a/Resources/Private/Partials/Backend/ListTokens.html b/Resources/Private/Partials/Backend/ListTokens.html index 28e78914..5c766a9d 100644 --- a/Resources/Private/Partials/Backend/ListTokens.html +++ b/Resources/Private/Partials/Backend/ListTokens.html @@ -20,11 +20,11 @@
- + - +
diff --git a/Resources/Private/Partials/Backend/TokenInfo/FacebookInstagram.html b/Resources/Private/Partials/Backend/TokenInfo/FacebookInstagram.html index 4dff0ac5..d83e09c3 100644 --- a/Resources/Private/Partials/Backend/TokenInfo/FacebookInstagram.html +++ b/Resources/Private/Partials/Backend/TokenInfo/FacebookInstagram.html @@ -13,10 +13,12 @@

- +

diff --git a/Resources/Private/Templates/Administration/EditToken.html b/Resources/Private/Templates/Administration/EditToken.html index 43f04464..7e15d774 100644 --- a/Resources/Private/Templates/Administration/EditToken.html +++ b/Resources/Private/Templates/Administration/EditToken.html @@ -13,7 +13,7 @@

- + @@ -52,4 +52,4 @@

- \ No newline at end of file + diff --git a/Resources/Public/Icons/Extension.gif b/Resources/Public/Icons/Extension.gif new file mode 100644 index 00000000..a555b3ec Binary files /dev/null and b/Resources/Public/Icons/Extension.gif differ diff --git a/Resources/Public/Icons/Extension.svg b/Resources/Public/Icons/Extension.svg new file mode 100644 index 00000000..554d6822 --- /dev/null +++ b/Resources/Public/Icons/Extension.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Public/Icons/ext_icon.svg b/Resources/Public/Icons/ext_icon.svg new file mode 100644 index 00000000..554d6822 --- /dev/null +++ b/Resources/Public/Icons/ext_icon.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Public/JavaScript/Backend/SocialFeedModule.js b/Resources/Public/JavaScript/Backend/SocialFeedModule.js index 7ad5dc6c..5ba7c6cb 100644 --- a/Resources/Public/JavaScript/Backend/SocialFeedModule.js +++ b/Resources/Public/JavaScript/Backend/SocialFeedModule.js @@ -8,277 +8,276 @@ define([ 'clipboard', ], function ($, bootstrap, Modal, Severity, Notification, MessageUtility, clipboard) { return (function ($, bootstrap, Modal, Severity, Notification, MessageUtility, clipboard) { - /** - * @private - * - * Hold the instance (Singleton Pattern) - */ - var _socialFeedModuleInstance = null; - - /** - * Main module JavaScript - * - * @return {{init: init}} - * @constructor - */ - function SocialFeedModule(settings) { - - /** - * If was initialized - * - * @type {boolean} - * @private - */ - var _isRunning = false; - - /** - * HTML Identifiers - * @type {{}} - * @private - */ - var _domElementsSelectors = { - confirmationButton: '.delete-action,.confirmation-action', - selectSocialType: '#select-type', - socialTypeUrlKeep: '#type-url-', - winStorageBrowser: '[data-identifier="browse-feeds-storage"]', - feedsStorageInput: '[data-identifier="feeds-storage-input"]', - feedsStorageTitle: '[data-identifier="feed-storage-title"]', - copyRedirectUriButton: '.copy-redirect-uri-button', - facebookLoginButton: '.facebook-login-link' - }; - - /** - * run if not running yet - * - * @public - */ - function run() { - if (_isRunning === false) { - _bootstrap(); - } - - _isRunning = true; - } - - /** - * Init - * @private - */ - function _bootstrap() { - _deleteConfirmation(); - _facebookLoginWindow(); - _changeSocialType(); - _winStorageBrowser(); - _getRedirectUriButtonClick(); - _initToolTip(); - _activateTabs(); - } - - /** - * Activate the tabs for the backend module - * - * @private - */ - function _activateTabs() { - let triggerTabList = [].slice.call(document.querySelectorAll('#tabs a')) - triggerTabList.forEach(function (triggerEl) { - let tabTrigger = new bootstrap.Tab(triggerEl) - - triggerEl.addEventListener('click', function (event) { - event.preventDefault() - tabTrigger.show() - }) - }); - } - - /** - * If user try to delete something - * - * @private - */ - function _deleteConfirmation() { - $(_getDomElementIdentifier('confirmationButton')).on('click', function (e) { - e.preventDefault(); - - var $this = $(this); - var title = $this.data('confirmation-title') || 'Delete'; - var message = $this.data('confirmation-message') || 'Are you sure you want to delete this record ?'; - - var url = $this.attr('href'), - modal = Modal.confirm(title, message, Severity.warning); - - modal.on('confirm.button.cancel', function () { - Modal.dismiss(modal); - }); - - modal.on('confirm.button.ok', function () { - Modal.dismiss(modal); - window.location.href = url; - }); - }) - } - - /** - * Show window with facebook login - * - * @private - */ - function _facebookLoginWindow() { - $(_getDomElementIdentifier('facebookLoginButton')).on('click', function (e) { - e.preventDefault(); - - var $this = $(this); - var w = 800; - var h = 800; - - var y = window.top.outerHeight / 2 + window.top.screenY - (h / 2); - var x = window.top.outerWidth / 2 + window.top.screenX - (w / 2); - - window.open($this.attr('href'), 'Facebook login', 'height=' + h + ',width=' + w + 'top=' + y + ', left=' + x); - }); - } - - /** - * Switch to different social type - * - * @private - */ - function _changeSocialType() { - $(_getDomElementIdentifier('selectSocialType')).on('change', function () { - var selectSocialType = $(this).find(':selected').val(); - - window.location.href = $(_getDomElementIdentifier('socialTypeUrlKeep') + selectSocialType).val(); - }); - } - - /** - * Copy redirect uri to clipboard - * @private - */ - function _getRedirectUriButtonClick() { - new clipboard(_getDomElementIdentifier('copyRedirectUriButton')); - } - - /** - * Load browser pages window - * - * @private - */ - function _winStorageBrowser() { - window.addEventListener('message', function (e) { - if (!MessageUtility.MessageUtility.verifyOrigin(e.origin)) { - throw 'Denied message sent by ' + e.origin; - } - - if (typeof e.data.fieldName === 'undefined') { - throw 'fieldName not defined in message'; - } - - if (typeof e.data.value === 'undefined') { - throw 'value not defined in message'; - } - - const fieldElement = _getInsertTarget(e.data.fieldName); - if (fieldElement) { - fieldElement.value = e.data.value; - } - - const storageTitleElement = document.querySelector(_getDomElementIdentifier('feedsStorageTitle')); - if (storageTitleElement) { - storageTitleElement.innerHTML = e.data.label; - } - }); - - $(_getDomElementIdentifier('winStorageBrowser')).on('click', function () { - var insertTarget = $(_getDomElementIdentifier('feedsStorageInput')), - randomIdentifier = Math.floor(Math.random() * 100000 + 1); - - insertTarget.attr('data-insert-target', randomIdentifier); - _openTypo3WinBrowser('db', randomIdentifier + '|||pages'); - }); - } - - /** - * @private - * - * opens a popup window with the element browser - * - * @param mode - * @param params - */ - function _openTypo3WinBrowser(mode, params) { - const url = _getSetting('browserUrl') + '&mode=' + mode + '&bparams=' + params; - Modal.advanced({ - type: Modal.types.iframe, - content: url, - size: Modal.sizes.large, - }); - } - - /** - * Get selector - * @param elementIdentifier - * @return {*|undefined} - * @private - */ - function _getDomElementIdentifier(elementIdentifier) { - return _domElementsSelectors[elementIdentifier] || undefined; - } - - /** - * Get insert target - * @param reference - * @return {HTMLElement|null} - * @private - */ - function _getInsertTarget(reference) { - return document.querySelector('[data-insert-target="' + reference + '"]'); - } - - /** - * Get settings - * @param key - * @return {*|undefined} - * @private - */ - function _getSetting(key) { - return settings[key] || undefined; - } - - /** - * Tool tip - * @private - */ - function _initToolTip() { - $(function () { - $('[data-toggle="tooltip"]').tooltip(); - }); - } - - /** - * return public methods - */ - return { - run: run - } - } - - return { - /** - * @public - * @static - * - * Implement the "Singleton Pattern". - * - * @return object - */ - getInstance: function (settings) { - if (_socialFeedModuleInstance === null) { - _socialFeedModuleInstance = new SocialFeedModule(settings); - } - - return _socialFeedModuleInstance; - }, - }; - })($, bootstrap, Modal, Severity, Notification, MessageUtility, clipboard); + /** + * @private + * + * Hold the instance (Singleton Pattern) + */ + var _socialFeedModuleInstance = null; + + /** + * Main module JavaScript + * + * @return {{init: init}} + * @constructor + */ + function SocialFeedModule(settings) { + /** + * If was initialized + * + * @type {boolean} + * @private + */ + var _isRunning = false; + + /** + * HTML Identifiers + * @type {{}} + * @private + */ + var _domElementsSelectors = { + confirmationButton: '.delete-action,.confirmation-action', + selectSocialType: '#select-type', + socialTypeUrlKeep: '#type-url-', + winStorageBrowser: '[data-identifier="browse-feeds-storage"]', + feedsStorageInput: '[data-identifier="feeds-storage-input"]', + feedsStorageTitle: '[data-identifier="feed-storage-title"]', + copyRedirectUriButton: '.copy-redirect-uri-button', + facebookLoginButton: '.facebook-login-link', + }; + + /** + * run if not running yet + * + * @public + */ + function run() { + if (_isRunning === false) { + _bootstrap(); + } + + _isRunning = true; + } + + /** + * Init + * @private + */ + function _bootstrap() { + _deleteConfirmation(); + _facebookLoginWindow(); + _changeSocialType(); + _winStorageBrowser(); + _getRedirectUriButtonClick(); + _initToolTip(); + _activateTabs(); + } + + /** + * Activate the tabs for the backend module + * + * @private + */ + function _activateTabs() { + let triggerTabList = [].slice.call(document.querySelectorAll('#tabs a')); + triggerTabList.forEach(function (triggerEl) { + let tabTrigger = new bootstrap.Tab(triggerEl); + + triggerEl.addEventListener('click', function (event) { + event.preventDefault(); + tabTrigger.show(); + }); + }); + } + + /** + * If user try to delete something + * + * @private + */ + function _deleteConfirmation() { + $(_getDomElementIdentifier('confirmationButton')).on('click', function (e) { + e.preventDefault(); + + var $this = $(this); + var title = $this.data('confirmation-title') || 'Delete'; + var message = $this.data('confirmation-message') || 'Are you sure you want to delete this record ?'; + + var url = $this.attr('href'), + modal = Modal.confirm(title, message, Severity.warning); + + modal.addEventListener('confirm.button.cancel', function () { + Modal.dismiss(modal); + }); + + modal.addEventListener('confirm.button.ok', function () { + Modal.dismiss(modal); + window.location.href = url; + }); + }); + } + + /** + * Show window with facebook login + * + * @private + */ + function _facebookLoginWindow() { + $(_getDomElementIdentifier('facebookLoginButton')).on('click', function (e) { + e.preventDefault(); + + var $this = $(this); + var w = 800; + var h = 800; + + var y = window.top.outerHeight / 2 + window.top.screenY - h / 2; + var x = window.top.outerWidth / 2 + window.top.screenX - w / 2; + + window.open($this.attr('href'), 'Facebook login', 'height=' + h + ',width=' + w + 'top=' + y + ', left=' + x); + }); + } + + /** + * Switch to different social type + * + * @private + */ + function _changeSocialType() { + $(_getDomElementIdentifier('selectSocialType')).on('change', function () { + var selectSocialType = $(this).find(':selected').val(); + + window.location.href = $(_getDomElementIdentifier('socialTypeUrlKeep') + selectSocialType).val(); + }); + } + + /** + * Copy redirect uri to clipboard + * @private + */ + function _getRedirectUriButtonClick() { + new clipboard(_getDomElementIdentifier('copyRedirectUriButton')); + } + + /** + * Load browser pages window + * + * @private + */ + function _winStorageBrowser() { + window.addEventListener('message', function (e) { + if (!MessageUtility.MessageUtility.verifyOrigin(e.origin)) { + throw 'Denied message sent by ' + e.origin; + } + + if (typeof e.data.fieldName === 'undefined') { + throw 'fieldName not defined in message'; + } + + if (typeof e.data.value === 'undefined') { + throw 'value not defined in message'; + } + + const fieldElement = _getInsertTarget(e.data.fieldName); + if (fieldElement) { + fieldElement.value = e.data.value.split('_').pop(); + } + + const storageTitleElement = document.querySelector(_getDomElementIdentifier('feedsStorageTitle')); + if (storageTitleElement) { + storageTitleElement.innerHTML = e.data.label; + } + }); + + $(_getDomElementIdentifier('winStorageBrowser')).on('click', function () { + var insertTarget = $(_getDomElementIdentifier('feedsStorageInput')), + randomIdentifier = Math.floor(Math.random() * 100000 + 1); + + insertTarget.attr('data-insert-target', randomIdentifier); + _openTypo3WinBrowser('db', randomIdentifier + '|||pages'); + }); + } + + /** + * @private + * + * opens a popup window with the element browser + * + * @param mode + * @param params + */ + function _openTypo3WinBrowser(mode, params) { + const url = _getSetting('browserUrl') + '&mode=' + mode + '&bparams=' + params; + Modal.advanced({ + type: Modal.types.iframe, + content: url, + size: Modal.sizes.large, + }); + } + + /** + * Get selector + * @param elementIdentifier + * @return {*|undefined} + * @private + */ + function _getDomElementIdentifier(elementIdentifier) { + return _domElementsSelectors[elementIdentifier] || undefined; + } + + /** + * Get insert target + * @param reference + * @return {HTMLElement|null} + * @private + */ + function _getInsertTarget(reference) { + return document.querySelector('[data-insert-target="' + reference + '"]'); + } + + /** + * Get settings + * @param key + * @return {*|undefined} + * @private + */ + function _getSetting(key) { + return settings[key] || undefined; + } + + /** + * Tool tip + * @private + */ + function _initToolTip() { + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + }); + } + + /** + * return public methods + */ + return { + run: run, + }; + } + + return { + /** + * @public + * @static + * + * Implement the "Singleton Pattern". + * + * @return object + */ + getInstance: function (settings) { + if (_socialFeedModuleInstance === null) { + _socialFeedModuleInstance = new SocialFeedModule(settings); + } + + return _socialFeedModuleInstance; + }, + }; + })($, bootstrap, Modal, Severity, Notification, MessageUtility, clipboard); }); diff --git a/Resources/Public/JavaScript/Backend/social-feed-administration-module.js b/Resources/Public/JavaScript/Backend/social-feed-administration-module.js new file mode 100644 index 00000000..1c5c335a --- /dev/null +++ b/Resources/Public/JavaScript/Backend/social-feed-administration-module.js @@ -0,0 +1,218 @@ +import DocumentService from '@typo3/core/document-service.js'; +import Notification from '@typo3/backend/notification.js'; +import Modal from '@typo3/backend/modal.js'; +import $ from 'jquery'; +import Severity from '@typo3/backend/severity.js'; +import bootstrap from '@typo3/backend/bootstrap'; +/* + * file created by Abdellatif Landolsi for TYPO3 v12.4 + * Date: 2023-10-19 + */ + +class SocialFeedAdministrationModule { + constructor() { + this._isRunning = false; + this._domElementsSelectors = { + confirmationButton: '.delete-action,.confirmation-action', + selectSocialType: '#select-type', + socialTypeUrlKeep: '#type-url-', + winStorageBrowser: '[data-identifier="browse-feeds-storage"]', + feedsStorageInput: '[data-identifier="feeds-storage-input"]', + feedsStorageTitle: '[data-identifier="feed-storage-title"]', + copyRedirectUriButton: '.copy-redirect-uri-button', + facebookLoginButton: '.facebook-login-link', + }; + DocumentService.ready().then(() => { + this.initialize(); + }); + } + initialize() { + if (this._isRunning === false) { + this._bootstrap(); + } + _isRunning = true; + } + + _bootstrap() { + this._deleteConfirmation(); + this._facebookLoginWindow(); + this._changeSocialType(); + this._winStorageBrowser(); + this._getRedirectUriButtonClick(); + this._initToolTip(); + this._activateTabs(); + } + + /** + * Activate the tabs for the backend module + * + * @private + */ + _activateTabs() { + let triggerTabList = [].slice.call(document.querySelectorAll('#tabs a')); + triggerTabList.forEach(function (triggerEl) { + let tabTrigger = new bootstrap.Tab(triggerEl); + + triggerEl.addEventListener('click', function (event) { + event.preventDefault(); + tabTrigger.show(); + }); + }); + } + + /** + * If user try to delete something + * + * @private + */ + _deleteConfirmation() { + this._getDomElementIdentifier('confirmationButton').on('click', function (e) { + e.preventDefault(); + + var $this = $(this); + var title = $this.data('confirmation-title') || 'Delete'; + var message = $this.data('confirmation-message') || 'Are you sure you want to delete this record ?'; + + var url = $this.attr('href'), + modal = Modal.confirm(title, message, Severity.warning); + + modal.on('confirm.button.cancel', function () { + Modal.dismiss(modal); + }); + + modal.on('confirm.button.ok', function () { + Modal.dismiss(modal); + window.location.href = url; + }); + }); + } + + /** + * Show window with facebook login + * + * @private + */ + _facebookLoginWindow() { + this._getDomElementIdentifier('facebookLoginButton').on('click', function (e) { + e.preventDefault(); + + var $this = $(this); + var w = 800; + var h = 800; + + var y = window.top.outerHeight / 2 + window.top.screenY - h / 2; + var x = window.top.outerWidth / 2 + window.top.screenX - w / 2; + + window.open($this.attr('href'), 'Facebook login', 'height=' + h + ',width=' + w + 'top=' + y + ', left=' + x); + }); + } + + /** + * Switch to different social type + * + * @private + */ + _changeSocialType() { + this._getDomElementIdentifier('selectSocialType').on('change', function () { + var selectSocialType = $(this).find(':selected').val(); + + window.location.href = $(_getDomElementIdentifier('socialTypeUrlKeep') + selectSocialType).val(); + }); + } + + /** + * Copy redirect uri to clipboard + * @private + */ + _getRedirectUriButtonClick() { + new clipboard(this._getDomElementIdentifier('copyRedirectUriButton')); + } + + /** + * Load browser pages window + * + * @private + */ + _winStorageBrowser() { + window.addEventListener('message', function (e) { + if (!MessageUtility.MessageUtility.verifyOrigin(e.origin)) { + throw 'Denied message sent by ' + e.origin; + } + + if (typeof e.data.fieldName === 'undefined') { + throw 'fieldName not defined in message'; + } + + if (typeof e.data.value === 'undefined') { + throw 'value not defined in message'; + } + + const fieldElement = this._getInsertTarget(e.data.fieldName); + if (fieldElement) { + fieldElement.value = e.data.value; + } + + const storageTitleElement = document.querySelector(this._getDomElementIdentifier('feedsStorageTitle')); + if (storageTitleElement) { + storageTitleElement.innerHTML = e.data.label; + } + }); + + this._getDomElementIdentifier('winStorageBrowser').on('click', function () { + var insertTarget = this._getDomElementIdentifier('feedsStorageInput'), + randomIdentifier = Math.floor(Math.random() * 100000 + 1); + + insertTarget.attr('data-insert-target', randomIdentifier); + this._openTypo3WinBrowser('db', randomIdentifier + '|||pages'); + }); + } + + /** + * @private + * + * opens a popup window with the element browser + * + * @param mode + * @param params + */ + _openTypo3WinBrowser(mode, params) { + const url = _getSetting('browserUrl') + '&mode=' + mode + '&bparams=' + params; + Modal.advanced({ + type: Modal.types.iframe, + content: url, + size: Modal.sizes.large, + }); + } + + /** + * Get selector + * @param elementIdentifier + * @return {*|undefined} + * @private + */ + _getDomElementIdentifier(elementIdentifier) { + return _domElementsSelectors[elementIdentifier] || undefined; + } + + /** + * Get insert target + * @param reference + * @return {HTMLElement|null} + * @private + */ + _getInsertTarget(reference) { + return document.querySelector('[data-insert-target="' + reference + '"]'); + } + + /** + * Get settings + * @param key + * @return {*|undefined} + * @private + */ + _getSetting(key) { + return settings[key] || undefined; + } +} + +export default new SocialFeedAdministrationModule(); diff --git a/composer.json b/composer.json index 7e40ab7f..b6738136 100644 --- a/composer.json +++ b/composer.json @@ -17,9 +17,9 @@ } ], "require": { - "php": ">=7.4", - "typo3/cms-core": "^10.3 || ^11.5", - "typo3/cms-scheduler": "^10.3 || ^11.5", + "php": ">=8.1", + "typo3/cms-core": "^11.5 || ^12.4", + "typo3/cms-scheduler": "^11.5 || ^12.4", "league/oauth2-facebook": "2.0.5" }, "require-dev": { diff --git a/ext_emconf.php b/ext_emconf.php index 52bdee4a..a129ef19 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -12,17 +12,17 @@ 'author_email' => 'info@pixelant.net', 'author_company' => 'Pixelant', 'state' => 'stable', + 'version' => '4.0.0', 'uploadfolder' => '0', 'createDirs' => '', 'clearCacheOnLoad' => 0, - 'version' => '3.7.2', 'constraints' => [ 'depends' => [ - 'typo3' => '10.3.0-11.5.99', + 'typo3' => '11.5.0-12.4.99', ], 'conflicts' => [ ], 'suggests' => [ ], ], -]; +]; \ No newline at end of file diff --git a/ext_localconf.php b/ext_localconf.php index d41b46ba..f295d417 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,10 +1,8 @@ $_EXTKEY, + 'extension' => 'pxa_social_feed', 'title' => $ll . 'task.import.name', 'description' => $ll . 'task.import.description', 'additionalFields' => \Pixelant\PxaSocialFeed\Task\ImportTaskAdditionalFieldProvider::class, ]; // hook for extension BE view - $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info']['pxasocialfeed_showfeed'][$_EXTKEY] = + $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info']['pxasocialfeed_showfeed']['pxa_social_feed'] = \Pixelant\PxaSocialFeed\Hooks\PageLayoutView::class . '->getExtensionInformation'; // @codingStandardsIgnoreEnd @@ -37,28 +35,6 @@ "@import 'EXT:pxa_social_feed/Configuration/TSconfig/ContentElementWizard.tsconfig'" ); - // Register icons - $icons = [ - 'ext-pxasocialfeed-wizard-icon' => 'feed.svg', - 'ext-pxasocialfeed-model-icon' => 'feed.svg', - 'ext-pxasocialfeed-model-icon-facebook' => 'facebook.svg', - 'ext-pxasocialfeed-model-icon-instagram' => 'instagram.svg', - 'ext-pxasocialfeed-model-icon-twitter' => 'twitter.svg', - 'ext-pxasocialfeed-model-icon-youtube' => 'youtube.svg', - ]; - /** @var \TYPO3\CMS\Core\Imaging\IconRegistry $iconRegistry */ - $iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance( - \TYPO3\CMS\Core\Imaging\IconRegistry::class - ); - - foreach ($icons as $identifier => $path) { - $iconRegistry->registerIcon( - $identifier, - \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, - ['source' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/' . $path] - ); - } - // Register eID to obtain access token $eID = \Pixelant\PxaSocialFeed\Controller\EidController::IDENTIFIER; $GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include'][$eID] = diff --git a/ext_tables.php b/ext_tables.php index 8ad0e219..871e1f78 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -1,33 +1,3 @@ - 'index, editToken, updateToken, deleteToken, editConfiguration, ' . - 'updateConfiguration, deleteConfiguration, resetAccessToken, runConfiguration', - ], - [ - 'access' => 'user,group', - 'icon' => 'EXT:pxa_social_feed/Resources/Public/Icons/BE/feed.svg', - 'labels' => 'LLL:EXT:pxa_social_feed/Resources/Private/Language/locallang_be.xlf', - ] - ); - } - - foreach (['feed', 'token', 'configuration'] as $table) { - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::allowTableOnStandardPages( - 'tx_pxasocialfeed_domain_model_' . $table - ); - } -})(); +defined('TYPO3') or die(); diff --git a/ext_tables.sql b/ext_tables.sql index 7519abb7..8b6db7b4 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -50,7 +50,7 @@ CREATE TABLE tx_pxasocialfeed_domain_model_token app_secret varchar(255) DEFAULT '' NOT NULL, access_token text, fb_social_id varchar(255) DEFAULT '' NOT NULL, - parent_token int(11), + parent_token int(11) DEFAULT '0' NOT NULL, # Twitter, access_token already exist api_key varchar(255) DEFAULT '' NOT NULL, diff --git a/ext_typoscript_setup.typoscript b/ext_typoscript_setup.typoscript index f84d7d4b..6fc4ae38 100644 --- a/ext_typoscript_setup.typoscript +++ b/ext_typoscript_setup.typoscript @@ -4,3 +4,4 @@ plugin.tx_pxasocialfeed { } } module.tx_pxasocialfeed < plugin.tx_pxasocialfeed +