Newer
Older
namespace SGalinski\SgNews\Utility;
/***************************************************************
* 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\Model\News;
use SGalinski\SgNews\Domain\Model\Tag;
use SGalinski\SgNews\Domain\Repository\NewsRepository;
use SGalinski\SgNews\Domain\Repository\TagRepository;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryHelper;
use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
use TYPO3\CMS\Core\Imaging\IconRegistry;
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): int {
$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(): array {
$andWhere = ' AND sys_language_uid IN (0,-1)';
$rootOptionRows = self::getRecordsByField(
'pages', 'is_siteroot', 1, $andWhere, '', '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 = []): array {
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): array {
$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)
);
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$result = $queryBuilder->select('uid', 'title')
->from('pages')
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('doktype', $queryBuilder->createNamedParameter(self::CATEGORY_DOKTYPE, \PDO::PARAM_INT)),
$queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter(explode(',', $childPids), Connection::PARAM_INT_ARRAY))
)
)
->execute()->fetchAll();
$categories = [];
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): array {
$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']);
}
/**
* Get an array of all tags uids => titles for the specified news uid
*
* @param int $newsItemUid
* @param int $languageUid
* @return array
* @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
* @throws \InvalidArgumentException
*/
public static function getTagsForNewsItem($newsItemUid, $languageUid = 0): array {
$temporaryTSFEInstance = FALSE;
if (!isset($GLOBALS['TSFE'])) {
$temporaryTSFEInstance = TRUE;
$GLOBALS['TSFE'] = new \stdClass();
$GLOBALS['TSFE']->gr_list = '';
}
$newsItemUid = (int) $newsItemUid;
$languageUid = (int) $languageUid;
$tags = [];
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$newsRepository = $objectManager->get(NewsRepository::class);
$query = $newsRepository->createQuery();
$querySettings = $query->getQuerySettings();
$querySettings->setLanguageUid($languageUid);
$querySettings->setLanguageOverlayMode(TRUE);
$querySettings->setLanguageMode('content_fallback');
$query->setQuerySettings($querySettings);
if ($newsItemUid) {
$query->matching($query->equals('uid', $newsItemUid));
}
/** @var News $newsItem */
$newsItem = $query->execute()->getFirst();
if ($temporaryTSFEInstance) {
unset($GLOBALS['TSFE']);
}
if ($newsItem) {
/** @var Tag $tag */
foreach ($newsItem->getTags() as $tag) {
$tags[(int) $tag->getUid()] = trim($tag->getTitle());
}
}
/**
* 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): array {
$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 {
/** @var array $filterCategories */
$filterCategories = $filters['categories'];
foreach ($filterCategories as $categoryUid) {
$categoryPageInfo = BackendUtility::readPageAccess(
(int) $categoryUid, $GLOBALS['BE_USER']->getPagePermsClause(1)
);
if ($categoryPageInfo) {
$categories[] = (int) $categoryUid;
}
}
}
$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));
}
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$deletedRestriction = GeneralUtility::makeInstance(DeletedRestriction::class);
$queryBuilder = $connectionPool->getQueryBuilderForTable('pages');
$queryBuilder->getRestrictions()->removeAll()->add($deletedRestriction);
$queryBuilder->select('p.uid',
'p.pid',
'p.hidden',
'p.sorting',
'p.doktype',
'p.title'
)->from('pages', 'p')
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->in('p.uid', $queryBuilder->createNamedParameter($allowedUids, Connection::PARAM_INT_ARRAY)),
$queryBuilder->expr()->eq('p.doktype', $queryBuilder->createNamedParameter(self::NEWS_DOKTYPE, \PDO::PARAM_INT))
)
)
->groupBy('p.uid')
->orderBy('p.sorting');
$queryBuilder->leftJoin('p', 'pages', 'translation',
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('translation.l10n_parent', 'p.uid'),
$queryBuilder->expr()->eq('translation.sys_language_uid', $queryBuilder->createNamedParameter($languageUid, \PDO::PARAM_INT))
)
);
if (isset($filters['tags']) && is_array($filters['tags']) && count($filters['tags'])) {
$queryBuilder->innerJoin('p', 'sys_category_record_mm', 'tag',
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('tag.tablenames', $queryBuilder->createNamedParameter('pages')),
$queryBuilder->expr()->eq('tag.fieldname', $queryBuilder->createNamedParameter('tx_sgnews_tags')),
$queryBuilder->expr()->eq('tag.uid_foreign', 'p.uid'),
$queryBuilder->expr()->in('tag.uid_local', $queryBuilder->createNamedParameter(\array_unique($filters['tags']), Connection::PARAM_INT_ARRAY))
if (isset($filters['search']) && trim($filters['search'])) {
$searchParameter = $queryBuilder->createNamedParameter('%' . trim($filters['search']) . '%');
$queryBuilder->expr()->like('p.title', $searchParameter),
$queryBuilder->expr()->like('p.description', $searchParameter),
$queryBuilder->expr()->like('p.abstract', $searchParameter),
$expressions[] = $queryBuilder->expr()->like('translation.title', $searchParameter);
$expressions[] = $queryBuilder->expr()->like('translation.description', $searchParameter);
$expressions[] = $queryBuilder->expr()->like('translation.abstract', $searchParameter);
}
$authorQueryBuilder = $connectionPool->getQueryBuilderForTable('tx_sgnews_domain_model_author');
$authorQueryBuilder->getRestrictions()->removeAll()->add($deletedRestriction);
$authorSearchParameter = $authorQueryBuilder->createNamedParameter('%' . trim($filters['search']) . '%');
$authors = $authorQueryBuilder->select('author.uid')->from('tx_sgnews_domain_model_author', 'author')
->where(
$authorQueryBuilder->expr()->orX(
$authorQueryBuilder->expr()->like('author.name', $authorSearchParameter),
$authorQueryBuilder->expr()->like('author.email', $authorSearchParameter)
)
)->execute()->fetchAll();
if (count($authors) > 0) {
foreach (array_column($authors, 'uid') as $authorUid) {
$expressions[] = $queryBuilder->expr()->inSet('p.tx_sgnews_news_author', $authorUid);
}
$queryBuilder->andWhere(
$queryBuilder->expr()->orX(
return $queryBuilder->execute()->fetchAll();
}
/**
* Returns the available languages for the current BE user
*
* @param int $pageUid
* @return array
* @throws \InvalidArgumentException
*/
public static function getAvailableLanguages($pageUid = 0): array {
$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]
];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
$languageRows = $queryBuilder->select('uid', 'title', 'flag')
->from('sys_language')
->orderBy('sorting')
->execute()->fetchAll();
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'],
];
}
}
}
/**
* Returns records from table, $theTable, where a field ($theField) equals the value, $theValue
* The records are returned in an array
* If no records were selected, the function returns nothing
*
* @param string $theTable Table name present in $GLOBALS['TCA']
* @param string $theField Field to select on
* @param string $theValue Value that $theField must match
* @param string $whereClause Optional additional WHERE clauses put in the end of the query. DO NOT PUT IN GROUP BY, ORDER BY or LIMIT!
* @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
* @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
* @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
* @param bool $useDeleteClause Use the deleteClause to check if a record is deleted (default TRUE)
* @return mixed Multidimensional array with selected records (if any is selected)
*/
public static function getRecordsByField(
$theTable, $theField, $theValue, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '',
$useDeleteClause = TRUE
) {
if (is_array($GLOBALS['TCA'][$theTable])) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($theTable);
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));;
if ($useDeleteClause) {
$queryBuilder->getRestrictions()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder->select('*')
->from($theTable)
->where(
$queryBuilder->expr()->eq($theField, $queryBuilder->createNamedParameter($theValue))
);
// additional where
if ($whereClause) {
$queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($whereClause));
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
// group by
if ($groupBy !== '') {
$queryBuilder->groupBy(QueryHelper::parseGroupBy($groupBy));
}
// order by
if ($orderBy !== '') {
foreach (QueryHelper::parseOrderBy($orderBy) as $orderPair) {
list($fieldName, $order) = $orderPair;
$queryBuilder->addOrderBy($fieldName, $order);
}
}
// limit
if ($limit !== '') {
if (strpos($limit, ',')) {
$limitOffsetAndMax = GeneralUtility::intExplode(',', $limit);
$queryBuilder->setFirstResult((int)$limitOffsetAndMax[0]);
$queryBuilder->setMaxResults((int)$limitOffsetAndMax[1]);
} else {
$queryBuilder->setMaxResults((int)$limit);
}
}
return $queryBuilder->execute()->fetchAll();
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
/**
* Register the extension icons
* For use in ext_localconf.php
*/
public static function registerIcons() {
$iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
$iconRegistry->registerIcon(
'actions-document-open-white',
SvgIconProvider::class,
['source' => 'EXT:sg_news/Resources/Public/Icons/actions-document-open-white.svg']
);
$iconRegistry->registerIcon(
'sg_news-module',
SvgIconProvider::class,
['source' => 'EXT:sg_news/Resources/Public/Icons/module-sgnews.svg']
);
$iconRegistry->registerIcon(
'sg_news-module-transparent',
SvgIconProvider::class,
['source' => 'EXT:sg_news/Resources/Public/Icons/module-sgnews-transparent.svg']
);
$iconRegistry->registerIcon(
'tcarecords-pages-' . self::CATEGORY_DOKTYPE,
BitmapIconProvider::class,
['source' => 'EXT:sg_news/Resources/Public/Images/Category.png']
);
$iconRegistry->registerIcon(
'tcarecords-pages-' . self::NEWS_DOKTYPE,
BitmapIconProvider::class,
['source' => 'EXT:sg_news/Resources/Public/Images/News.png']
);
}