diff --git a/Classes/Controller/BackendController.php b/Classes/Controller/BackendController.php index a9afd3a4aa89c69fdc4eab2911bd13d3f039080a..ba3e6955fa39c1a74ac77777e0d1a4f028672469 100644 --- a/Classes/Controller/BackendController.php +++ b/Classes/Controller/BackendController.php @@ -26,12 +26,20 @@ namespace SGalinski\SgNews\Controller; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use SGalinski\SgNews\Service\Backend\Utility; +use In2code\Powermail\Utility\LocalizationUtility; +use SGalinski\SgNews\Service\BackendNewsUtility; +use SGalinski\SgNews\Service\LicensingService; +use TYPO3\CMS\Backend\Clipboard\Clipboard; +use TYPO3\CMS\Backend\Template\Components\ButtonBar; use TYPO3\CMS\Backend\Template\Components\DocHeaderComponent; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\VersionNumberUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; +use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; /** * News Controller @@ -39,55 +47,252 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; class BackendController extends ActionController { /** - * DocHeaderComponent + * The uid of the current page * - * @var DocHeaderComponent + * @var int */ - protected $docHeaderComponent; + private $pageUid = 0; /** - * @var \SGalinski\SgNews\Domain\Repository\NewsRepository - * @inject + * The info fields of the current page + * + * @var array */ - private $newsRepository; + private $pageInfo = []; /** - * @var \SGalinski\SgNews\Domain\Repository\CategoryRepository - * @inject + * The uid of the current root page + * + * @var int */ - private $categoryRepository; + private $rootPageUid = 0; /** - * @var \SGalinski\SgNews\Domain\Repository\TagRepository - * @inject + * Command array on the form [tablename][uid][command] = value. + * This array may get additional data set internally based on clipboard commands send in CB var! + * + * @var array */ - private $tagRepository; + private $command; /** - * @param array $filters + * Clipboard command array. May trigger changes in "cmd" + * + * @var array + */ + private $clipboardCommandArray; + + /** + * Currently selected language + * + * @var int + */ + public $language; + + /** + * DocHeaderComponent + * + * @var DocHeaderComponent + */ + private $docHeaderComponent; + + /** + * Initialize action for all actions + * * @throws \InvalidArgumentException + */ + public function initializeAction() { + $this->command = GeneralUtility::_GP('cmd'); + $this->clipboardCommandArray = GeneralUtility::_GP('CB'); + $this->initClipboard(); + } + + /** + * 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 ViewInterface $view The view to be initialized + * + * @return void * @throws \UnexpectedValueException - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException + * @throws \InvalidArgumentException + * @api */ - public function indexAction(array $filters = []) { + protected function initializeView(ViewInterface $view) { // create doc header component - $pageUid = (int) GeneralUtility::_GP('id'); + $this->pageUid = (int) GeneralUtility::_GP('id'); /** @var BackendUserAuthentication $backendUser */ $backendUser = $GLOBALS['BE_USER']; - $pageInfo = BackendUtility::readPageAccess($pageUid, $backendUser->getPagePermsClause(1)); - $this->docHeaderComponent = GeneralUtility::makeInstance(DocHeaderComponent::class); - $this->docHeaderComponent->setMetaInformation($pageInfo); - Utility::makeButtons($this->docHeaderComponent, $this->request); - - // retrieve next site root id - $siteRootId = Utility::getSiteRoot((int) GeneralUtility::_GP('id')); - $categories = Utility::getCategoriesForSiteRoot($siteRootId); - $news = Utility::getAllNewsByCategories($categories, $filters); - - $this->view->assign('docHeader', $this->docHeaderComponent->docHeaderContent()); - $this->view->assign('pageUid', $pageUid); - $this->view->assign('categories', $categories); - $this->view->assign('news', $news); - $this->view->assign('filters', $filters); + $this->pageInfo = BackendUtility::readPageAccess($this->pageUid, $backendUser->getPagePermsClause(1)); + if ($this->pageInfo) { + $this->docHeaderComponent = GeneralUtility::makeInstance(DocHeaderComponent::class); + if ($this->pageUid) { + $this->rootPageUid = BackendNewsUtility::getRootUidByPageUid($this->pageUid); + } + + /** @var BackendUserAuthentication $backendUser */ + $backendUser = $GLOBALS['BE_USER']; + + $menuSettings = GeneralUtility::_GP('SET') ?: []; + foreach ($menuSettings as $key => $menuSetting) { + $backendUser->pushModuleData('tools_beuser/index.php/web_SgNewsNews_' . $key, $menuSetting); + } + $this->language = $backendUser->getModuleData( + 'tools_beuser/index.php/web_SgNewsNews_language', 'ses' + ) ?: 0; + + $this->docHeaderComponent->setMetaInformation($this->pageInfo); + $this->makeButtons(); + $this->makeLanguageMenu(); + $this->view->assign('pageUid', $this->pageUid); + $this->view->assign('rootPageUid', $this->rootPageUid); + $this->view->assign('docHeader', $this->docHeaderComponent->docHeaderContent()); + $this->view->assign('typo3Version', VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version)); + + if (!LicensingService::checkKey()) { + $this->view->assign('showLicenseBanner', TRUE); + } + } + } + + /** + * @param array $filters + * @throws \InvalidArgumentException + * @throws \UnexpectedValueException + * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException + * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception + * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidNumberOfConstraintsException + * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException + */ + public function indexAction(array $filters = NULL) { + $showNewsList = FALSE; + if ( + ($this->pageUid && $this->pageUid === $this->rootPageUid) || + (int) $this->pageInfo['doktype'] === BackendNewsUtility::CATEGORY_DOKTYPE + ) { + /** @var BackendUserAuthentication $backendUser */ + $backendUser = $GLOBALS['BE_USER']; + if ($filters === NULL) { + $filters = $backendUser->getModuleData('tools_beuser/index.php/web_SgNewsNews_filters', 'ses') ?: []; + } else { + $backendUser->pushModuleData('tools_beuser/index.php/web_SgNewsNews_filters', $filters); + } + if ((int) $this->pageInfo['doktype'] !== BackendNewsUtility::CATEGORY_DOKTYPE) { + $categories = BackendNewsUtility::getCategoriesForSiteRoot($this->rootPageUid); + $this->view->assign('categories', $categories); + $this->view->assign('showCategoryFilter', TRUE); + } else { + $filters['categories'] = [$this->pageUid]; + } + $tags = BackendNewsUtility::getTagsForPage($this->pageUid, $this->language); + $news = BackendNewsUtility::getNewsByFilters($this->rootPageUid, $filters, $this->language); + + $this->view->assign('tags', $tags); + $this->view->assign('news', $news); + $this->view->assign('filters', $filters); + $showNewsList = TRUE; + } else { + $alternativePageOptions = BackendNewsUtility::getAlternativePageOptions(); + $this->view->assign('alternativePageOptions', $alternativePageOptions); + } + $this->view->assign('showNewsList', $showNewsList); + } + + /** + * create buttons for the backend module header + * + * @return void + * @throws \InvalidArgumentException + * @throws \UnexpectedValueException + */ + private function makeButtons() { + /** @var ButtonBar $buttonBar */ + $buttonBar = $this->docHeaderComponent->getButtonBar(); + + /** @var IconFactory $iconFactory */ + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + + // Refresh + $refreshButton = $buttonBar->makeLinkButton() + ->setHref(GeneralUtility::getIndpEnv('REQUEST_URI')) + ->setTitle(LocalizationUtility::translate('LLL:EXT:lang/locallang_core.xlf:labels.reload', '')) + ->setIcon($iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL)); + $buttonBar->addButton($refreshButton, ButtonBar::BUTTON_POSITION_RIGHT); + + // shortcut button + $shortcutButton = $buttonBar->makeShortcutButton() + ->setModuleName($this->request->getPluginName()) + ->setGetVariables( + [ + 'id', + 'M' + ] + ) + ->setSetVariables([]); + $buttonBar->addButton($shortcutButton, ButtonBar::BUTTON_POSITION_RIGHT); + + $this->docHeaderComponent->getButtonBar(); + } + + /** + * create buttons for the backend module header + * + * @return void + * @throws \InvalidArgumentException + */ + private function makeLanguageMenu() { + if (!LicensingService::checkKey()) { + return; + } + $languageMenu = $this->docHeaderComponent->getMenuRegistry()->makeMenu(); + $languageMenu->setIdentifier('languageMenu'); + $languages = BackendNewsUtility::getAvailableLanguages($this->pageUid); + foreach ($languages as $key => $language) { + $menuItem = $languageMenu + ->makeMenuItem() + ->setTitle($language['title']) + ->setHref( + BackendUtility::getModuleUrl('web_SgNewsNews') . '&id=' . $this->pageUid . '&SET[language]=' . $key + ); + if ((int) $this->language === $key) { + $menuItem->setActive(TRUE); + } + $languageMenu->addMenuItem($menuItem); + } + $this->docHeaderComponent->getMenuRegistry()->addMenu($languageMenu); + } + + /** + * Clipboard pasting and deleting. + * + * @throws \InvalidArgumentException + */ + public function initClipboard() { + if (is_array($this->clipboardCommandArray)) { + $clipObj = GeneralUtility::makeInstance(Clipboard::class); + $clipObj->initializeClipboard(); + if ($this->clipboardCommandArray['paste']) { + $clipObj->setCurrentPad($this->clipboardCommandArray['pad']); + $this->command = $clipObj->makePasteCmdArray( + $this->clipboardCommandArray['paste'], + $this->command, + $this->clipboardCommandArray['update'] ?? NULL + ); + } + + if ($this->clipboardCommandArray['delete']) { + $clipObj->setCurrentPad($this->clipboardCommandArray['pad']); + $this->command = $clipObj->makeDeleteCmdArray($this->command); + } + + if ($this->clipboardCommandArray['el']) { + $this->clipboardCommandArray['setP'] = 'normal'; + $clipObj->setCmd($this->clipboardCommandArray); + $clipObj->cleanCurrent(); + $clipObj->endClipboard(); + } + } } } diff --git a/Classes/Hooks/EditDocumentController.php b/Classes/Hooks/EditDocumentController.php new file mode 100644 index 0000000000000000000000000000000000000000..879bdeefd40130bee329774d5436d01734d9c3bc --- /dev/null +++ b/Classes/Hooks/EditDocumentController.php @@ -0,0 +1,62 @@ +<?php + +namespace SGalinski\SgNews\Hooks; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use SGalinski\SgNews\Service\BackendNewsUtility; +use TYPO3\CMS\Backend\Utility\BackendUtility; + +/** + * Backend edid form hook + */ +class EditDocumentController { + + /** + * Sets the default value for the pages author field + * to the name of the logged-in user + * + * @param \TYPO3\CMS\Backend\Controller\EditDocumentController $controller Default configuration + * @return void + */ + public function preInitAfter($controller) { + if (isset($controller->editconf['pages']) && $GLOBALS['BE_USER']->user['realName'] !== '') { + $doktype = 0; + foreach ($controller->editconf['pages'] as $pageUid => $command) { + if ($command === 'edit') { + $pageRow = BackendUtility::getRecord('pages', (int) $pageUid); + if ($pageRow && isset($pageRow['doktype'])) { + $doktype = (int) $pageRow['doktype']; + } + } elseif ($command === 'new' && isset($controller->overrideVals['pages']['doktype'])) { + $doktype = (int) $controller->overrideVals['pages']['doktype']; + } + break; + } + if ($doktype === BackendNewsUtility::NEWS_DOKTYPE) { + $GLOBALS['BE_USER']->userTS['TCAdefaults.']['pages.']['author'] = $GLOBALS['BE_USER']->user['realName']; + } + } + } +} diff --git a/Classes/Service/Backend/Utility.php b/Classes/Service/Backend/Utility.php deleted file mode 100644 index a14fef70a1561f0260a2373381716b7c19be513c..0000000000000000000000000000000000000000 --- a/Classes/Service/Backend/Utility.php +++ /dev/null @@ -1,175 +0,0 @@ -<?php - -namespace SGalinski\SgNews\Service\Backend; - -/*************************************************************** - * Copyright notice - * - * (c) sgalinski Internet Services (https://www.sgalinski.de) - * - * All rights reserved - * - * This script is part of the TYPO3 project. The TYPO3 project is - * free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * The GNU General Public License can be found at - * http://www.gnu.org/copyleft/gpl.html. - * - * This script is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This copyright notice MUST APPEAR in all copies of the script! - ***************************************************************/ - -use SGalinski\SgNews\Domain\Repository\NewsRepository; -use TYPO3\CMS\Backend\Template\Components\ButtonBar; -use TYPO3\CMS\Backend\Template\Components\DocHeaderComponent; -use TYPO3\CMS\Backend\Utility\BackendUtility; -use TYPO3\CMS\Core\Database\DatabaseConnection; -use TYPO3\CMS\Core\Database\QueryGenerator; -use TYPO3\CMS\Core\Imaging\Icon; -use TYPO3\CMS\Core\Imaging\IconFactory; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Mvc\Request; -use TYPO3\CMS\Extbase\Object\ObjectManager; -use TYPO3\CMS\Extbase\Utility\LocalizationUtility; - -/** - * Class Utility - */ -class Utility { - const CATEGORY_DOKTYPE = 117; - const NEWS_DOKTYPE = 117; - - /** - * create buttons for the backend module header - * - * @param $docHeaderComponent DocHeaderComponent - * @param $request Request - * @throws \InvalidArgumentException - * @throws \UnexpectedValueException - */ - public static function makeButtons(&$docHeaderComponent, &$request) { - /** @var ButtonBar $buttonBar */ - $buttonBar = $docHeaderComponent->getButtonBar(); - - /** @var IconFactory $iconFactory */ - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - - // Refresh - $refreshButton = $buttonBar->makeLinkButton() - ->setHref(GeneralUtility::getIndpEnv('REQUEST_URI')) - ->setTitle(LocalizationUtility::translate('LLL:EXT:lang/locallang_core.xlf:labels.reload', '')) - ->setIcon($iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL)); - $buttonBar->addButton($refreshButton, ButtonBar::BUTTON_POSITION_RIGHT); - - // shortcut button - $shortcutButton = $buttonBar->makeShortcutButton() - ->setModuleName($request->getPluginName()) - ->setGetVariables( - [ - 'id', - 'M' - ] - ) - ->setSetVariables([]); - - $buttonBar->addButton($shortcutButton, ButtonBar::BUTTON_POSITION_RIGHT); - $docHeaderComponent->getButtonBar(); - } - - /** - * Retrieves the next site root in the page hierarchy from the current page - * - * @param int $currentPid - * @return int - */ - public static function getSiteRoot($currentPid) { - $rootLine = BackendUtility::BEgetRootLine((int) $currentPid); - $siteRoot = ['uid' => 0]; - - foreach ($rootLine as $page) { - if ($page['is_siteroot'] === '1') { - $siteRoot = $page; - break; - } - } - - return $siteRoot['uid']; - } - - /** - * Get an array of all category uids => >titles below the given site root id - * - * @param $siteRootPid - * @throws \InvalidArgumentException - * @return array - */ - public static function getCategoriesForSiteRoot($siteRootPid) { - // get all pageids below the given siteroot - $queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class); - $childPids = $queryGenerator->getTreeList($siteRootPid, PHP_INT_MAX, 0, 1); - - // if doktype = 117 (category) then get the category name (page title) - /** @var DatabaseConnection $databaseConnection */ - $databaseConnection = $GLOBALS['TYPO3_DB']; - - $where = 'doktype = ' . self::CATEGORY_DOKTYPE . ' AND uid in (' . $childPids . ')'; - $result = $databaseConnection->exec_SELECTquery('uid, title', 'pages', $where)->fetch_all(); - $categories = []; - /** @var array $result */ - foreach ($result as $item) { - $categories[$item[0]] = $item[1]; - } - - return $categories; - } - - /** - * Get all news for the given categories (all if filter is empty) - * - * @param array $categories - * @param array $filters - * @return array - * @throws \InvalidArgumentException - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException - */ - public static function getAllNewsByCategories(array $categories = [], array $filters = []) { - $objectManager = GeneralUtility::makeInstance(ObjectManager::class); - /** @var NewsRepository $newsRepository */ - $newsRepository = $objectManager->get(NewsRepository::class); - - // filter by category if set - if (!empty($filters['filters']['categories'])) { - foreach ($categories as $key => $value) { - if (is_array($filters['filters']['categories'])) { - $unset = TRUE; - foreach($filters['filters']['categories'] as $category) { - if ($key == (int) $category) { - $unset = FALSE; - } - } - - if ($unset) { - unset($categories[$key]); - } - } - } - } - - $categoryIds = []; - foreach ($categories as $key => $value) { - $categoryIds[] = $key; - } - - if (empty($categoryIds)) { - return []; - } - return $newsRepository->findAllSortedNewsByCategories($categoryIds, 0, 0, 'date', NULL, $raw = TRUE); - } -} diff --git a/Classes/Service/BackendNewsUtility.php b/Classes/Service/BackendNewsUtility.php new file mode 100644 index 0000000000000000000000000000000000000000..a2eade64dc67d3b5639c2d0ae6d435da030dcd6c --- /dev/null +++ b/Classes/Service/BackendNewsUtility.php @@ -0,0 +1,410 @@ +<?php + +namespace SGalinski\SgNews\Service; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use SGalinski\SgNews\Domain\Repository\TagRepository; +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Database\DatabaseConnection; +use TYPO3\CMS\Core\Database\QueryGenerator; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Persistence\QueryInterface; +use TYPO3\CMS\Extbase\Utility\LocalizationUtility; + +/** + * Class Utility + */ +class BackendNewsUtility { + + /** + * @var int The category page doktype + */ + const CATEGORY_DOKTYPE = 117; + + /** + * @var int The news page doktype + */ + const NEWS_DOKTYPE = 116; + + /** + * Retrieves the next site root in the page hierarchy from the current page + * + * @param int $currentPid + * @return int + */ + public static function getRootUidByPageUid($currentPid) { + $rootLine = BackendUtility::BEgetRootLine((int) $currentPid); + $siteRoot = ['uid' => 0]; + + foreach ($rootLine as $page) { + if ((int) $page['is_siteroot'] === 1) { + $siteRoot = $page; + break; + } + } + + return (int) $siteRoot['uid']; + } + + /** + * Get an array of alternative pages for the BE Module view + * + * @return array + * @throws \InvalidArgumentException + */ + public static function getAlternativePageOptions() { + $options = []; + /** @var array $rootOptionRows */ + $rootOptionRows = BackendUtility::getRecordsByField('pages', 'is_siteroot', 1, '', '', 'sorting'); + if ($rootOptionRows) { + foreach ($rootOptionRows as $row) { + $pageInfo = BackendUtility::readPageAccess($row['uid'], $GLOBALS['BE_USER']->getPagePermsClause(1)); + if ($pageInfo) { + $options[] = self::getOptionPageInfo($pageInfo); + } + $categories = self::getCategoriesForSiteRoot((int) $row['uid']); + /** @var int $categoryUid */ + foreach ($categories as $categoryUid => $categoryTitle) { + if ((int) $pageInfo['uid'] !== $categoryUid) { + $categoryPageInfo = BackendUtility::readPageAccess( + $categoryUid, $GLOBALS['BE_USER']->getPagePermsClause(1) + ); + if ($categoryPageInfo) { + $options[] = self::getOptionPageInfo($categoryPageInfo); + } + } + } + } + } + return $options; + } + + /** + * Get an array of alternative pages for the BE Module view + * + * @param array $pageInfo + * @return array + */ + private static function getOptionPageInfo(array $pageInfo = []) { + if (isset($pageInfo['uid']) && (int) $pageInfo['uid']) { + $rootline = BackendUtility::BEgetRootLine($pageInfo['uid'], '', TRUE); + ksort($rootline); + $path = '/root'; + foreach ($rootline as $page) { + $path .= '/p' . dechex($page['uid']); + } + $pageInfo['path'] = $path; + $pageInfo['_thePathFull'] = substr($pageInfo['_thePathFull'], 1); + $pageInfo['_thePathFull'] = substr($pageInfo['_thePathFull'], 0, -1); + } + return $pageInfo; + } + + /** + * Get an array of all category uids => titles below the given site root id + * + * @param int $siteRootUid + * @return array + * @throws \InvalidArgumentException + */ + public static function getCategoriesForSiteRoot($siteRootUid) { + $siteRootUid = (int) $siteRootUid; + // get all pageids below the given siteroot + $queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class); + $childPids = $queryGenerator->getTreeList( + $siteRootUid, PHP_INT_MAX, 0, $GLOBALS['BE_USER']->getPagePermsClause(1) + ); + + // if doktype = 117 (category) then get the category name (page title) + /** @var DatabaseConnection $databaseConnection */ + $databaseConnection = $GLOBALS['TYPO3_DB']; + + $where = 'deleted = 0 AND doktype = ' . self::CATEGORY_DOKTYPE . ' AND uid in (' . $childPids . ')'; + $result = $databaseConnection->exec_SELECTgetRows('uid, title', 'pages', $where); + $categories = []; + /** @var array $result */ + foreach ($result as $page) { + $categoryPageInfo = BackendUtility::readPageAccess( + (int) $page['uid'], $GLOBALS['BE_USER']->getPagePermsClause(1) + ); + if ($categoryPageInfo) { + $categories[(int) $page['uid']] = $page['title']; + } + } + + return $categories; + } + + /** + * Get an array of all tags uids => titles below the given site root id + * + * @param int $pageUid + * @param int $languageUid + * @return array + * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException + * @throws \InvalidArgumentException + */ + public static function getTagsForPage($pageUid, $languageUid = 0) { + $temporaryTSFEInstance = FALSE; + if (!isset($GLOBALS['TSFE'])) { + $temporaryTSFEInstance = TRUE; + $GLOBALS['TSFE'] = new \stdClass(); + $GLOBALS['TSFE']->gr_list = ''; + } + $pageUid = (int) $pageUid; + $languageUid = (int) $languageUid; + $tags = []; + $objectManager = GeneralUtility::makeInstance(ObjectManager::class); + $tagRepository = $objectManager->get(TagRepository::class); + $query = $tagRepository->createQuery(); + $querySettings = $query->getQuerySettings(); + $querySettings->setLanguageUid($languageUid); + $querySettings->setLanguageOverlayMode(TRUE); + $querySettings->setLanguageMode('content_fallback'); + $query->setQuerySettings($querySettings); + if ($pageUid) { + $rootline = BackendUtility::BEgetRootLine($pageUid, '', TRUE); + $pageTS = BackendUtility::getPagesTSconfig($pageUid, $rootline); + + $tagsPid = 0; + if (isset($pageTS['TCEFORM.']['pages.']['tx_sgnews_tags.']['PAGE_TSCONFIG_ID'])) { + $tagsPid = (int) $pageTS['TCEFORM.']['pages.']['tx_sgnews_tags.']['PAGE_TSCONFIG_ID']; + } + if ($tagsPid) { + $query->matching($query->equals('pid', $tagsPid)); + } + } + $query->setOrderings(['title' => QueryInterface::ORDER_ASCENDING]); + $resultTags = $query->execute(TRUE); + if ($temporaryTSFEInstance) { + unset($GLOBALS['TSFE']); + } + foreach ($resultTags as $tag) { + $tags[(int) $tag['uid']] = trim($tag['title']); + } + return $tags; + } + + /** + * Get all news for the given categories (all if filter is empty) + * + * @param int $rootPageUid + * @param array $filters + * @param int $languageUid + * @return array + * @throws \InvalidArgumentException + */ + public static function getNewsByFilters($rootPageUid = 0, array $filters = [], $languageUid = 0) { + $out = []; + $rootPageUid = (int) $rootPageUid; + $languageUid = (int) $languageUid; + if (!$rootPageUid) { + return $out; + } + $categories = []; + if (!isset($filters['categories']) || !is_array($filters['categories']) || !count($filters['categories'])) { + $rootCategories = self::getCategoriesForSiteRoot($rootPageUid); + foreach ($rootCategories as $categoryUid => $categoryTitle) { + $categories[] = (int) $categoryUid; + } + } else { + foreach ($filters['categories'] as $categoryUid) { + $categoryPageInfo = BackendUtility::readPageAccess( + (int) $categoryUid, $GLOBALS['BE_USER']->getPagePermsClause(1) + ); + if ($categoryPageInfo) { + $categories[] = (int) $categoryUid; + } + } + } + if (!count($categories)) { + return $out; + } + $queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class); + $allowedUids = []; + foreach ($categories as $categoryUid) { + $allowedUidsTemp = GeneralUtility::intExplode( + ',', $queryGenerator->getTreeList( + $categoryUid, 1, 0, $GLOBALS['BE_USER']->getPagePermsClause(1) + ), TRUE + ); + $allowedUids = array_unique(array_merge($allowedUids, $allowedUidsTemp)); + } + if (!count($allowedUids)) { + return $out; + } + + list($select, $tables, $where) = self::getNewsQueryParts($allowedUids, $filters, $languageUid); + if ($tables === '') { + return $out; + } + + /** @var DatabaseConnection $databaseConnection */ + $databaseConnection = $GLOBALS['TYPO3_DB']; + $result = $databaseConnection->exec_SELECTquery($select, $tables, $where, '`pages`.`uid`', '`pages`.`sorting`'); + while ($row = $result->fetch_assoc()) { + $out[] = $row; + } + return $out; + } + + /** + * Creates news query parts + * + * @param array $allowedUids + * @param array $filters + * @param int $languageUid + * @return array + */ + private static function getNewsQueryParts(array $allowedUids, array $filters = [], $languageUid = 0) { + $out = [0 => ' + `pages`.`uid` AS uid, + `pages`.`pid` AS pid, + `pages`.`hidden` AS hidden, + `pages`.`sorting` AS sorting, + `pages`.`doktype` AS doktype, + `pages`.`title` AS title + ', 1 => '', 2 => '']; + $out[1] = '`pages`'; + $out[2] = '`pages`.`uid` IN(' . implode(',', $allowedUids) . ') ' . BackendUtility::deleteClause('pages') . + ' AND `pages`.`doktype` = ' . self::NEWS_DOKTYPE; + if ($languageUid) { + $out[0] .= ', `translation`.`title` AS translation_title'; + $out[1] .= ' LEFT JOIN `pages_language_overlay` AS `translation` ON `translation`.`pid` = `pages`.`uid` ' . + BackendUtility::deleteClause('pages_language_overlay', 'translation') . + ' AND `translation`.`sys_language_uid` = ' . $languageUid; + } + if (isset($filters['tags']) && is_array($filters['tags']) && count($filters['tags'])) { + $tagUids = []; + foreach ($filters['tags'] as $tagUid) { + if ((int) $tagUid && !in_array((int) $tagUid, $tagUids, TRUE)) { + $tagUids[] = (int) $tagUid; + } + } + if (count($tagUids)) { + $out[1] .= ' INNER JOIN `sys_category_record_mm` AS `tag` ON `tag`.`tablenames` = \'pages\'' . + ' AND `tag`.`fieldname` = \'tx_sgnews_tags\' AND `tag`.`uid_foreign` = `pages`.`uid`' . + ' AND `tag`.`uid_local` IN (' . implode(',', $filters['tags']) . ')'; + } + } + if (isset($filters['search']) && trim($filters['search'])) { + $out[2] .= self::getNewsSearchClause(trim($filters['search']), $languageUid); + } + + return $out; + } + + /** + * Creates constraints of query for searching news by search-word + * + * @param string $searchString + * @param int $languageUid + * @return string + */ + private static function getNewsSearchClause($searchString = '', $languageUid = 0) { + $out = ''; + $searchString = strtolower(trim($searchString)); + $languageUid = (int) $languageUid; + if (!$searchString) { + return $out; + } + $out = ' AND ('; + /** @var DatabaseConnection $databaseConnection */ + $databaseConnection = $GLOBALS['TYPO3_DB']; + $likeString = 'LIKE \'%' . $databaseConnection->escapeStrForLike($searchString, 'pages') . '%\''; + $constraints = []; + $pageFields = [ + 'title' => TRUE, + 'description' => TRUE, + 'author' => TRUE, + 'abstract' => TRUE, + ]; + foreach ($pageFields as $fieldName => $isCommonField) { + if ($isCommonField) { + $constraints[] = 'LOWER(`pages`.`' . $fieldName . '`) ' . $likeString; + } + } + if (!$languageUid) { + foreach ($pageFields as $fieldName => $isCommonField) { + if (!$isCommonField) { + $constraints[] = 'LOWER(`pages`.`' . $fieldName . '`) ' . $likeString; + } + } + } else { + foreach ($pageFields as $fieldName => $isCommonField) { + if ($isCommonField) { + $constraints[] = 'LOWER(`translation`.`' . $fieldName . '`) ' . $likeString; + } + } + } + $out .= implode(' OR ', $constraints) . ')'; + return $out; + } + + /** + * Returns the available languages for the current BE user + * + * @param int $pageUid + * @return array + * @throws \InvalidArgumentException + */ + public static function getAvailableLanguages($pageUid = 0) { + $pageUid = (int) $pageUid; + $rootline = BackendUtility::BEgetRootLine($pageUid, '', TRUE); + $defaultLanguage = LocalizationUtility::translate('backend.language.default', 'SgNews'); + $pageTS = BackendUtility::getPagesTSconfig($pageUid, $rootline); + if (isset($pageTS['mod.']['SHARED.']['defaultLanguageLabel'])) { + $defaultLanguage = $pageTS['mod.']['SHARED.']['defaultLanguageLabel'] . ' (' . $defaultLanguage . ')'; + } + $defaultLanguageFlag = 'empty-empty'; + if (isset($pageTS['mod.']['SHARED.']['defaultLanguageFlag'])) { + $defaultLanguageFlag = 'flags-' . $pageTS['mod.']['SHARED.']['defaultLanguageFlag']; + } + $languages = [ + 0 => ['title' => $defaultLanguage, 'flag' => $defaultLanguageFlag] + ]; + /** @var DatabaseConnection $databaseConnection */ + $databaseConnection = $GLOBALS['TYPO3_DB']; + $languageRows = $databaseConnection->exec_SELECTgetRows( + 'uid, title, flag', 'sys_language', 'hidden = 0', '', 'sorting' + ); + if ($languageRows) { + /** @var BackendUserAuthentication $backendUser */ + $backendUser = $GLOBALS['BE_USER']; + foreach ($languageRows as $languageRow) { + if ($backendUser->checkLanguageAccess($languageRow['uid'])) { + $languages[(int) $languageRow['uid']] = [ + 'title' => $languageRow['title'], + 'flag' => 'flags-' . $languageRow['flag'], + ]; + } + } + } + return $languages; + } +} diff --git a/Classes/Service/LicensingService.php b/Classes/Service/LicensingService.php new file mode 100644 index 0000000000000000000000000000000000000000..92b72fefd8732f818548eb7f8bd1ec29fc60bae6 --- /dev/null +++ b/Classes/Service/LicensingService.php @@ -0,0 +1,115 @@ +<?php + +namespace SGalinski\SgNews\Service; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Class SGalinski\SgNews\Service\LicensingService + */ +class LicensingService { + /** + * Licensing Service Url + */ + const URL = 'https://www.sgalinski.de/?eID=sgLicensing'; + + /** + * Licensing Service Url + */ + const EXTENSION_KEY = 'sg_news'; + + /** @var bool|NULL */ + private static $isLicenseKeyValid; + + /** + * @return boolean + */ + public static function checkKey(): bool { + if (static::$isLicenseKeyValid === NULL) { + static::$isLicenseKeyValid = FALSE; + $configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][self::EXTENSION_KEY], [FALSE]); + if (isset($configuration['key']) && $key = trim($configuration['key'])) { + static::$isLicenseKeyValid = (bool) preg_match('/^([A-Z\d]{6}-?){4}$/', $key); + } + } + + return static::$isLicenseKeyValid; + } + + /** + * Licensing Service ping + * + * @param boolean $returnUrl + * @return string + */ + public static function ping($returnUrl = FALSE): string { + try { + $configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][self::EXTENSION_KEY], [FALSE]); + $key = ''; + if (isset($configuration['key'])) { + $key = trim($configuration['key']); + } + $params = [ + 'extension' => self::EXTENSION_KEY, + 'host' => GeneralUtility::getIndpEnv('HTTP_HOST'), + 'key' => $key + ]; + $params = http_build_query($params); + $pingUrl = self::URL; + $pingUrl .= $params !== '' ? (strpos($pingUrl, '?') === FALSE ? '?' : '&') . $params : ''; + if ($returnUrl) { + return $pingUrl; + } + + GeneralUtility::getUrl($pingUrl); + } catch (\Exception $exception) { + } + + return ''; + } + + /** + * Generates a random password string based on the configured password policies. + * + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return ResponseInterface + * @throws \InvalidArgumentException + */ + public function ajaxPing(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { + /** @var BackendUserAuthentication $backendUser */ + $backendUser = $GLOBALS['BE_USER']; + if ($backendUser && !$backendUser->getModuleData('tools_beuser/index.php/web_SgNewsNews_pinged', 'ses')) { + $backendUser->pushModuleData('tools_beuser/index.php/web_SgNewsNews_pinged', TRUE); + self::ping(); + } + return $response; + } +} diff --git a/Classes/ViewHelpers/AbstractViewHelper.php b/Classes/ViewHelpers/AbstractViewHelper.php index 5690834bba7665ef58b98b829bde03955b5d734c..a99506e454bcf2ca7c5b86ff6bd3c4703587ed49 100644 --- a/Classes/ViewHelpers/AbstractViewHelper.php +++ b/Classes/ViewHelpers/AbstractViewHelper.php @@ -32,6 +32,17 @@ use TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper; * Abstract view helper */ class AbstractViewHelper extends AbstractBackendViewHelper { + + /** + * @var boolean + */ + protected $escapeOutput = FALSE; + + /** + * @var boolean + */ + protected $escapeChildren = FALSE; + /** * Returns the base url of the site * diff --git a/Classes/ViewHelpers/Backend/ControlViewHelper.php b/Classes/ViewHelpers/Backend/ControlViewHelper.php index 060badb345c768672226fced8961afaa8819b603..3e7c5da4cbb726f50ddaefa60d3741d135b14ebd 100644 --- a/Classes/ViewHelpers/Backend/ControlViewHelper.php +++ b/Classes/ViewHelpers/Backend/ControlViewHelper.php @@ -26,10 +26,15 @@ namespace SGalinski\SgNews\ViewHelpers\Backend; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use SGalinski\SgNews\Service\LicensingService; +use SGalinski\SgNews\ViewHelpers\AbstractViewHelper; +use TYPO3\CMS\Backend\Clipboard\Clipboard; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3\CMS\Extbase\Utility\LocalizationUtility; use TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList; /** @@ -41,17 +46,49 @@ class ControlViewHelper extends AbstractViewHelper { * * @param string $table * @param mixed $row + * @param array $sortingData + * @param boolean $clipboard * @return string * @throws \InvalidArgumentException * @throws \UnexpectedValueException */ - public function render($table, $row) { + public function render($table, $row, array $sortingData = [], $clipboard = FALSE) { + if (!is_array($row)) { + $row = BackendUtility::getRecord($table, $row->getUid()); + } /** @var DatabaseRecordList $databaseRecordList */ $databaseRecordList = GeneralUtility::makeInstance(DatabaseRecordList::class); /** @var BackendUserAuthentication $backendUser */ $backendUser = $GLOBALS['BE_USER']; $pageInfo = BackendUtility::readPageAccess($row['pid'], $backendUser->getPagePermsClause(1)); $databaseRecordList->calcPerms = $GLOBALS['BE_USER']->calcPerms($pageInfo); - return $databaseRecordList->makeControl($table, $row); + $databaseRecordList->currentTable = $sortingData; + $out = $databaseRecordList->makeControl($table, $row); + if ($clipboard) { + $databaseRecordList->MOD_SETTINGS['clipBoard'] = TRUE; + $databaseRecordList->clipObj = GeneralUtility::makeInstance(Clipboard::class); + $databaseRecordList->clipObj->initializeClipboard(); + $GLOBALS['SOBE'] = $databaseRecordList; + $out .= $databaseRecordList->makeClip($table, $row); + } + if ($table === 'pages' && LicensingService::checkKey()) { + $rootline = BackendUtility::BEgetRootLine($row['uid'], '', TRUE); + ksort($rootline); + $path = '/root'; + foreach ($rootline as $page) { + $path .= '/p' . dechex($page['uid']); + } + + /** @var IconFactory $iconFactory */ + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $out .= ' <div class="btn-group" role="group"> + <a href="#" onclick="return sgNewsGoToPageModule(' . $row['uid'] . ', \'' . $path . '\');" class="btn btn-default" title="' . LocalizationUtility::translate( + 'backend.button.editPageContent', 'SgNews' + ) . '">' . + $iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . + '</a> + </div>'; + } + return $out; } } diff --git a/Classes/ViewHelpers/Backend/EditOnClickViewHelper.php b/Classes/ViewHelpers/Backend/EditOnClickViewHelper.php index 70d0fdd7e1ed35a5b1bd52b0642c2520027bd4f5..fb6cda9d73f2f5d26870bccadc0c5ee6ade2ff43 100644 --- a/Classes/ViewHelpers/Backend/EditOnClickViewHelper.php +++ b/Classes/ViewHelpers/Backend/EditOnClickViewHelper.php @@ -26,8 +26,9 @@ namespace SGalinski\SgNews\ViewHelpers\Backend; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use SGalinski\SgNews\Service\BackendNewsUtility; +use SGalinski\SgNews\ViewHelpers\AbstractViewHelper; use TYPO3\CMS\Backend\Utility\BackendUtility; -use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; /** * Class EditOnClickViewHelper @@ -39,9 +40,17 @@ class EditOnClickViewHelper extends AbstractViewHelper { * @param string $table * @param int $uid * @param boolean $new + * @param string $type * @return string */ - public function render($table, $uid, $new = FALSE) { - return BackendUtility::editOnClick('&edit[' . $table . '][' . $uid . ']=' . ($new ? 'new' : 'edit'), '', -1); + public function render($table, $uid, $new = FALSE, $type = '') { + $additionalParameters = ''; + if ($new && $table === 'pages' && in_array($type, ['news', 'category'], TRUE)) { + $additionalParameters = '&overrideVals[pages][doktype]=' . + ($type === 'news' ? BackendNewsUtility::NEWS_DOKTYPE : BackendNewsUtility::CATEGORY_DOKTYPE); + } + return BackendUtility::editOnClick( + '&edit[' . $table . '][' . $uid . ']=' . ($new ? 'new' : 'edit') . $additionalParameters, '', -1 + ); } } diff --git a/Classes/ViewHelpers/Backend/IconViewHelper.php b/Classes/ViewHelpers/Backend/IconViewHelper.php index a27a54aa279fc7a8eaabeaa4570a1ea78ace1981..cd792ce2ddd665b749651a7a378d3ead8bda390c 100644 --- a/Classes/ViewHelpers/Backend/IconViewHelper.php +++ b/Classes/ViewHelpers/Backend/IconViewHelper.php @@ -26,36 +26,41 @@ namespace SGalinski\SgNews\ViewHelpers\Backend; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use TYPO3\CMS\Backend\Utility\BackendUtility; +use SGalinski\SgNews\ViewHelpers\AbstractViewHelper; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; /** * Class IconViewHelper **/ class IconViewHelper extends AbstractViewHelper { /** - * Renders the icon for the specified record + * Renders the icon for the specified identifier + * with the requested size option and overlay. * - * @param string $table - * @param mixed $row - * @param boolean $clickMenu + * @param string $id + * @param string $size + * @param string $overlayId * @return string * @throws \InvalidArgumentException */ - public function render($table, $row, $clickMenu = TRUE) { + public function render($id, $size = '', $overlayId = NULL) { + $id = trim($id); + $size = trim($size); + switch ($size) { + case 'small' : + $size = Icon::SIZE_SMALL; + break; + case 'large' : + $size = Icon::SIZE_LARGE; + break; + default : + $size = Icon::SIZE_DEFAULT; + break; + } /** @var IconFactory $iconFactory */ $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - $toolTip = BackendUtility::getRecordToolTip($row, $table); - $iconImg = '<span ' . $toolTip . '>' - . $iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render() - . '</span>'; - if ($clickMenu) { - return BackendUtility::wrapClickMenuOnIcon($iconImg, $table, $row['uid']); - } else { - return $iconImg; - } + return $iconFactory->getIcon($id, $size, $overlayId)->render(); } } diff --git a/Classes/ViewHelpers/Backend/RecordIconViewHelper.php b/Classes/ViewHelpers/Backend/RecordIconViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..3fcb96388946bf6607450f627e699dfa30042f75 --- /dev/null +++ b/Classes/ViewHelpers/Backend/RecordIconViewHelper.php @@ -0,0 +1,61 @@ +<?php + +namespace SGalinski\SgNews\ViewHelpers\Backend; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use SGalinski\SgNews\ViewHelpers\AbstractViewHelper; +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Imaging\IconFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Class IconViewHelper + **/ +class RecordIconViewHelper extends AbstractViewHelper { + /** + * Renders the icon for the specified record + * + * @param string $table + * @param mixed $row + * @param boolean $clickMenu + * @return string + * @throws \InvalidArgumentException + */ + public function render($table, $row, $clickMenu = TRUE) { + /** @var IconFactory $iconFactory */ + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $toolTip = BackendUtility::getRecordToolTip($row, $table); + $iconImg = '<span ' . $toolTip . '>' + . $iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render() + . '</span>'; + if ($clickMenu) { + return BackendUtility::wrapClickMenuOnIcon($iconImg, $table, $row['uid']); + } else { + return $iconImg; + } + } +} diff --git a/Classes/ViewHelpers/Backend/TranslationLinksViewHelper.php b/Classes/ViewHelpers/Backend/TranslationLinksViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..bf9f22d3de8ab6b0cbd1f77dbd7d50727addaffb --- /dev/null +++ b/Classes/ViewHelpers/Backend/TranslationLinksViewHelper.php @@ -0,0 +1,123 @@ +<?php + +namespace SGalinski\SgNews\ViewHelpers\Backend; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use SGalinski\SgNews\Service\BackendNewsUtility; +use SGalinski\SgNews\Service\LicensingService; +use SGalinski\SgNews\ViewHelpers\AbstractViewHelper; +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Imaging\IconFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Utility\LocalizationUtility; + +/** + * Class EditOnClickViewHelper + **/ +class TranslationLinksViewHelper extends AbstractViewHelper { + /** + * Renders the translation links for the specified record + * + * @param int $pageUid + * @param string $table + * @param int $uid + * @return string + * @throws \InvalidArgumentException + */ + public function render($pageUid = 0, $table, $uid) { + $out = ''; + if (!LicensingService::checkKey()) { + return $out; + } + $table = trim($table); + if ($table !== 'pages' && !isset($GLOBALS['TCA'][$table]['ctrl']['languageField'])) { + return $out; + } + $uid = (int) $uid; + $pageUid = (int) $pageUid; + $row = BackendUtility::getRecord($table, $uid); + if ($row) { + /** @var IconFactory $iconFactory */ + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $lanuageField = $GLOBALS['TCA'][$table]['ctrl']['languageField']; + $currentLanguageUid = $table === 'pages' ? 0 : $row[$lanuageField]; + if ($currentLanguageUid === -1) { + $languages = [ + -1 => [ + 'title' => LocalizationUtility::translate('backend.language.allLanguages', 'SgNews'), + 'flag' => 'flags-multiple' + ] + ]; + } else { + $languages = BackendNewsUtility::getAvailableLanguages($pageUid); + } + $editLabel = LocalizationUtility::translate('backend.action.edit', 'SgNews'); + $newLabel = LocalizationUtility::translate('backend.action.new', 'SgNews'); + $translationParameters = '&cmd[' . $table . '][' . $row['uid'] . '][localize]=%s'; + if ($table === 'pages') { + $translationParameters = '&edit[pages_language_overlay][' . $row['uid'] . ']=new'; + $translationParameters .= '&overrideVals[pages_language_overlay][doktype]=' . $row['doktype']; + $translationParameters .= '&overrideVals[pages_language_overlay][sys_language_uid]=%s'; + } + foreach ($languages as $languageUid => $language) { + $translatedUid = 0; + if ((int) $languageUid <= 0) { + $translationTable = $table; + $translatedUid = $uid; + } else { + $translationTable = $table === 'pages' ? 'pages_language_overlay' : $table; + $translatedRows = BackendUtility::getRecordLocalization($table, $uid, $languageUid); + if (count($translatedRows)) { + $translatedUid = (int) $translatedRows[0]['uid']; + } + } + if ($translatedUid) { + $onClick = BackendUtility::editOnClick( + '&edit[' . $translationTable . '][' . $translatedUid . ']=edit', '', -1 + ); + $out .= ' <a href="#" onclick="' . $onClick . '" title="' . $language['title'] . ' [' . $editLabel . ']" >' . + $iconFactory->getIcon($language['flag'], Icon::SIZE_SMALL)->render() . + '</a>'; + } else { + if ($table === 'pages') { + $onClick = BackendUtility::editOnClick( + sprintf($translationParameters, $languageUid), '', -1 + ); + } else { + $onClick = 'window.location.href=' . BackendUtility::getLinkToDataHandlerAction( + sprintf($translationParameters, $languageUid), -1 + ) . ' return false;'; + } + $out .= ' <a href="#" onclick="' . $onClick . '" title="' . $language['title'] . ' [' . $newLabel . ']" >' . + $iconFactory->getIcon($language['flag'], Icon::SIZE_SMALL, 'overlay-new')->render() . + '</a>'; + } + } + } + return $out; + } +} diff --git a/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php b/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php index c8bb2bdeb033e697f5510363d3dddf457cf3b5d7..350eab995d26fb44fceb1458c45db07fe0c3819d 100644 --- a/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php +++ b/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php @@ -1,4 +1,5 @@ <?php + namespace SGalinski\SgNews\ViewHelpers\Backend\Widget; /*************************************************************** @@ -24,7 +25,7 @@ namespace SGalinski\SgNews\ViewHelpers\Backend\Widget; * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use SGalinski\SgRoutes\ViewHelpers\Backend\Widget\Controller\PaginateController; +use SGalinski\SgNews\ViewHelpers\Backend\Widget\Controller\PaginateController; use TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper; /** @@ -32,7 +33,7 @@ use TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper; */ class PaginateViewHelper extends AbstractWidgetViewHelper { /** - * @var \SGalinski\SgRoutes\ViewHelpers\Backend\Widget\Controller\PaginateController + * @var \SGalinski\SgNews\ViewHelpers\Backend\Widget\Controller\PaginateController */ protected $controller; @@ -54,6 +55,7 @@ class PaginateViewHelper extends AbstractWidgetViewHelper { * @param string $as * @param array $configuration * @return string + * @throws \TYPO3\CMS\Fluid\Core\Widget\Exception\MissingControllerException */ public function render( $objects, $as, diff --git a/Configuration/Backend/AjaxRoutes.php b/Configuration/Backend/AjaxRoutes.php new file mode 100644 index 0000000000000000000000000000000000000000..6b2f02dd2cbcb14bf3ae63c935a35010921d2c9f --- /dev/null +++ b/Configuration/Backend/AjaxRoutes.php @@ -0,0 +1,8 @@ +<?php + +return [ + 'sg_news::ajaxPing' => [ + 'path' => '/sg_news/ajaxPing', + 'target' => SGalinski\SgNews\Service\LicensingService::class . '::ajaxPing', + ] +]; diff --git a/Resources/Private/Language/de.locallang.xlf b/Resources/Private/Language/de.locallang.xlf index 76b5ad0504b315dc09be5f4320a2162b1e9c3c9c..4034187b922e097665e6bb0b3ee23a1749475303 100644 --- a/Resources/Private/Language/de.locallang.xlf +++ b/Resources/Private/Language/de.locallang.xlf @@ -9,6 +9,74 @@ <authorEmail>stefan@sgalinski.de</authorEmail> </header> <body> + <trans-unit id="backend.action.edit" approved="yes"> + <source>Edit</source> + <target>Bearbeiten</target> + </trans-unit> + <trans-unit id="backend.action.new" approved="yes"> + <source>New</source> + <target>Neu</target> + </trans-unit> + <trans-unit id="backend.button.createCategory" approved="yes"> + <source>Add News Category</source> + <target>Kategorie hinzufügen</target> + </trans-unit> + <trans-unit id="backend.button.createNews" approved="yes"> + <source>Add News Record</source> + <target>News-Eintrag hinzufügen</target> + </trans-unit> + <trans-unit id="backend.button.editPageContent" approved="yes"> + <source>Edit Page Content</source> + <target>Seiteninhalt bearbeiten</target> + </trans-unit> + <trans-unit id="backend.filter" approved="yes"> + <source>Filter</source> + <target>Filter</target> + </trans-unit> + <trans-unit id="backend.filters.categories" approved="yes"> + <source>Categories</source> + <target>Kategorien</target> + </trans-unit> + <trans-unit id="backend.filters.categories.description" approved="yes"> + <source>(Use the <em>ctrl</em> or <em>cmd</em> keys to deselect an option or to select multiple options)</source> + <target>(Benutze die <em>ctrl</em> oder <em>cmd</em>-Tasten, um eine Option abzuwählen oder mehrere anzuwählen)</target> + </trans-unit> + <trans-unit id="backend.filters.search" approved="yes"> + <source>Search</source> + <target>Suche</target> + </trans-unit> + <trans-unit id="backend.filters.tags" approved="yes"> + <source>Tags</source> + <target>Tags</target> + </trans-unit> + <trans-unit id="backend.filters.tags.description" approved="yes"> + <source>(Use the <em>ctrl</em> or <em>cmd</em> keys to deselect an option or to select multiple options)</source> + <target>(Benutze die <em>ctrl</em> oder <em>cmd</em>-Tasten, um eine Option abzuwählen oder mehrere anzuwählen)</target> + </trans-unit> + <trans-unit id="backend.language.default" approved="yes"> + <source>Default</source> + <target>Default</target> + </trans-unit> + <trans-unit id="backend.licenceBannerText" approved="yes"> + <source><![CDATA[Please visit our <a class="text-primary" href="https://shop.sgalinski.de/products/" target="_blank">online shop</a> to purchase a license with all features and to remove this ad. You can enter the licence key in the extension manager configuration of sg_news. Thanks for helping our development efforts!]]></source> + <target><![CDATA[Bitte besuchen Sie unseren <a class="text-primary" href="https://shop.sgalinski.de/products/" target="_blank">Online-Shop</a>, um eine lizenzierte Version ohne diese Ad und mit allen Features zu erwerben. Sie können den Key in der Extension-Manager-Konfiguration von sg_news eingeben. Vielen Dank für die Unterstützung unserer Produkt-Entwicklungen!]]></target> + </trans-unit> + <trans-unit id="backend.licenceBannerTitle" approved="yes"> + <source>SG_NEWS Free version</source> + <target>SG_NEWS Kostenlose Version</target> + </trans-unit> + <trans-unit id="backend.selectPage" approved="yes"> + <source>Please select one of the Root or News Category pages to continue</source> + <target>Bitte wählen Sie eine der Root- oder News-Kategorieseiten aus, um fortzufahren</target> + </trans-unit> + <trans-unit id="backend.selectPageOr" approved="yes"> + <source>, or</source> + <target>, oder</target> + </trans-unit> + <trans-unit id="configuration.licenseKey" approved="yes"> + <source>License Key</source> + <target>Lizenzschlüssel</target> + </trans-unit> <trans-unit id="frontend.overview.allTabLabel" approved="yes"> <source>All</source> <target>Alle</target> diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 4f4a33c03f23e905d855566b2d0c62492bc4f7e8..b30e02559fae366f99aee81fb5696e76383c625f 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -9,6 +9,57 @@ <authorEmail>stefan@sgalinski.de</authorEmail> </header> <body> + <trans-unit id="backend.action.edit"> + <source>Edit</source> + </trans-unit> + <trans-unit id="backend.action.new"> + <source>New</source> + </trans-unit> + <trans-unit id="backend.button.createCategory"> + <source>Add News Category</source> + </trans-unit> + <trans-unit id="backend.button.createNews"> + <source>Add News Record</source> + </trans-unit> + <trans-unit id="backend.button.editPageContent"> + <source>Edit Page Content</source> + </trans-unit> + <trans-unit id="backend.filter"> + <source>Filter</source> + </trans-unit> + <trans-unit id="backend.filters.categories"> + <source>Categories</source> + </trans-unit> + <trans-unit id="backend.filters.categories.description"> + <source>(Use the <em>ctrl</em> or <em>cmd</em> keys to deselect an option or to select multiple options)</source> + </trans-unit> + <trans-unit id="backend.filters.search"> + <source>Search</source> + </trans-unit> + <trans-unit id="backend.filters.tags"> + <source>Tags</source> + </trans-unit> + <trans-unit id="backend.filters.tags.description"> + <source>(Use the <em>ctrl</em> or <em>cmd</em> keys to deselect an option or to select multiple options)</source> + </trans-unit> + <trans-unit id="backend.language.default"> + <source>Default</source> + </trans-unit> + <trans-unit id="backend.licenceBannerText"> + <source><![CDATA[Please visit our <a class="text-primary" href="https://shop.sgalinski.de/products/" target="_blank">online shop</a> to purchase a license with all features and to remove this ad. You can enter the licence key in the extension manager configuration of sg_news. Thanks for helping our development efforts!]]></source> + </trans-unit> + <trans-unit id="backend.licenceBannerTitle"> + <source>SG_NEWS Free version</source> + </trans-unit> + <trans-unit id="backend.selectPage"> + <source>Please select one of the Root or News Category pages to continue</source> + </trans-unit> + <trans-unit id="backend.selectPageOr"> + <source>, or</source> + </trans-unit> + <trans-unit id="configuration.licenseKey"> + <source>License Key</source> + </trans-unit> <trans-unit id="frontend.overview.allTabLabel"> <source>All</source> </trans-unit> diff --git a/Resources/Private/Layouts/Backend.html b/Resources/Private/Layouts/Backend.html index cea4b404f6a098af9c7b2317045d9ff5d1fb1760..093b539027a23e4a66a8fb23fcb97a5cbd73d27f 100644 --- a/Resources/Private/Layouts/Backend.html +++ b/Resources/Private/Layouts/Backend.html @@ -8,13 +8,17 @@ 2: 'TYPO3/CMS/Backend/Tooltip'}"> <sg:addJavaScriptFile javaScriptFile="{f:uri.resource(path: 'Scripts/Backend.js')}" /> - <f:format.raw> - <sg:inlineLanguageLabels labels="backend.delete_route, backend.delete_all, backend.htaccess" /> - </f:format.raw> <div class="module" data-module-id="" data-module-name=""> <div class="module-docheader t3js-module-docheader"> <div class="module-docheader-bar module-docheader-bar-navigation t3js-module-docheader-bar t3js-module-docheader-bar-navigation"> <div class="module-docheader-bar-column-left"> + <f:for each="{docHeader.menus}" as="menu"> + <f:be.menus.actionMenu additionalAttributes="{name: menu.identifier}"> + <f:for each="{menu.menuItems}" as="menuItem"> + <option value="{menuItem.href}" {f:if(condition: '{menuItem.active}', then: 'selected="selected"')}>{menuItem.title}</option> + </f:for> + </f:be.menus.actionMenu> + </f:for> </div> <div class="module-docheader-bar-column-right"> <span class="typo3-docheader-pagePath"><f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.path" />: <f:format.raw>{docHeader.metaInformation.path}</f:format.raw></span> @@ -23,9 +27,6 @@ </div> <div class="module-docheader-bar module-docheader-bar-buttons t3js-module-docheader-bar t3js-module-docheader-bar-buttons"> <div class="module-docheader-bar-column-left"> - <div class="btn-toolbar" role="toolbar" aria-label=""> - <f:render section="iconButtons" /> - </div> </div> <div class="module-docheader-bar-column-right"> <f:render partial="ButtonBar" arguments="{buttons:docHeader.buttons.right}" /> @@ -35,6 +36,9 @@ </div> <div id="typo3-docbody"> <div id="typo3-inner-docbody"> + <f:if condition="{showLicenseBanner}"> + <f:render partial="Backend/LicenseBanner" /> + </f:if> <h1> <f:render section="headline" /> </h1> diff --git a/Resources/Private/Partials/Backend/Filter.html b/Resources/Private/Partials/Backend/Filter.html index 53395972e523f116c4010e52f36bf54201c224cb..8392b2d5b1617bc03d7e56e07566bac8ab844162 100644 --- a/Resources/Private/Partials/Backend/Filter.html +++ b/Resources/Private/Partials/Backend/Filter.html @@ -1,24 +1,39 @@ <f:form action="index" controller="Backend" method="post" objectName="filters" object="{filters}"> <div class="row"> - <div class="col-xs-5"> - <div class="form-group"> - <label for="filter-category"> - <f:translate key="backend.filters.category" /> - <small><f:translate key="backend.filters.categories.description" /></small> - </label> - <f:form.select class="form-control" multiple="1" size="4" property="filters.categories" options="{categories}" id="filter-category" /> + <f:if condition="{showCategoryFilter}"> + <div class="col-xs-6"> + <div class="form-group"> + <label for="filter-categories"> + <f:translate key="backend.filters.categories" /> + </label> + <f:form.select class="form-control" multiple="1" size="4" property="categories" options="{categories}" id="filter-categories" /> + <small><f:format.raw><f:translate key="backend.filters.categories.description" /></f:format.raw></small> + </div> </div> - </div> - <div class="col-xs-4"> + </f:if> + <div class="col-xs-{f:if(condition: showCategoryFilter, then: '6', else: '5')}"> <div class="form-group"> - <label for="filter-search"><f:translate key="backend.filters.search" /></label> - <f:form.textfield class="form-control" property="search" id="filter-search" /> + <label for="filter-tags"> + <f:translate key="backend.filters.tags" /> + </label> + <f:form.select class="form-control" multiple="1" size="4" property="tags" options="{tags}" id="filter-tags" /> + <small><f:format.raw><f:translate key="backend.filters.tags.description" /></f:format.raw></small> </div> </div> - <div class="col-xs-3"> - <div class="form-group"> - <label> </label><br /> - <f:form.button class="btn btn-success form-group col-xs-12" type="submit">Filter</f:form.button> + <div class="col-xs-7 pull-right"> + <div class="row"> + <div class="col-xs-7"> + <div class="form-group"> + <label for="filter-search"><f:translate key="backend.filters.search" /></label> + <f:form.textfield class="form-control" property="search" id="filter-search" /> + </div> + </div> + <div class="col-xs-5"> + <div class="form-group"> + <label> </label><br /> + <f:form.button class="btn btn-success form-group col-xs-12" type="submit"><f:translate key="backend.filter" /></f:form.button> + </div> + </div> </div> </div> </div> diff --git a/Resources/Private/Partials/Backend/LicenseBanner.html b/Resources/Private/Partials/Backend/LicenseBanner.html new file mode 100644 index 0000000000000000000000000000000000000000..c394186a3a346752fa7484e126e8667866b5c13c --- /dev/null +++ b/Resources/Private/Partials/Backend/LicenseBanner.html @@ -0,0 +1,17 @@ +<div class="bannerContainer"> + <div class="row"> + <div class="col-xs-12"> + <div class="panel panel-info"> + <div class="panel-heading"> + <f:image width="150" height="33" src="{f:uri.resource(path: 'Images/logo.svg')}" /> + | <span><f:translate key="backend.licenceBannerTitle"/></span> + </div> + <div class="panel-body"> + <f:format.raw> + <f:translate key="backend.licenceBannerText"/> + </f:format.raw> + </div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/Resources/Private/Partials/Backend/SelectPage.html b/Resources/Private/Partials/Backend/SelectPage.html new file mode 100644 index 0000000000000000000000000000000000000000..7957f4311a70b4c27c564dca73be38e690c25823 --- /dev/null +++ b/Resources/Private/Partials/Backend/SelectPage.html @@ -0,0 +1,30 @@ +{namespace sg=SGalinski\SgNews\ViewHelpers} +<p> + <f:translate key="backend.selectPage" /><f:if condition="{pageUid}"><f:else>.</f:else></f:if> + <f:if condition="{pageUid}"> + <f:translate key="backend.selectPageOr" /> + <a href="#" class="btn btn-default" onclick="{sg:backend.editOnClick(table: 'pages', uid: pageUid, new: 1, type: 'category')}"> + <sg:backend.icon id="actions-document-new" size="small" /> + <f:translate key="backend.button.createCategory" /> + </a> + </f:if> +</p> +<f:if condition="{pages}"> + <div class="panel panel-default recordlist"> + <div class="table-fit"> + <table data-table="pages" class="table table-striped table-hover"> + <tbody> + <f:for each="{pages}" as="page"> + <tr data-uid="{page.uid}"> + <td nowrap="nowrap" class="col-title"> + <a href="#" onclick="sgNewsGoToPage({page.uid}, '{page.path}'); return false;"> + <sg:backend.recordIcon table="pages" row="{page}" clickMenu="0" /> {page._thePathFull} + </a> + </td> + </tr> + </f:for> + </tbody> + </table> + </div> + </div> +</f:if> \ No newline at end of file diff --git a/Resources/Private/Templates/Backend/Index.html b/Resources/Private/Templates/Backend/Index.html index 1555ee68dd79201ffb525978a53d554b0b65d1d2..902f0f018799c08954b2cc596891c71a6c377295 100644 --- a/Resources/Private/Templates/Backend/Index.html +++ b/Resources/Private/Templates/Backend/Index.html @@ -1,4 +1,4 @@ -{namespace sg=SGalinski\SgRoutes\ViewHelpers} +{namespace sg=SGalinski\SgNews\ViewHelpers} <f:layout name="Backend" /> @@ -11,45 +11,72 @@ <p> <f:translate key="backend.message" /> </p> - <f:render partial="Backend/Filter" arguments="{categories: categories, filters: filters}" /> + <f:if condition="{showNewsList}"> + <f:then> + <f:render partial="Backend/Filter" arguments="{categories: categories, tags: tags, showCategoryFilter: showCategoryFilter, filters: filters}" /> - <f:if condition="{news}"> - <div class="panel panel-default recordlist"> - <div class="table-fit"> - <table data-table="pages" class="table table-striped table-hover"> - <sg:backend.widget.paginate objects="{news}" as="paginatedNews" configuration="{insertAbove: 1, itemsPerPage: 20}"> - <tbody> - <f:for each="{paginatedNews}" as="singleNews"> - {sg:backend.editOnClick(table: 'pages', uid: singleNews.uid) -> sg:set(name: 'editOnClick')} - <tr data-uid="{singleNews.uid}"> - <td nowrap="nowrap" class="col-icon"> - <f:format.raw> - <sg:backend.icon table="pages" row="{singleNews}" /> - </f:format.raw> - </td> - <td nowrap="nowrap"> - <a href="#" onclick="{editOnClick}"> - <span> - <f:if condition="{singleNews.title}"> - <f:then> - {singleNews.title} - </f:then> - </f:if> - </span> - </a> - </td> - <td nowrap="nowrap" class="col-control"> - <f:format.raw> - <sg:backend.control table="tx_sgroutes_domain_model_page" row="{singleNews}" /> - </f:format.raw> - </td> - </tr> - </f:for> - </tbody> - </sg:backend.widget.paginate> - </table> + + <div class="form-group"> + <f:if condition="{showCategoryFilter}"> + <f:then> + <a href="#" class="btn btn-default" onclick="{sg:backend.editOnClick(table: 'pages', uid: pageUid, new: 1, type: 'category')}"> + <sg:backend.icon id="actions-document-new" size="small" /> + <f:translate key="backend.button.createCategory" /> + </a> + </f:then> + <f:else> + <a href="#" class="btn btn-default" onclick="{sg:backend.editOnClick(table: 'pages', uid: pageUid, new: 1, type: 'news')}"> + <sg:backend.icon id="actions-document-new" size="small" /> + <f:translate key="backend.button.createNews" /> + </a> + </f:else> + </f:if> </div> - </div> - </f:if> + <f:if condition="{news}"> + <div class="panel panel-default recordlist"> + <div class="table-fit"> + <table data-table="pages" class="table table-striped table-hover"> + <sg:backend.widget.paginate objects="{news}" as="paginatedNews" configuration="{insertAbove: 1, itemsPerPage: 20}"> + <tbody> + <f:for each="{paginatedNews}" as="singleNews"> + <tr data-uid="{singleNews.uid}"> + <td nowrap="nowrap" class="col-icon"> + <f:format.raw> + <sg:backend.recordIcon table="pages" row="{singleNews}" /> + </f:format.raw> + </td> + <td nowrap="nowrap"> + <a href="#" onclick="{sg:backend.editOnClick(table: 'pages', uid: singleNews.uid)}"> + <span> + <f:if condition="{singleNews.translation_title}"> + <f:then> + {singleNews.translation_title} + </f:then> + <f:else> + {singleNews.title} + </f:else> + </f:if> + </span> + </a> <br /> + <sg:backend.translationLinks pageUid="{pageUid}" table="pages" uid="{singleNews.uid}" /> + </td> + <td nowrap="nowrap" class="col-control"> + <f:format.raw> + <sg:backend.control table="pages" row="{singleNews}" clipboard="1" /> + </f:format.raw> + </td> + </tr> + </f:for> + </tbody> + </sg:backend.widget.paginate> + </table> + </div> + </div> + </f:if> + </f:then> + <f:else> + <f:render partial="Backend/SelectPage" arguments="{pages: alternativePageOptions, pageUid: pageUid}" /> + </f:else> + </f:if> </f:section> diff --git a/Resources/Private/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html b/Resources/Private/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html new file mode 100644 index 0000000000000000000000000000000000000000..c1f58860aaa46f523e9d66fa79f2eb02c7e6aed4 --- /dev/null +++ b/Resources/Private/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html @@ -0,0 +1,127 @@ +{namespace core=TYPO3\CMS\Core\ViewHelpers} +{namespace sg=SGalinski\SgRoutes\ViewHelpers} + +<f:if condition="{configuration.insertAbove}"> + <thead> + <tr> + <td colspan="6"> + <f:render section="paginator" arguments="{pagination: pagination, position:'top', recordsLabel: configuration.recordsLabel}" /> + </td> + </tr> + </thead> +</f:if> + +<f:renderChildren arguments="{contentArguments}" /> + +<f:if condition="{configuration.insertBelow}"> + <tfoot> + <tr> + <td colspan="6"> + <f:render section="paginator" arguments="{pagination: pagination, position:'bottom', recordsLabel: configuration.recordsLabel}" /> + </td> + </tr> + </tfoot> +</f:if> + +<f:section name="paginator"> + <nav class="pagination-wrap"> + <ul class="pagination pagination-block"> + <f:if condition="{pagination.hasLessPages}"> + <f:then> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: 1})}" title="{f:translate(key:'widget.pagination.first')}"> + <core:icon identifier="actions-view-paging-first" /> + </a> + </li> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.previousPage})}" title="{f:translate(key:'widget.pagination.previous')}"> + <core:icon identifier="actions-view-paging-previous" /> + </a> + </li> + </f:then> + <f:else> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-first" /> + </span> + </li> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-previous" /> + </span> + </li> + </f:else> + </f:if> + <li> + <span> + <f:if condition="{recordsLabel}"> + <f:then> + {recordsLabel} + </f:then> + <f:else> + <f:translate key="widget.pagination.records" /> + </f:else> + </f:if> + {pagination.startRecord} - {pagination.endRecord} / {pagination.totalObjects} + </span> + </li> + <li> + <span> + <f:translate key="widget.pagination.page" /> + + <form id="paginator-form-{position}" onsubmit="goToPage{position}(this); return false;" style="display:inline;"> + <script type="text/javascript"> + function goToPage{position}(formObject){ + var url = '{sg:widget.uri(arguments:{currentPage: 987654321}) -> f:format.raw()}'; + var page = formObject.elements['paginator-target-page'].value; + if (page > {pagination.numberOfPages}) { + page = {pagination.numberOfPages}; + } + else + if (page < 1) { + page = 1; + } + url = url.replace('987654321', page); + self.location.href = url; + } + </script> + <f:form.textfield id="paginator-{position}" name="paginator-target-page" additionalAttributes="{min: '1'}" class="form-control input-sm paginator-input" size="5" value="{pagination.current}" type="number" /> + </form> + + / {pagination.numberOfPages} + </span> + </li> + <f:if condition="{pagination.hasMorePages}"> + <f:then> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.nextPage})}" title="{f:translate(key:'widget.pagination.next')}"> + <core:icon identifier="actions-view-paging-next" /> + </a> + </li> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.numberOfPages})}" title="{f:translate(key:'widget.pagination.last')}"> + <core:icon identifier="actions-view-paging-last" /> + </a> + </li> + </f:then> + <f:else> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-next" /> + </span> + </li> + <li class="disabled"> + <span> + <core:icon identifier="actions-view-paging-last" /> + </span> + </li> + </f:else> + </f:if> + <li> + <a href="{sg:widget.uri(arguments:{currentPage: pagination.current})}" title="{f:translate(key:'widget.pagination.refresh')}"> + <core:icon identifier="actions-refresh" /> + </a> + </li> + </ul> + </nav> +</f:section> diff --git a/Resources/Public/Icons/module-sgnews.svg b/Resources/Public/Icons/module-sgnews.svg new file mode 100644 index 0000000000000000000000000000000000000000..0cfe412d7293248e670a14ec19342512cee0527d --- /dev/null +++ b/Resources/Public/Icons/module-sgnews.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"> + <path fill="#335056" d="M0 0h18v18H0z"/> + <circle fill="#59F" cx="9" cy="9" r="8"/> + <path fill="#AAD400" d="M 15.973 8.378 C 15.762 6.08 14.437 4.033 12.428 2.898 C 12.336 2.995 12.311 2.938 12.311 3.035 C 12.034 3.132 11.665 2.938 11.388 2.841 C 10.712 2.57 9.964 2.536 9.266 2.744 C 8.989 2.841 8.344 2.938 8.62 3.52 C 8.804 3.812 9.265 4.103 9.727 3.715 C 9.911 3.521 10.097 3.23 10.372 3.521 C 10.465 3.619 10.465 3.716 10.465 3.813 C 10.372 4.103 10.095 4.201 9.819 4.201 C 9.449 4.298 9.079 4.201 8.711 4.298 C 8.341 4.395 7.788 4.492 7.788 4.978 C 7.788 5.558 7.603 5.656 7.141 5.753 C 6.771 5.753 6.588 5.947 6.771 6.335 C 6.864 6.625 7.141 6.625 7.326 6.529 C 8.064 6.141 8.71 5.171 9.54 6.335 L 9.724 6.238 C 9.909 5.462 10.094 5.948 10.278 6.238 L 10.555 6.528 C 11.662 5.462 11.385 7.014 11.755 7.304 C 11.015 7.11 10.185 7.886 9.54 7.401 C 8.433 6.529 7.695 6.917 6.68 7.789 C 5.85 8.469 5.665 9.341 5.85 10.117 C 6.126 11.087 7.14 11.184 7.972 10.99 C 8.248 10.892 8.802 10.892 8.802 11.086 C 8.617 11.959 9.632 12.541 9.262 13.51 C 8.986 14.286 9.448 15.256 10.092 15.547 C 10.739 15.837 11.292 15.062 11.57 14.383 C 11.662 14.286 11.57 14.093 11.662 13.995 C 12.4 13.025 12.308 11.571 13.23 10.601 C 13.507 10.311 14.43 9.244 13.69 8.371 C 15.444 7.886 14.8 9.631 15.168 10.408 C 15.444 9.826 15.628 9.244 15.813 8.758 C 15.905 8.468 15.973 8.378 15.973 8.378 Z M 5.948 3.21 C 6.238 2.904 6.251 2.854 6.542 2.445 C 5.845 2.703 5.195 3.074 4.619 3.544 L 4.787 3.721 L 5.66 3.721 C 5.66 3.721 5.757 3.721 5.757 3.621 L 5.854 3.518 C 5.854 3.518 5.951 3.518 5.951 3.416 L 6.047 3.314 L 6.144 3.211 C 5.854 3.416 5.854 3.314 5.95 3.211 C 5.95 3.314 5.95 3.314 5.95 3.211 C 5.95 3.314 5.95 3.211 5.95 3.211 L 5.948 3.21 Z M 4.666 12.65 C 4.666 12.65 4.57 12.558 4.666 12.65 C 4.57 12.558 4.57 12.558 4.666 12.65 L 4.666 12.374 L 4.57 12.282 C 4.475 12.282 4.475 12.189 4.38 12.189 C 4.285 12.189 4.19 12.096 4.094 12.096 L 3.903 12.096 C 3.903 12.096 3.808 12.096 3.808 12.004 C 3.14 11.634 2.855 10.989 2.664 10.252 C 2.664 10.067 2.569 9.882 2.569 9.698 C 2.475 9.605 2.475 9.513 2.379 9.42 C 2.189 9.236 2.285 8.867 2.475 8.498 L 2.475 8.314 C 2.475 8.221 2.475 8.221 2.57 8.129 C 2.57 8.039 2.57 8.039 2.666 7.946 C 2.856 7.576 3.238 7.3 3.428 7.023 C 3.523 6.931 3.523 6.931 3.523 6.839 L 3.523 6.747 C 3.618 6.655 3.618 6.47 3.713 6.377 C 3.713 6.287 3.809 6.101 3.809 6.009 C 3.809 5.824 3.809 5.732 3.713 5.549 C 3.617 5.271 3.559 5.031 3.369 4.846 C 2.511 6.046 1.999 7.392 1.999 8.959 C 1.999 11.082 2.902 12.965 4.413 14.283 C 4.413 14.283 4.474 14.309 4.474 14.219 L 4.474 14.125 C 4.474 14.033 4.569 14.033 4.569 13.941 C 4.665 13.571 4.665 13.019 4.666 12.65 Z"/> + <path opacity=".2" d="M 9 17 C 4.589 17 1 13.411 1 9 C 1 4.589 4.589 1 9 1 C 13.411 1 17 4.589 17 9 C 17 13.411 13.411 17 9 17 Z M 9 2 C 5.14 2 2 5.14 2 9 C 2 12.859 5.14 16 9 16 C 12.859 16 16 12.859 16 9 C 16 5.14 12.859 2 9 2 Z"/> + <path fill="#FFF" d="M 14 7 L 4 7 L 4 13.894 C 5.314 15.242 7.117 16.001 9 16 C 10.96 16 12.729 15.192 14 13.894 L 14 7 Z"/> + <path fill="#666" d="M 14 7 L 4 7 L 4 13.894 C 4.306 14.206 4.641 14.489 5 14.74 L 5 8 L 13 8 L 13 14.74 C 13.359 14.49 13.694 14.207 14 13.894 L 14 7 Z"/> + <path fill="#333" d="M 6 9 L 12 9 L 12 11 L 6 11 Z"/> + <path fill="#B9B9B9" d="M 6 12 L 12 12 L 12 13 L 6 13 Z M 6 14 L 12 14 L 12 15 L 6 15 Z"/> +</svg> \ No newline at end of file diff --git a/Resources/Public/Images/logo.svg b/Resources/Public/Images/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..70eb80e8303e5b8a889275ed67579712d964b269 --- /dev/null +++ b/Resources/Public/Images/logo.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="150" height="33.857" viewBox="0 14.071 150 33.857"><path fill="#7C2623" d="M14.679 14.071V19l.053-.054 10.339 6v5.197l-10.393-6.268-4.339 2.571 4.339 2.518 10.393 5.946v2.144L14.732 43.16h-.053v4.769l14.678-8.464V22.536"/><path fill="#BC3A2F" d="M14.679 33.143L4.286 27.304v-2.357L14.679 19v-4.929L0 22.536v16.929l14.679 8.464V43.16L4.286 37.268V32.34l10.393 5.839h.053l4.393-2.571"/><path fill="#565D5D" d="M47.679 26.232a.55.55 0 0 1-.268.321c-.107.054-.214.106-.321.106-.107 0-.268-.053-.429-.16s-.375-.215-.589-.321c-.214-.107-.482-.269-.803-.321a3.229 3.229 0 0 0-1.072-.161c-.375 0-.696.054-.964.161-.268.107-.536.214-.696.375-.214.16-.321.375-.429.589a1.712 1.712 0 0 0-.161.75c0 .321.107.644.268.857.214.214.429.429.75.589a6.33 6.33 0 0 0 1.071.429c.375.107.804.269 1.232.429.428.16.803.321 1.232.482.375.214.75.428 1.071.696.322.268.589.643.75 1.071.214.429.268.91.268 1.554 0 .643-.107 1.286-.321 1.821a4.018 4.018 0 0 1-.964 1.5c-.429.429-.964.75-1.554 1.018-.643.268-1.339.375-2.143.375a5.4 5.4 0 0 1-1.339-.16 11.764 11.764 0 0 1-1.286-.375 5.551 5.551 0 0 1-1.125-.59 3.51 3.51 0 0 1-.964-.803l.803-1.286c.054-.107.161-.161.268-.214.107-.055.214-.107.321-.107.161 0 .321.053.536.214s.429.269.697.429c.268.16.589.321.911.482.375.16.803.214 1.286.214.75 0 1.339-.161 1.768-.536.429-.375.643-.91.643-1.553 0-.375-.107-.697-.268-.911-.214-.214-.428-.429-.75-.589a6.33 6.33 0 0 0-1.071-.429c-.375-.107-.804-.214-1.232-.375-.429-.107-.804-.269-1.232-.482-.375-.161-.75-.429-1.071-.75s-.589-.696-.75-1.125c-.214-.429-.268-1.018-.268-1.661 0-.535.107-1.018.322-1.553.214-.482.536-.965.911-1.34a4.69 4.69 0 0 1 1.5-.91 5.73 5.73 0 0 1 2.036-.375c.857 0 1.661.107 2.357.375.697.268 1.339.643 1.822 1.125l-.753 1.125zm11.035 9.858c.589 0 1.125-.055 1.607-.161.429-.107.857-.269 1.286-.429v-2.625h-1.821a.615.615 0 0 1-.429-.16.488.488 0 0 1-.161-.375v-1.5h4.822v5.839c-.375.268-.75.481-1.125.696s-.804.375-1.286.482c-.428.107-.911.214-1.446.268s-1.071.107-1.661.107c-1.071 0-2.036-.161-2.893-.536-.911-.375-1.661-.856-2.303-1.554a6.338 6.338 0 0 1-1.5-2.357c-.375-.91-.536-1.875-.536-2.945 0-1.072.161-2.09.536-3a7.681 7.681 0 0 1 1.5-2.357 6.338 6.338 0 0 1 2.357-1.5c.911-.375 1.982-.536 3.107-.536 1.178 0 2.196.161 3.053.536.857.375 1.607.803 2.197 1.339l-.804 1.232c-.161.268-.375.375-.589.375a.865.865 0 0 1-.482-.161c-.214-.107-.429-.268-.643-.375s-.482-.214-.75-.321c-.268-.106-.589-.161-.911-.214a7.333 7.333 0 0 0-1.179-.107c-.696 0-1.339.107-1.929.375-.589.215-1.071.59-1.5 1.018-.428.429-.696.965-.964 1.607a6.582 6.582 0 0 0-.322 2.09c0 .856.107 1.553.375 2.25.214.643.536 1.178.964 1.66.429.429.911.804 1.5 1.018a5.651 5.651 0 0 0 1.93.321m21.911 1.981h-2.09c-.214 0-.428-.054-.589-.161a.825.825 0 0 1-.321-.428l-1.071-2.947h-6l-1.072 2.947c-.053.16-.161.268-.321.428-.161.107-.375.215-.589.215h-2.089l5.679-14.465h2.732l5.731 14.411zm-9.375-5.464h4.607l-1.768-4.822c-.054-.214-.161-.481-.268-.75a4.182 4.182 0 0 1-.268-.964c-.107.321-.161.644-.268.964-.107.269-.161.536-.268.75l-1.767 4.822zm14.946 3.268h5.786v2.196h-8.465V23.607h2.679m9.429 0h2.679v14.464h-2.679V23.607zm9.054.053c.054 0 .161.055.214.055.054.053.107.053.161.106l.214.214 7.607 9.697c-.054-.215-.054-.482-.054-.697v-9.428h2.357v14.464h-1.394c-.214 0-.375-.054-.535-.106-.16-.055-.268-.161-.429-.375l-7.554-9.644c0 .214.054.429.054.644v9.481h-2.356V23.607h1.393c.108 0 .214 0 .322.053m22.553 2.572a.55.55 0 0 1-.268.321c-.107.054-.215.106-.322.106s-.268-.053-.428-.16a5.625 5.625 0 0 0-.59-.321c-.215-.107-.482-.269-.804-.321a3.223 3.223 0 0 0-1.071-.161c-.375 0-.696.054-.965.161-.268.107-.535.214-.695.375-.215.16-.322.375-.43.589-.106.214-.16.482-.16.75 0 .321.107.644.268.857.215.214.429.429.75.589.322.161.697.322 1.072.429.375.107.803.269 1.231.429s.804.321 1.232.482c.375.214.75.428 1.071.696.321.268.59.643.75 1.071.215.429.268.91.268 1.554 0 .643-.107 1.286-.321 1.821a4.018 4.018 0 0 1-.964 1.5c-.429.429-.965.75-1.554 1.018-.644.268-1.339.375-2.144.375-.481 0-.91-.053-1.339-.16s-.856-.215-1.286-.375a5.551 5.551 0 0 1-1.125-.59 3.525 3.525 0 0 1-.964-.803l.804-1.286c.054-.107.16-.161.268-.214.107-.055.215-.107.322-.107.16 0 .32.053.535.214s.429.269.696.429c.269.16.589.321.911.482.375.16.803.214 1.285.214.75 0 1.34-.161 1.768-.536.43-.375.644-.91.644-1.553 0-.375-.107-.697-.269-.911-.214-.214-.428-.429-.75-.589-.32-.161-.643-.322-1.07-.429-.375-.107-.805-.214-1.232-.375-.429-.107-.804-.269-1.232-.482-.375-.161-.75-.429-1.071-.75s-.589-.696-.75-1.125c-.214-.429-.269-1.018-.269-1.661 0-.535.107-1.018.322-1.553a4.56 4.56 0 0 1 .91-1.34 4.69 4.69 0 0 1 1.5-.91 5.735 5.735 0 0 1 2.036-.375c.856 0 1.661.107 2.356.375.697.268 1.34.643 1.822 1.125l-.748 1.125zm7.178 3.428h.644c.268 0 .481-.053.643-.106s.321-.161.429-.321l3.965-5.036c.16-.214.32-.375.535-.429.16-.107.429-.107.696-.107h2.304l-4.875 6c-.16.161-.321.322-.429.482-.161.107-.268.215-.429.322.215.106.429.214.59.32.16.161.375.322.535.536l5.036 6.75h-2.357c-.321 0-.536-.054-.696-.161a1.242 1.242 0 0 1-.375-.375l-4.071-5.356c-.107-.161-.269-.321-.429-.375s-.429-.107-.696-.107h-.857v6.375h-2.679V23.607h2.679v6.053h-.163zm12.911-6.053H150v14.464h-2.679V23.607z"/></svg> \ No newline at end of file diff --git a/Resources/Public/Scripts/Backend.js b/Resources/Public/Scripts/Backend.js new file mode 100644 index 0000000000000000000000000000000000000000..620eff05f9bcaccb06bf0a8c73fe0c0647c989d2 --- /dev/null +++ b/Resources/Public/Scripts/Backend.js @@ -0,0 +1,107 @@ +(function($) { + $(document).ready(function() { + $.get(TYPO3.settings.ajaxUrls['sg_news::ajaxPing']); + }); +})(TYPO3.jQuery); + +// functions for backend docheader functionality +function jumpExt(URL, anchor) { // + var anc = anchor ? anchor : ""; + window.location.href = URL + (T3_THIS_LOCATION ? "&returnUrl=" + T3_THIS_LOCATION : "") + anc; + return false; +} + +function jumpSelf(URL) { // + window.location.href = URL + (T3_RETURN_URL ? "&returnUrl=" + T3_RETURN_URL : ""); + return false; +} + +function jumpToUrl(URL) { + window.location.href = URL; + return false; +} + +function setHighlight(id) { // + top.fsMod.recentIds["web"] = id; + top.fsMod.navFrameHighlightedID["web"] = "pages" + id + "_" + top.fsMod.currentBank; // For highlighting + if (top.content && top.content.nav_frame && top.content.nav_frame.refresh_nav) { + top.content.nav_frame.refresh_nav(); + } +} + +/** + * Switches to the spefied page in the BE + * + * @param {number} uid + * @param {string} path + */ +function sgNewsGoToPage(uid, path, selectOnly) { + if(typeof selectOnly === 'undefined') { + selectOnly = false; + } + selectOnly = Boolean(selectOnly); + if (top.nav) { + if (selectOnly) { + top.nav.invokePageId(uid, gotToPageCallbackNoFollow); + } else { + top.nav.invokePageId(uid, gotToPageCallback); + } + } else { + var tree = top.Ext.getCmp('typo3-pagetree'); + if (tree) { + tree.activeTree.selectPath(path); + } + if (selectOnly) { + return; + } + var separator = '?'; + if (top.currentSubScript.indexOf('?') !== -1) { + separator = '&'; + } + top.TYPO3.Backend.ContentContainer.setUrl( + top.currentSubScript + separator + 'id=' + uid + ); + } +} + + +/** + * Callback for page selection in the pagetree without follow + */ +function gotToPageCallbackNoFollow(path){ + var callback = top.Ext.createDelegate(top.nav.mainTree.selectPath, top.nav.mainTree); + callback.apply(this, arguments); +} + + +/** + * Callback for page selection in the pagetree + */ +function gotToPageCallback(path){ + var callback = top.Ext.createDelegate(top.nav.mainTree.selectPath, top.nav.mainTree); + callback.apply(this, arguments); + var node = top.nav.getSelected(); + if (node) { + top.TYPO3.Components.PageTree.Actions.singleClick(node, top.TYPO3.Components.PageTree.Tree); + } +} + +/** + * opens the selected category edit form + * + * @return {boolean} + */ +function editSelectedCategory(){ + var selected = TYPO3.jQuery('#filter-categories').val(); + if(selected && CategoryEditLinks[selected[0]]) { + jumpToUrl(CategoryEditLinks[selected[0]] + '&returnUrl=' + T3_THIS_LOCATION); + } + return false; +} + +function sgNewsGoToPageModule(uid, path) { + sgNewsGoToPage(uid, path, true); + parent.fsMod.recentIds['web'] = uid; + parent.TYPO3.ModuleMenu.App.showModule('web_layout'); + return false; +} diff --git a/ext_conf_template.txt b/ext_conf_template.txt new file mode 100644 index 0000000000000000000000000000000000000000..5a0d655968734511aea82af803b0efdf5c632244 --- /dev/null +++ b/ext_conf_template.txt @@ -0,0 +1,2 @@ +# cat=license; type=string; label=LLL:EXT:sg_news/Resources/Private/Language/locallang.xlf:configuration.licenseKey +key = diff --git a/ext_emconf.php b/ext_emconf.php index 6bf1fb5750a20976a47bdcc09ae57ab670209a21..1cad58fbfecf4f21f955b194f50a59c9256d2397 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -23,7 +23,7 @@ $EM_CONF[$_EXTKEY] = [ 'constraints' => [ 'depends' => [ 'typo3' => '7.6.0-8.7.99', - 'php' => '5.5.0-7.1.99', + 'php' => '5.6.0-7.1.99', ], 'conflicts' => [], 'suggests' => [ diff --git a/ext_localconf.php b/ext_localconf.php index 4c878c952f116dbdbb381d7cc6959862d59ab63b..2686473c8aa5b18333d7d276e2c177e37c5d11b5 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -83,3 +83,13 @@ if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('realurl')) { $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/realurl/class.tx_realurl_autoconfgen.php']['extensionConfiguration']['sgnews'] = \SGalinski\SgNews\Hooks\RealUrlAutoConfiguration::class . '->addNewsConfig'; } + +/** @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher */ +$signalSlotDispatcher = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class); + +$signalSlotDispatcher->connect( + \TYPO3\CMS\Backend\Controller\EditDocumentController::class, + 'preInitAfter', + \SGalinski\SgNews\Hooks\EditDocumentController::class, + 'preInitAfter' +); \ No newline at end of file diff --git a/ext_tables.php b/ext_tables.php index 983a3a4e742a80fc6577364bfb182c24ee61c71f..2ebfed203c51c5eeca7bf553eb63cd64162a3c33 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -39,7 +39,7 @@ if (TYPO3_MODE === 'BE') { ], [ 'access' => 'user,group', - 'icon' => 'EXT:sg_news/Resources/Public/Images/News.png', + 'icon' => 'EXT:sg_news/Resources/Public/Icons/module-sgnews.svg', 'labels' => 'LLL:EXT:sg_news/Resources/Private/Language/locallang.xlf', ] );