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']; } }