Initial commit - Typo3 11.5.41

This commit is contained in:
Matteo Gallo
2026-07-03 17:53:31 +02:00
commit 5ca4743197
6811 changed files with 568848 additions and 0 deletions
@@ -0,0 +1,649 @@
<?php
namespace GeorgRinger\News\Controller;
use GeorgRinger\News\Backend\RecordList\NewsDatabaseRecordList;
use GeorgRinger\News\Domain\Model\Dto\AdministrationDemand;
use GeorgRinger\News\Domain\Repository\AdministrationRepository;
use GeorgRinger\News\Domain\Repository\CategoryRepository;
use GeorgRinger\News\Domain\Repository\NewsRepository;
use GeorgRinger\News\Domain\Repository\TagRepository;
use GeorgRinger\News\Event\AdministrationExtendMenuEvent;
use GeorgRinger\News\Event\AdministrationIndexActionEvent;
use GeorgRinger\News\Event\AdministrationNewsPidListingActionEvent;
use GeorgRinger\News\Utility\Page;
use TYPO3\CMS\Backend\Clipboard\Clipboard;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\Components\Menu\Menu;
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
use TYPO3\CMS\Backend\View\BackendTemplateView;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Administration controller
*/
class AdministrationController extends NewsController
{
/** @var int */
protected $pageUid = 0;
/** @var array */
protected $tsConfiguration = [];
/**
* @var \GeorgRinger\News\Domain\Repository\AdministrationRepository
*/
protected $administrationRepository;
/** @var ConfigurationManagerInterface */
protected $configurationManager;
/** @var array */
protected $pageInformation = [];
/** @var array */
protected $allowedNewTables = [];
/** @var array */
protected $deniedNewTables = [];
public function initializeAction()
{
$this->pageUid = (int)GeneralUtility::_GET('id');
$this->pageInformation = BackendUtilityCore::readPageAccess($this->pageUid, '');
$this->setTsConfig();
parent::initializeAction();
}
/** @var BackendTemplateView */
protected $view;
/** @var IconFactory */
protected $iconFactory;
/** @var string */
protected $defaultViewObjectName = BackendTemplateView::class;
/**
* AdministrationController constructor.
* @param NewsRepository $newsRepository
* @param CategoryRepository $categoryRepository
* @param TagRepository $tagRepository
* @param ConfigurationManagerInterface $configurationManager
* @param AdministrationRepository $administrationRepository
* @param AdministrationRepository $iconFactory
*/
public function __construct(
NewsRepository $newsRepository,
CategoryRepository $categoryRepository,
TagRepository $tagRepository,
ConfigurationManagerInterface $configurationManager,
AdministrationRepository $administrationRepository,
IconFactory $iconFactory
) {
parent::__construct($newsRepository, $categoryRepository, $tagRepository, $configurationManager);
$this->administrationRepository = $administrationRepository;
$this->iconFactory = $iconFactory;
}
/**
* @param ViewInterface $view
*/
protected function initializeView(ViewInterface $view)
{
parent::initializeView($view);
$view->getModuleTemplate()->getDocHeaderComponent()->setMetaInformation([]);
$pageRenderer = $this->view->getModuleTemplate()->getPageRenderer();
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DateTimePicker');
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/AjaxDataHandler');
$pageRenderer->loadRequireJsModule('TYPO3/CMS/News/AdministrationModule');
$pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf');
$dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] : ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY']);
$pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
$web_list_modTSconfig = BackendUtilityCore::getPagesTSconfig($this->pageUid)['mod.']['web_list.'] ?? [];
$this->allowedNewTables = GeneralUtility::trimExplode(
',',
$web_list_modTSconfig['allowedNewTables'] ?? '',
true
);
$this->deniedNewTables = GeneralUtility::trimExplode(
',',
$web_list_modTSconfig['deniedNewTables'] ?? '',
true
);
$this->createMenu();
$this->createButtons();
$view->assign('showSupportArea', $this->showSupportArea());
}
protected function createMenu(): void
{
$uriBuilder = $this->objectManager->get(UriBuilder::class);
$uriBuilder->setRequest($this->request);
$menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
$menu->setIdentifier('news');
$actions = [
['action' => 'index', 'label' => 'newsListing'],
['action' => 'newsPidListing', 'label' => 'newsPidListing'],
['action' => 'donate', 'label' => 'donate']
];
foreach ($actions as $action) {
$item = $menu->makeMenuItem()
->setTitle($this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:module.' . $action['label']))
->setHref($uriBuilder->uriFor($action['action'], [], 'Administration', 'News', 'web_NewsAdministration'))
->setActive($this->request->getControllerActionName() === $action['action']);
$menu->addMenuItem($item);
}
$menu = $this->extendMenu($menu);
if ($menu instanceof Menu) {
$this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
}
if (is_array($this->pageInformation)) {
$this->view->getModuleTemplate()->getDocHeaderComponent()->setMetaInformation($this->pageInformation);
}
}
/**
* Extends menu selector with items from 3rd party extensions.
*
* @param Menu $menu
* @return Menu
*/
protected function extendMenu(Menu $menu): Menu
{
$event = $this->eventDispatcher->dispatch(new AdministrationExtendMenuEvent($this, $menu));
return $event->getMenu();
}
protected function createButtons(): void
{
$buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar();
$uriBuilder = $this->objectManager->get(UriBuilder::class);
$uriBuilder->setRequest($this->request);
if ($this->request->getControllerActionName() === 'index' && $this->isFilteringEnabled()) {
$toggleButton = $buttonBar->makeLinkButton()
->setHref('#')
->setDataAttributes([
'togglelink' => '1',
'toggle' => 'tooltip',
'placement' => 'bottom',
])
->setTitle($this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:administration.toggleForm'))
->setIcon($this->iconFactory->getIcon('actions-filter', Icon::SIZE_SMALL));
$buttonBar->addButton($toggleButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
}
$buttons = [
[
'table' => 'tx_news_domain_model_news',
'label' => 'module.createNewNewsRecord',
'action' => 'newNews',
'icon' => 'ext-news-type-default'
],
[
'table' => 'tx_news_domain_model_tag',
'label' => 'module.createNewTag',
'action' => 'newTag',
'icon' => 'ext-news-tag'
],
[
'table' => 'sys_category',
'label' => 'module.createNewCategory',
'action' => 'newCategory',
'icon' => 'mimetypes-x-sys_category'
]
];
foreach ($buttons as $key => $tableConfiguration) {
if ($this->showButton($tableConfiguration['table'])) {
$title = $this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:' . $tableConfiguration['label']);
$viewButton = $buttonBar->makeLinkButton()
->setHref($uriBuilder->reset()->setRequest($this->request)->uriFor(
$tableConfiguration['action'],
[],
'Administration'
))
->setDataAttributes([
'toggle' => 'tooltip',
'placement' => 'bottom',
'title' => $title])
->setTitle($title)
->setIcon($this->iconFactory->getIcon($tableConfiguration['icon'], Icon::SIZE_SMALL, 'overlay-new'));
$buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
}
}
$clipBoard = GeneralUtility::makeInstance(Clipboard::class);
$clipBoard->initializeClipboard();
$elFromTable = $clipBoard->elFromTable('tx_news_domain_model_news');
if (!empty($elFromTable)) {
$viewButton = $buttonBar->makeLinkButton()
->setHref($clipBoard->pasteUrl('', $this->pageUid))
->setOnClick('return ' . $clipBoard->confirmMsgText(
'pages',
BackendUtilityCore::getRecord('pages', $this->pageUid),
'into',
$elFromTable
))
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:clip_pasteInto'))
->setIcon($this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL));
$buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
}
// Donation
$donationButton = $buttonBar->makeLinkButton()
->setHref($uriBuilder->reset()->setRequest($this->request)->uriFor(
'donate',
[],
'Administration'
))
->setTitle($this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:administration.donation.title'))
->setIcon($this->iconFactory->getIcon('ext-news-donation', Icon::SIZE_SMALL));
$buttonBar->addButton($donationButton, ButtonBar::BUTTON_POSITION_RIGHT);
// Refresh
$refreshButton = $buttonBar->makeLinkButton()
->setHref(GeneralUtility::getIndpEnv('REQUEST_URI'))
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload'))
->setIcon($this->iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL));
$buttonBar->addButton($refreshButton, ButtonBar::BUTTON_POSITION_RIGHT);
// Shortcut
if ($this->getBackendUser()->mayMakeShortcut()) {
$shortcutButton = $buttonBar->makeShortcutButton()
->setGetVariables(['route', 'module', 'id'])
->setModuleName($this->request->getPluginName())
->setDisplayName('Shortcut');
$buttonBar->addButton($shortcutButton, ButtonBar::BUTTON_POSITION_RIGHT);
}
}
protected function showButton(string $table): bool
{
if (!$this->getBackendUser()->check('tables_modify', $table)) {
return false;
}
// No deny/allow tables are set:
if (empty($this->allowedNewTables) && empty($this->deniedNewTables)) {
return true;
}
$showButton = !in_array($table, $this->deniedNewTables, true) &&
(empty($this->allowedNewTables) || in_array($table, $this->allowedNewTables, true));
return $showButton;
}
public function indexAction(): void
{
$this->redirectToPageOnStart();
$demandVars = GeneralUtility::_GET('tx_news_web_newsadministration');
$demand = GeneralUtility::makeInstance(AdministrationDemand::class);
$autoSubmitForm = 0;
if (!empty($demandVars['demand'] ?? [])) {
foreach ($demandVars['demand'] as $key => $value) {
if (property_exists(AdministrationDemand::class, $key)) {
$getter = 'set' . ucfirst($key);
$demand->$getter($value);
}
}
} else {
// Preselect by TsConfig (e.g. tx_news.module.preselect.topNewsRestriction = 1)
if (isset($this->tsConfiguration['preselect.'])
&& is_array($this->tsConfiguration['preselect.'])
) {
$anyPropertySet = false;
unset($this->tsConfiguration['preselect.']['orderByAllowed']);
foreach ($this->tsConfiguration['preselect.'] as $propertyName => $propertyValue) {
$propertySet = ObjectAccess::setProperty($demand, $propertyName, $propertyValue);
if ($propertySet) {
$anyPropertySet = true;
}
}
if ($anyPropertySet && !GeneralUtility::_GET('formSubmitted')) {
$autoSubmitForm = 1;
}
}
if (!(bool)($this->tsConfiguration['alwaysShowFilter'] ?? false) || !$this->isFilteringEnabled()) {
$this->view->assign('hideForm', true);
}
}
$this->view->assign('autoSubmitForm', $autoSubmitForm);
$categories = $this->categoryRepository->findParentCategoriesByPid($this->pageUid);
$idList = [];
foreach ($categories as $c) {
$idList[] = $c->getUid();
}
if (empty($idList) && !$this->getBackendUser()->isAdmin()) {
$idList = $this->getBackendUser()->getCategoryMountPoints();
}
if (!empty($this->tsConfiguration['allowedCategoryRootIds'])) {
$allowedList = GeneralUtility::intExplode(',', $this->tsConfiguration['allowedCategoryRootIds'], true);
if (!empty($allowedList)) {
$idList = array_intersect($idList, $allowedList);
}
}
// Initialize the dblist object:
$dblist = GeneralUtility::makeInstance(NewsDatabaseRecordList::class);
$this->view->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ActionDispatcher');
$this->view->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
$pageinfo = BackendUtilityCore::readPageAccess($this->pageUid, $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW));
$dblist->pageRow = $pageinfo;
$majorVersion = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
if ($majorVersion <= 10) {
$dblist->calcPerms = Permission::CONTENT_EDIT;
} else {
$dblist->calcPerms->set(Permission::CONTENT_EDIT);
if ($majorVersion >= 11) {
$dblist->displayColumnSelector = false;
$dblist->displayRecordDownload = false;
}
}
$dblist->disableSingleTableView = true;
$dblist->clickTitleMode = 'edit';
$dblist->allFields = 1;
$dblist->displayFields = false;
$dblist->dontShowClipControlPanels = true;
$pointer = MathUtility::forceIntegerInRange(GeneralUtility::_GP('pointer'), 0);
$limit = isset($this->settings['list']['paginate']['itemsPerPage']) ? (int)$this->settings['list']['paginate']['itemsPerPage'] : 20;
$dblist->start(
$this->pageUid,
'tx_news_domain_model_news',
$pointer,
'',
$demand->getRecursive(),
$limit
);
$dblist->setDispFields();
$dblist->noControlPanels = !(bool)($this->tsConfiguration['controlPanels'] ?? false);
$dblist->setFields = [
'tx_news_domain_model_news' => GeneralUtility::trimExplode(',', $this->tsConfiguration['columns'] ?? 'teaser,istopnews,datetime,categories', true)
];
$tableRendering = $dblist->generateList();
if (!$tableRendering && GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() <= 10) {
$tableRendering = $dblist->HTMLcode;
}
$tableRendering = trim($tableRendering);
$counter = !empty($tableRendering);
$this->view->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Recordlist');
$assignedValues = [
'moduleToken' => $this->getToken(true),
'page' => $this->pageUid,
'demand' => $demand,
'news' => $tableRendering,
'newsCount' => $counter,
'showSearchForm' => (!is_null($demand) || $counter > 0),
'requestUri' => GeneralUtility::quoteJSvalue(rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI'))),
'categories' => $this->categoryRepository->findTree($idList),
'filters' => $this->tsConfiguration['filters.'],
'enableFiltering' => $this->isFilteringEnabled(),
'dateformat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy']
];
$event = $this->eventDispatcher->dispatch(new AdministrationIndexActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
}
/**
* Shows a page tree including count of news + category records
*/
public function newsPidListingAction(int $treeLevel = 2): void
{
$tree = Page::pageTree($this->pageUid, $treeLevel);
$rawTree = [];
foreach ($tree->tree as $row) {
$this->countRecordsOnPage($row);
$rawTree[] = $row;
}
$event = $this->eventDispatcher->dispatch(new AdministrationNewsPidListingActionEvent($this, $rawTree, $treeLevel));
$this->view->assignMultiple([
'tree' => $event->getRawTree(),
'treeLevel' => $event->getTreeLevel(),
]);
}
public function donateAction(): void
{
$this->view->assignMultiple([
'counts' => $this->administrationRepository->getTotalCounts()
]);
}
/**
* Redirect to form to create a news record
*
* @return void
*/
public function newNewsAction(): void
{
$this->redirectToCreateNewRecord('tx_news_domain_model_news');
}
/**
* Redirect to form to create a category record
*
* @return void
*/
public function newCategoryAction(): void
{
$this->redirectToCreateNewRecord('sys_category');
}
/**
* Redirect to form to create a tag record
*
* @return void
*/
public function newTagAction(): void
{
$this->redirectToCreateNewRecord('tx_news_domain_model_tag');
}
/**
* Update page record array with count of news & category records
*
* @param array $row page record
*
* @return void
*/
private function countRecordsOnPage(array &$row): void
{
$pageUid = (int)$row['row']['uid'];
$counts = [
'countNews' => 'tx_news_domain_model_news',
'countCategories' => 'sys_category',
'countTags' => 'tx_news_domain_model_tag',
];
foreach ($counts as $key => $table) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($table);
$row[$key] = $queryBuilder->count('*')
->from($table)
->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageUid, \PDO::PARAM_INT)))
->execute()
->fetchColumn(0);
}
$row['countAll'] = ($row['countNews'] + $row['countCategories'] + $row['countTags']);
}
/**
* Redirect to creating a new record
*
* @param string $table table name
*/
private function redirectToCreateNewRecord(string $table): void
{
$pid = $this->pageUid;
if ($pid === 0 && isset($this->tsConfiguration['defaultPid.'])
&& is_array($this->tsConfiguration['defaultPid.'])
&& isset($this->tsConfiguration['defaultPid.'][$table])
) {
$pid = (int)$this->tsConfiguration['defaultPid.'][$table];
}
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
$returnUrl = $uriBuilder->buildUriFromRoutePath('/module/web/NewsAdministration/', [
'id' => $this->pageUid,
'token' => $this->getToken(true)
]);
$params = [
'edit[' . $table . '][' . $pid . ']' => 'new',
'returnUrl' => (string)$returnUrl
];
$url = $uriBuilder->buildUriFromRoute('record_edit', $params);
HttpUtility::redirect((string)$url);
}
/**
* Set the TsConfig configuration for the extension
*/
protected function setTsConfig(): void
{
$tsConfig = BackendUtilityCore::getPagesTSconfig($this->pageUid);
if (isset($tsConfig['tx_news.']['module.']) && is_array($tsConfig['tx_news.']['module.'])) {
$this->tsConfiguration = $tsConfig['tx_news.']['module.'];
}
}
/**
* Check if at least one filter is enabled
*/
protected function isFilteringEnabled(): bool
{
if (isset($this->tsConfiguration['filters.'])) {
foreach ($this->tsConfiguration['filters.'] as $filter => $enabled) {
if ($enabled == 1) {
// Check dependencies on other filter
if (($filter === 'categoryConjunction' || $filter === 'includeSubCategories')
&& $this->tsConfiguration['filters.']['categories'] == 0) {
continue;
}
return true;
}
}
}
return false;
}
/**
* If defined in TsConfig with tx_news.module.redirectToPageOnStart = 123
* and the current page id is 0, a redirect to the given page will be done
*/
protected function redirectToPageOnStart(): void
{
$allowedPage = (int)($this->tsConfiguration['allowedPage'] ?? 0);
$redirectPageOnStart = (int)($this->tsConfiguration['redirectToPageOnStart'] ?? 0);
if ($allowedPage > 0 && $this->pageUid !== $allowedPage) {
$id = $allowedPage;
} elseif ($this->pageUid === 0 && $redirectPageOnStart > 0) {
$id = $redirectPageOnStart;
}
if (!empty($id)) {
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
$url = $uriBuilder->buildUriFromRoutePath('/module/web/NewsAdministration/', [
'id' => $id,
'token' => $this->getToken(true)
]);
HttpUtility::redirect((string)$url);
}
}
/**
* Get a CSRF token
*
* @param bool $tokenOnly Set it to TRUE to get only the token, otherwise including the &moduleToken= as prefix
*/
protected function getToken(bool $tokenOnly = false): string
{
$tokenParameterName = 'token';
$token = FormProtectionFactory::get('backend')->generateToken('route', 'web_NewsAdministration');
if ($tokenOnly) {
return $token;
}
return '&' . $tokenParameterName . '=' . $token;
}
/**
* Show support area only for admins in given percent of time
*
* @param int $probabilityInPercent
*/
private function showSupportArea(int $probabilityInPercent = 10): bool
{
if (!$this->getBackendUser()->isAdmin()) {
return false;
}
if (mt_rand() % 100 <= $probabilityInPercent) {
return true;
}
return false;
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
@@ -0,0 +1,51 @@
<?php
namespace GeorgRinger\News\Controller;
use GeorgRinger\News\Event\CategoryListActionEvent;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Category controller
*/
class CategoryController extends NewsController
{
/**
* List categories
*
* @param array $overwriteDemand
*
* @return void
*/
public function listAction(array $overwriteDemand = null)
{
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
if ($overwriteDemand !== null && $this->settings['disableOverrideDemand'] != 1) {
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
}
$idList = is_array($demand->getCategories()) ? $demand->getCategories() : explode(',', $demand->getCategories());
$startingPoint = null;
if (!empty($this->settings['startingpoint'])) {
$startingPoint = $this->settings['startingpoint'];
}
$assignedValues = [
'categories' => $this->categoryRepository->findTree($idList, $startingPoint),
'overwriteDemand' => $overwriteDemand,
'demand' => $demand,
];
$event = $this->eventDispatcher->dispatch(new CategoryListActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
}
}
@@ -0,0 +1,161 @@
<?php
namespace GeorgRinger\News\Controller;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use Exception;
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
use GeorgRinger\News\Jobs\ImportJobInterface;
use GeorgRinger\News\Utility\ImportJob;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\View\BackendTemplateView;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use UnexpectedValueException;
/**
* Controller to import news records
*/
class ImportController extends ActionController
{
/**
* Backend Template Container
*
* @var BackendTemplateView
*/
protected $defaultViewObjectName = BackendTemplateView::class;
/**
* Retrieve all available import jobs by traversing trough registered
* import jobs and checking "isEnabled".
*
* @return array
*/
protected function getAvailableJobs(): array
{
$availableJobs = [];
$registeredJobs = ImportJob::getRegisteredJobs();
foreach ($registeredJobs as $registeredJob) {
$jobInstance = $this->objectManager->get($registeredJob['className']);
if ($jobInstance instanceof ImportJobInterface && $jobInstance->isEnabled()) {
$availableJobs[$registeredJob['className']] = $GLOBALS['LANG']->sL($registeredJob['title']);
}
}
return $availableJobs;
}
/**
* Shows the import jobs selection .
*
* @return void
*/
public function indexAction(): void
{
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$pageRenderer->loadRequireJsModule('TYPO3/CMS/News/Import');
$this->view->assignMultiple(
[
'error' => $this->checkCorrectConfiguration(),
'availableJobs' => array_merge([0 => ''], $this->getAvailableJobs()),
'moduleUrl' => $uriBuilder->buildUriFromRoute($this->request->getPluginName())
]
);
}
/**
* Check for correct configuration
*
* @return string
* @throws Exception
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
*/
protected function checkCorrectConfiguration(): string
{
$error = '';
$settings = GeneralUtility::makeInstance(EmConfiguration::class);
try {
$storageId = (int)$settings->getStorageUidImporter();
$path = $settings->getResourceFolderImporter();
if ($storageId === 0) {
throw new UnexpectedValueException('import.error.configuration.storageUidImporter');
}
if (empty($path)) {
throw new UnexpectedValueException('import.error.configuration.resourceFolderImporter');
}
$storage = $this->getResourceFactory()->getStorageObject($storageId);
$pathExists = $storage->hasFolder($path);
if (!$pathExists) {
throw new FolderDoesNotExistException('Folder does not exist', 1474827988);
}
} catch (FolderDoesNotExistException $e) {
$error = 'import.error.configuration.resourceFolderImporter.notExist';
} catch (UnexpectedValueException $e) {
$error = $e->getMessage();
}
return $error;
}
/**
* Runs an actual job.
*
* @param string $jobClassName
* @param int $offset
* @return string
*/
public function runJobAction($jobClassName, $offset = 0): string
{
/** @var ImportJobInterface $job */
$job = $this->objectManager->get($jobClassName);
$job->run($offset);
return 'OK';
}
/**
* Retrieves the job info of a given jobClass
*
* @param string $jobClassName
* @return string
*/
public function jobInfoAction($jobClassName): string
{
$response = null;
try {
/** @var ImportJobInterface $job */
$job = $this->objectManager->get($jobClassName);
$response = $job->getInfo();
} catch (Exception $e) {
$response['message'] = $e->getMessage();
$response['line'] = $e->getLine();
$response['trace'] = $e->getTrace();
HttpUtility::setResponseCode(HttpUtility::HTTP_STATUS_400);
}
return json_encode($response);
}
/**
* @return ResourceFactory
*/
protected function getResourceFactory(): ResourceFactory
{
return GeneralUtility::makeInstance(ResourceFactory::class);
}
}
@@ -0,0 +1,166 @@
<?php
namespace GeorgRinger\News\Controller;
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Extbase\Mvc\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Frontend\Controller\ErrorController;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Base controller
*/
class NewsBaseController extends ActionController
{
/**
* Initializes the view before invoking an action method.
* Override this method to solve assign variables common for all actions
* or prepare the view in another way before the action is called.
*
* @param \TYPO3\CMS\Extbase\Mvc\View\ViewInterface $view The view to be initialized
*
* @return void
*/
protected function initializeView(ViewInterface $view)
{
$view->assign('contentObjectData', $this->configurationManager->getContentObject()->data);
$view->assign('emConfiguration', GeneralUtility::makeInstance(EmConfiguration::class));
if (isset($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
$view->assign('pageData', $GLOBALS['TSFE']->page);
}
parent::initializeView($view);
}
// /**
// * @param RequestInterface $request
// * @param ResponseInterface $response
// * @throws \Exception
// */
// public function processRequest(RequestInterface $request, ResponseInterface $response)
// {
// try {
// parent::processRequest($request, $response);
// } catch (\Exception $exception) {
// $this->handleKnownExceptionsElseThrowAgain($exception);
// }
// }
/**
* @param \Exception $exception
*
* @throws \Exception
*
* @return void
*/
private function handleKnownExceptionsElseThrowAgain(\Exception $exception): void
{
$previousException = $exception->getPrevious();
if (
$this->actionMethodName === 'detailAction'
&& $previousException instanceof \TYPO3\CMS\Extbase\Property\Exception
&& isset($this->settings['detail']['errorHandling'])
) {
$this->handleNoNewsFoundError($this->settings['detail']['errorHandling']);
} else {
throw $exception;
}
}
/**
* Error handling if no news entry is found
*
* @param string $configuration configuration what will be done
* @throws \InvalidArgumentException
*/
protected function handleNoNewsFoundError($configuration): string
{
if (empty($configuration)) {
return '';
}
$options = GeneralUtility::trimExplode(',', $configuration, true);
switch ($options[0]) {
case 'redirectToListView':
$this->redirect('list');
break;
case 'redirectToPage':
if (count($options) === 1 || count($options) > 3) {
$msg = sprintf(
'If error handling "%s" is used, either 2 or 3 arguments, split by "," must be used',
$options[0]
);
throw new \InvalidArgumentException($msg);
}
$this->uriBuilder->reset();
$this->uriBuilder->setTargetPageUid($options[1]);
$this->uriBuilder->setCreateAbsoluteUri(true);
if (GeneralUtility::getIndpEnv('TYPO3_SSL')) {
$this->uriBuilder->setAbsoluteUriScheme('https');
}
$url = $this->uriBuilder->build();
if (isset($options[2])) {
$this->redirectToUri($url, 0, (int)$options[2]);
} else {
$this->redirectToUri($url);
}
break;
case 'pageNotFoundHandler':
$typo3Information = GeneralUtility::makeInstance(Typo3Version::class);
if ($typo3Information->getMajorVersion() === 9) {
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction($GLOBALS['TYPO3_REQUEST'], 'No news entry found.');
throw new ImmediateResponseException($response);
}
$message = 'No news entry found!';
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'],
$message
);
throw new ImmediateResponseException($response, 1590468229);
break;
case 'showStandaloneTemplate':
if (isset($options[2])) {
$statusCode = constant(HttpUtility::class . '::HTTP_STATUS_' . $options[2]);
} else {
$statusCode = HttpUtility::HTTP_STATUS_404;
}
HttpUtility::setResponseCode($statusCode);
$this->getTypoScriptFrontendController()->set_no_cache('News record not found');
$standaloneTemplate = GeneralUtility::makeInstance(StandaloneView::class);
$standaloneTemplate->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($options[1]));
return $standaloneTemplate->render();
break;
default:
return '';
}
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
}
@@ -0,0 +1,703 @@
<?php
namespace GeorgRinger\News\Controller;
use GeorgRinger\News\Domain\Model\Dto\NewsDemand;
use GeorgRinger\News\Domain\Model\Dto\Search;
use GeorgRinger\News\Domain\Model\News;
use GeorgRinger\News\Domain\Repository\CategoryRepository;
use GeorgRinger\News\Domain\Repository\NewsRepository;
use GeorgRinger\News\Domain\Repository\TagRepository;
use GeorgRinger\News\Event\NewsCheckPidOfNewsRecordFailedInDetailActionEvent;
use GeorgRinger\News\Event\NewsDateMenuActionEvent;
use GeorgRinger\News\Event\NewsDetailActionEvent;
use GeorgRinger\News\Event\NewsListActionEvent;
use GeorgRinger\News\Event\NewsListSelectedActionEvent;
use GeorgRinger\News\Event\NewsSearchFormActionEvent;
use GeorgRinger\News\Event\NewsSearchResultActionEvent;
use GeorgRinger\News\Pagination\QueryResultPaginator;
use GeorgRinger\News\Seo\NewsTitleProvider;
use GeorgRinger\News\Utility\Cache;
use GeorgRinger\News\Utility\ClassCacheManager;
use GeorgRinger\News\Utility\Page;
use GeorgRinger\News\Utility\TypoScript;
use GeorgRinger\NumberedPagination\NumberedPagination;
use TYPO3\CMS\Core\Pagination\SimplePagination;
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Fluid\View\TemplateView;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Controller of news records
*
*/
class NewsController extends NewsBaseController
{
/**
* @var \GeorgRinger\News\Domain\Repository\NewsRepository
*/
protected $newsRepository;
/**
* @var \GeorgRinger\News\Domain\Repository\CategoryRepository
*/
protected $categoryRepository;
/**
* @var \GeorgRinger\News\Domain\Repository\TagRepository
*/
protected $tagRepository;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/** @var array */
protected $ignoredSettingsForOverride = ['demandclass', 'orderbyallowed', 'selectedList'];
/**
* Original settings without any magic done by stdWrap and skipping empty values
*
* @var array
*/
protected $originalSettings = [];
/**
* NewsController constructor.
* @param NewsRepository $newsRepository
* @param CategoryRepository $categoryRepository
* @param TagRepository $tagRepository
* @param ConfigurationManagerInterface $configurationManager
*/
public function __construct(
NewsRepository $newsRepository,
CategoryRepository $categoryRepository,
TagRepository $tagRepository,
ConfigurationManagerInterface $configurationManager
) {
$this->newsRepository = $newsRepository;
$this->categoryRepository = $categoryRepository;
$this->tagRepository = $tagRepository;
$this->configurationManager = $configurationManager;
}
/**
* Initializes the current action
*
* @return void
*/
public function initializeAction()
{
GeneralUtility::makeInstance(ClassCacheManager::class)->reBuildSimple();
$this->buildSettings();
if (isset($this->settings['format'])) {
$this->request->setFormat($this->settings['format']);
}
// Only do this in Frontend Context
if (!empty($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
// We only want to set the tag once in one request, so we have to cache that statically if it has been done
static $cacheTagsSet = false;
/** @var $typoScriptFrontendController \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController */
$typoScriptFrontendController = $GLOBALS['TSFE'];
if (!$cacheTagsSet) {
$typoScriptFrontendController->addCacheTags(['tx_news']);
$cacheTagsSet = true;
}
}
}
/**
* Create the demand object which define which records will get shown
*
* @param array $settings
* @param string $class optional class which must be an instance of \GeorgRinger\News\Domain\Model\Dto\NewsDemand
* @return \GeorgRinger\News\Domain\Model\Dto\NewsDemand
*/
protected function createDemandObjectFromSettings(
array $settings,
$class = NewsDemand::class
): \GeorgRinger\News\Domain\Model\Dto\NewsDemand {
$class = isset($settings['demandClass']) && !empty($settings['demandClass']) ? $settings['demandClass'] : $class;
/* @var $demand NewsDemand */
$demand = GeneralUtility::makeInstance($class, $settings);
if (!$demand instanceof NewsDemand) {
throw new \UnexpectedValueException(
sprintf(
'The demand object must be an instance of %s, but %s given!',
NewsDemand::class,
$class
),
1423157953
);
}
$demand->setCategories(GeneralUtility::trimExplode(',', $settings['categories'] ?? '', true));
$demand->setCategoryConjunction((string)($settings['categoryConjunction'] ?? ''));
$demand->setIncludeSubCategories((bool)($settings['includeSubCategories'] ?? false));
$demand->setTags((string)($settings['tags'] ?? ''));
$demand->setTopNewsRestriction((int)($settings['topNewsRestriction'] ?? 0));
$demand->setTimeRestriction($settings['timeRestriction'] ?? '');
$demand->setTimeRestrictionHigh($settings['timeRestrictionHigh'] ?? '');
$demand->setArchiveRestriction((string)($settings['archiveRestriction'] ?? ''));
$demand->setExcludeAlreadyDisplayedNews((bool)($settings['excludeAlreadyDisplayedNews'] ?? false));
$demand->setHideIdList((string)($settings['hideIdList'] ?? ''));
if ($settings['orderBy'] ?? '') {
$demand->setOrder($settings['orderBy'] . ' ' . $settings['orderDirection']);
}
$demand->setOrderByAllowed((string)($settings['orderByAllowed'] ?? ''));
$demand->setTopNewsFirst((bool)($settings['topNewsFirst'] ?? false));
$demand->setLimit((int)($settings['limit'] ?? 0));
$demand->setOffset((int)($settings['offset'] ?? 0));
$demand->setSearchFields((string)($settings['search']['fields'] ?? ''));
$demand->setDateField((string)($settings['dateField'] ?? ''));
$demand->setMonth((int)($settings['month'] ?? 0));
$demand->setYear((int)($settings['year'] ?? 0));
$demand->setStoragePage(Page::extendPidListByChildren(
(string)($settings['startingpoint'] ?? ''),
(int)($settings['recursive'] ?? 0)
));
if ($hooks = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Controller/NewsController.php']['createDemandObjectFromSettings'] ?? []) {
$params = [
'demand' => $demand,
'settings' => $settings,
'class' => $class,
];
foreach ($hooks as $reference) {
GeneralUtility::callUserFunction($reference, $params, $this);
}
}
return $demand;
}
/**
* Overwrites a given demand object by an propertyName => $propertyValue array
*
* @param \GeorgRinger\News\Domain\Model\Dto\NewsDemand $demand
* @param array $overwriteDemand
* @return \GeorgRinger\News\Domain\Model\Dto\NewsDemand
*/
protected function overwriteDemandObject(NewsDemand $demand, array $overwriteDemand): \GeorgRinger\News\Domain\Model\Dto\NewsDemand
{
foreach ($this->ignoredSettingsForOverride as $property) {
unset($overwriteDemand[$property]);
}
foreach ($overwriteDemand as $propertyName => $propertyValue) {
if (in_array(strtolower($propertyName), $this->ignoredSettingsForOverride, true)) {
continue;
}
if ($propertyValue !== '' || $this->settings['allowEmptyStringsForOverwriteDemand']) {
if (in_array($propertyName, ['categories'], true)) {
$propertyValue = GeneralUtility::trimExplode(',', $propertyValue, true);
}
ObjectAccess::setProperty($demand, $propertyName, $propertyValue);
}
}
return $demand;
}
/**
* Output a list view of news
*
* @param array|null $overwriteDemand
*
* @return void
*/
public function listAction(array $overwriteDemand = null)
{
$this->forwardToDetailActionWhenRequested();
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
if ((int)($this->settings['disableOverrideDemand'] ?? 1) !== 1 && $overwriteDemand !== null) {
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
}
$newsRecords = $this->newsRepository->findDemanded($demand);
// pagination
$paginationConfiguration = $this->settings['list']['paginate'] ?? [];
$itemsPerPage = (int)(($paginationConfiguration['itemsPerPage'] ?? '') ?: 10);
$maximumNumberOfLinks = (int)($paginationConfiguration['maximumNumberOfLinks'] ?? 0);
$currentPage = $this->request->hasArgument('currentPage') ? (int)$this->request->getArgument('currentPage') : 1;
$paginator = GeneralUtility::makeInstance(QueryResultPaginator::class, $newsRecords, $currentPage, $itemsPerPage, (int)($this->settings['limit'] ?? 0), (int)($this->settings['offset'] ?? 0));
$paginationClass = $paginationConfiguration['class'] ?? SimplePagination::class;
if (class_exists(NumberedPagination::class) && $paginationClass === NumberedPagination::class && $maximumNumberOfLinks) {
$pagination = GeneralUtility::makeInstance(NumberedPagination::class, $paginator, $maximumNumberOfLinks);
} elseif (class_exists($paginationClass)) {
$pagination = GeneralUtility::makeInstance($paginationClass, $paginator);
} else {
$pagination = GeneralUtility::makeInstance(SimplePagination::class, $paginator);
}
$assignedValues = [
'news' => $newsRecords,
'overwriteDemand' => $overwriteDemand,
'demand' => $demand,
'categories' => null,
'tags' => null,
'settings' => $this->settings,
'pagination' => [
'currentPage' => $currentPage,
'paginator' => $paginator,
'pagination' => $pagination,
]
];
if ($demand->getCategories() !== '') {
$categoriesList = $demand->getCategories();
if (is_string($categoriesList)) {
$categoriesList = GeneralUtility::trimExplode(',', $categoriesList);
}
if (!empty($categoriesList)) {
$assignedValues['categories'] = $this->categoryRepository->findByIdList($categoriesList);
}
}
if ($demand->getTags() !== '') {
$tagList = $demand->getTags();
if (!is_array($tagList)) {
$tagList = GeneralUtility::trimExplode(',', $tagList);
}
if (!empty($tagList)) {
$assignedValues['tags'] = $this->tagRepository->findByIdList($tagList);
}
}
$event = $this->eventDispatcher->dispatch(new NewsListActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
Cache::addPageCacheTagsByDemandObject($demand);
}
/**
* When list action is called along with a news argument, we forward to detail action.
*/
protected function forwardToDetailActionWhenRequested()
{
if (!$this->isActionAllowed('detail')
|| !$this->request->hasArgument('news')
) {
return;
}
$this->forward('detail', null, null, ['news' => $this->request->getArgument('news')]);
}
/**
* Checks whether an action is enabled in switchableControllerActions configuration
*
* @param string $action
* @return bool
*/
protected function isActionAllowed(string $action): bool
{
$frameworkConfiguration = $this->configurationManager->getConfiguration($this->configurationManager::CONFIGURATION_TYPE_FRAMEWORK);
$allowedActions = $frameworkConfiguration['controllerConfiguration']['News']['actions'] ?? [];
return \in_array($action, $allowedActions, true);
}
/**
* Output a selected list view of news
*/
public function selectedListAction(): void
{
$newsRecords = [];
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
if (empty($this->originalSettings['orderBy'] ?? '')) {
$idList = GeneralUtility::trimExplode(',', $this->settings['selectedList'], true);
foreach ($idList as $id) {
$news = $this->newsRepository->findByIdentifier($id);
if ($news) {
$newsRecords[] = $news;
}
}
} else {
$demand->setIdList($this->settings['selectedList']);
$newsRecords = $this->newsRepository->findDemanded($demand);
}
$assignedValues = [
'news' => $newsRecords,
'demand' => $demand,
'settings' => $this->settings
];
$event = $this->eventDispatcher->dispatch(new NewsListSelectedActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
if (!empty($newsRecords) && is_a($newsRecords[0], News::class)) {
Cache::addCacheTagsByNewsRecords($newsRecords);
}
}
/**
* Single view of a news record
*
* @param \GeorgRinger\News\Domain\Model\News $news news item
* @param int $currentPage current page for optional pagination
*
* @return null|string
*/
public function detailAction(News $news = null, $currentPage = 1)
{
if ($news === null || ($this->settings['isShortcut'] ?? false)) {
$previewNewsId = (int)($this->settings['singleNews'] ?? 0);
if ($this->request->hasArgument('news_preview')) {
$previewNewsId = (int)$this->request->getArgument('news_preview');
}
if ($previewNewsId > 0) {
if ($this->isPreviewOfHiddenRecordsEnabled()) {
$news = $this->newsRepository->findByUid($previewNewsId, false);
} else {
$news = $this->newsRepository->findByUid($previewNewsId);
}
}
}
if (is_a($news, News::class) && ($this->settings['detail']['checkPidOfNewsRecord'] ?? false)
) {
$news = $this->checkPidOfNewsRecord($news);
}
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
$assignedValues = [
'newsItem' => $news,
'currentPage' => (int)$currentPage,
'demand' => $demand,
'settings' => $this->settings
];
$event = $this->eventDispatcher->dispatch(new NewsDetailActionEvent($this, $assignedValues));
$assignedValues = $event->getAssignedValues();
$news = $assignedValues['newsItem'];
$this->view->assignMultiple($assignedValues);
// reset news if type is internal or external
if ($news && !($this->settings['isShortcut'] ?? false) && ($news->getType() === '1' || $news->getType() === '2')) {
$news = null;
}
if ($news !== null) {
Page::setRegisterProperties($this->settings['detail']['registerProperties'] ?? false, $news);
Cache::addCacheTagsByNewsRecords([$news]);
if ($this->settings['detail']['pageTitle']['_typoScriptNodeValue'] ?? false) {
$providerConfiguration = $this->settings['detail']['pageTitle'] ?? [];
$providerClass = $providerConfiguration['provider'] ?? NewsTitleProvider::class;
/** @var NewsTitleProvider $provider */
$provider = GeneralUtility::makeInstance($providerClass);
$provider->setTitleByNews($news, $providerConfiguration);
}
} elseif (isset($this->settings['detail']['errorHandling'])) {
$errorContent = $this->handleNoNewsFoundError($this->settings['detail']['errorHandling']);
if ($errorContent) {
return $errorContent;
}
}
}
/**
* Checks if the news pid could be found in the startingpoint settings of the detail plugin and
* if the pid could not be found it return NULL instead of the news object.
*
* @param \GeorgRinger\News\Domain\Model\News $news
* @return \GeorgRinger\News\Domain\Model\News|null
*/
protected function checkPidOfNewsRecord(News $news): ?\GeorgRinger\News\Domain\Model\News
{
$allowedStoragePages = GeneralUtility::trimExplode(
',',
Page::extendPidListByChildren(
$this->settings['startingpoint'],
$this->settings['recursive']
),
true
);
if (count($allowedStoragePages) > 0 && !in_array($news->getPid(), $allowedStoragePages)) {
$this->eventDispatcher->dispatch(new NewsCheckPidOfNewsRecordFailedInDetailActionEvent($this, $news));
$news = null;
}
return $news;
}
/**
* Checks if preview is enabled either in TS or FlexForm
*
* @return bool
*/
protected function isPreviewOfHiddenRecordsEnabled(): bool
{
if (!empty($this->settings['previewHiddenRecords']) && $this->settings['previewHiddenRecords'] == 2) {
$previewEnabled = !empty($this->settings['enablePreviewOfHiddenRecords']);
} else {
$previewEnabled = !empty($this->settings['previewHiddenRecords']);
}
return $previewEnabled;
}
/**
* Render a menu by dates, e.g. years, months or dates
*
* @param array|null $overwriteDemand
*
* @return void
*/
public function dateMenuAction(array $overwriteDemand = null): void
{
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
if ($this->settings['disableOverrideDemand'] != 1 && $overwriteDemand !== null) {
$overwriteDemandTemp = $overwriteDemand;
unset($overwriteDemandTemp['year']);
unset($overwriteDemandTemp['month']);
$demand = $this->overwriteDemandObject(
$demand,
$overwriteDemandTemp
);
unset($overwriteDemandTemp);
}
// It might be that those are set, @see http://forge.typo3.org/issues/44759
$demand->setLimit(0);
$demand->setOffset(0);
// @todo: find a better way to do this related to #13856
if (!$dateField = $this->settings['dateField']) {
$dateField = 'datetime';
}
$demand->setOrder($dateField . ' ' . $this->settings['orderDirection']);
$newsRecords = $this->newsRepository->findDemanded($demand);
$demand->setOrder($this->settings['orderDirection']);
$statistics = $this->newsRepository->countByDate($demand);
$assignedValues = [
'listPid' => ($this->settings['listPid'] ? $this->settings['listPid'] : $GLOBALS['TSFE']->id),
'dateField' => $dateField,
'data' => $statistics,
'news' => $newsRecords,
'overwriteDemand' => $overwriteDemand,
'demand' => $demand,
'settings' => $this->settings
];
$event = $this->eventDispatcher->dispatch(new NewsDateMenuActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
}
/**
* Display the search form
*
* @param \GeorgRinger\News\Domain\Model\Dto\Search $search
* @param array $overwriteDemand
*
* @return void
*/
public function searchFormAction(
Search $search = null,
array $overwriteDemand = []
): void {
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
if ($this->settings['disableOverrideDemand'] != 1 && $overwriteDemand !== null) {
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
}
if (is_null($search)) {
$search = GeneralUtility::makeInstance(Search::class);
}
$demand->setSearch($search);
$assignedValues = [
'search' => $search,
'overwriteDemand' => $overwriteDemand,
'demand' => $demand,
'settings' => $this->settings
];
$event = $this->eventDispatcher->dispatch(new NewsSearchFormActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
}
/**
* Displays the search result
*
* @param \GeorgRinger\News\Domain\Model\Dto\Search $search
* @param array $overwriteDemand
*
* @return void
*/
public function searchResultAction(
Search $search = null,
array $overwriteDemand = []
): void {
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
if ($this->settings['disableOverrideDemand'] != 1 && $overwriteDemand !== null) {
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
}
if (!is_null($search)) {
$search->setFields($this->settings['search']['fields']);
$search->setDateField($this->settings['dateField']);
$search->setSplitSubjectWords((bool)$this->settings['search']['splitSearchWord']);
}
$demand->setSearch($search);
$assignedValues = [
'news' => $this->newsRepository->findDemanded($demand),
'overwriteDemand' => $overwriteDemand,
'search' => $search,
'demand' => $demand,
'settings' => $this->settings
];
$event = $this->eventDispatcher->dispatch(new NewsSearchResultActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
}
/**
* initialize search result action
*
* @return void
*/
public function initializeSearchResultAction(): void
{
$this->initializeSearchActions();
}
/**
* Initialize search form action
*
* @return void
*/
public function initializeSearchFormAction(): void
{
$this->initializeSearchActions();
}
/**
* Initialize searchForm and searchResult actions
*
* @return void
*/
protected function initializeSearchActions(): void
{
if ($this->arguments->hasArgument('search')) {
$propertyMappingConfiguration = $this->arguments['search']->getPropertyMappingConfiguration();
$propertyMappingConfiguration->allowAllProperties();
$propertyMappingConfiguration->setTypeConverterOption('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true);
}
}
/***************************************************************************
* helper
**********************/
/**
* Injects the Configuration Manager and is initializing the framework settings
*
* @param ConfigurationManagerInterface $configurationManager Instance of the Configuration Manager
*
* @return void
*/
public function buildSettings()
{
$tsSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
'news',
'news_pi1'
);
$originalSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS
);
$propertiesNotAllowedViaFlexForms = ['orderByAllowed'];
foreach ($propertiesNotAllowedViaFlexForms as $property) {
$originalSettings[$property] = ($tsSettings['settings'] ?? [])[$property] ?? ($originalSettings[$property] ?? '');
}
$this->originalSettings = $originalSettings;
// Use stdWrap for given defined settings
if (isset($originalSettings['useStdWrap']) && !empty($originalSettings['useStdWrap'])) {
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
$typoScriptArray = $typoScriptService->convertPlainArrayToTypoScriptArray($originalSettings);
$stdWrapProperties = GeneralUtility::trimExplode(',', $originalSettings['useStdWrap'], true);
foreach ($stdWrapProperties as $key) {
if (is_array($typoScriptArray[$key . '.'])) {
$originalSettings[$key] = $this->configurationManager->getContentObject()->stdWrap(
$typoScriptArray[$key],
$typoScriptArray[$key . '.']
);
}
}
}
// start override
if (isset($tsSettings['settings']['overrideFlexformSettingsIfEmpty'])) {
$typoScriptUtility = GeneralUtility::makeInstance(TypoScript::class);
$originalSettings = $typoScriptUtility->override($originalSettings, $tsSettings);
}
foreach ($hooks = ($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Controller/NewsController.php']['overrideSettings'] ?? []) as $_funcRef) {
$_params = [
'originalSettings' => $originalSettings,
'tsSettings' => $tsSettings,
];
$originalSettings = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
}
$this->settings = $originalSettings;
}
/**
* Injects a view.
* This function is for testing purposes only.
*
* @param \TYPO3\CMS\Fluid\View\TemplateView $view the view to inject
*
* @return void
*/
public function setView(TemplateView $view): void
{
$this->view = $view;
}
}
@@ -0,0 +1,50 @@
<?php
namespace GeorgRinger\News\Controller;
use GeorgRinger\News\Event\TagListActionEvent;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Tag controller
*/
class TagController extends NewsController
{
/**
* List tags
*
* @param array $overwriteDemand
*
* @return void
*/
public function listAction(array $overwriteDemand = null)
{
// Default value is wrong for tags
if ($this->settings['orderBy'] === 'datetime') {
unset($this->settings['orderBy']);
}
$demand = $this->createDemandObjectFromSettings($this->settings);
$demand->setActionAndClass(__METHOD__, __CLASS__);
if ($overwriteDemand !== null && $this->settings['disableOverrideDemand'] != 1) {
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
}
$assignedValues = [
'tags' => $this->tagRepository->findDemanded($demand),
'overwriteDemand' => $overwriteDemand,
'demand' => $demand,
];
$event = $this->eventDispatcher->dispatch(new TagListActionEvent($this, $assignedValues));
$this->view->assignMultiple($event->getAssignedValues());
}
}