Initial commit - Typo3 11.5.41
This commit is contained in:
76
typo3conf/ext/news/Build/.php_cs
Normal file
76
typo3conf/ext/news/Build/.php_cs
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
/**
|
||||
* This file represents the configuration for Code Sniffing PSR-2-related
|
||||
* automatic checks of coding guidelines
|
||||
* Install @fabpot's great php-cs-fixer tool via
|
||||
*
|
||||
* $ composer global require friendsofphp/php-cs-fixer
|
||||
*
|
||||
* And then simply run
|
||||
*
|
||||
* $ php-cs-fixer fix --config ../Build/.php_cs
|
||||
*
|
||||
* inside the TYPO3 directory. Warning: This may take up to 10 minutes.
|
||||
*
|
||||
* For more information read:
|
||||
* http://www.php-fig.org/psr/psr-2/
|
||||
* http://cs.sensiolabs.org
|
||||
*/
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
die('This script supports command line usage only. Please check your command.');
|
||||
}
|
||||
// Define in which folders to search and which folders to exclude
|
||||
// Exclude some directories that are excluded by Git anyways to speed up the sniffing
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/../');
|
||||
// Return a Code Sniffing configuration using
|
||||
// all sniffers needed for PSR-2
|
||||
// and additionally:
|
||||
// - Remove leading slashes in use clauses.
|
||||
// - PHP single-line arrays should not have trailing comma.
|
||||
// - Single-line whitespace before closing semicolon are prohibited.
|
||||
// - Remove unused use statements in the PHP source code
|
||||
// - Ensure Concatenation to have at least one whitespace around
|
||||
// - Remove trailing whitespace at the end of blank lines.
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'no_leading_import_slash' => true,
|
||||
'no_trailing_comma_in_singleline_array' => true,
|
||||
'no_singleline_whitespace_before_semicolons' => true,
|
||||
'no_unused_imports' => true,
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'ordered_imports' => true,
|
||||
'single_quote' => true,
|
||||
'no_empty_statement' => true,
|
||||
'no_extra_consecutive_blank_lines' => true,
|
||||
'phpdoc_no_package' => true,
|
||||
'phpdoc_scalar' => true,
|
||||
'no_blank_lines_after_phpdoc' => true,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'whitespace_after_comma_in_array' => true,
|
||||
'function_typehint_space' => true,
|
||||
'hash_to_slash_comment' => true,
|
||||
'no_alias_functions' => true,
|
||||
'lowercase_cast' => true,
|
||||
'no_leading_namespace_whitespace' => true,
|
||||
'native_function_casing' => true,
|
||||
'self_accessor' => true,
|
||||
'no_short_bool_cast' => true,
|
||||
'no_unneeded_control_parentheses' => true
|
||||
])
|
||||
->setFinder($finder);
|
||||
34
typo3conf/ext/news/Build/FunctionalTests.xml
Normal file
34
typo3conf/ext/news/Build/FunctionalTests.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
Functional test suites setup
|
||||
|
||||
Functional tests should extend from \TYPO3\TestingFramework\Core\Tests\FunctionalTestCase,
|
||||
take a look at this class for further documentation on how to run the suite.
|
||||
|
||||
TYPO3 CMS functional test suite also needs phpunit bootstrap code, the
|
||||
file is located next to this .xml as FunctionalTestsBootstrap.php
|
||||
-->
|
||||
<phpunit
|
||||
backupGlobals="true"
|
||||
bootstrap="../.Build/vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTestsBootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
forceCoversAnnotation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false"
|
||||
verbose="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Core tests">
|
||||
<directory>../Tests/Functional/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>../Classes/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
34
typo3conf/ext/news/Build/Local/FunctionalTests.xml
Normal file
34
typo3conf/ext/news/Build/Local/FunctionalTests.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
Functional test suites setup
|
||||
|
||||
Functional tests should extend from \TYPO3\TestingFramework\Core\Tests\FunctionalTestCase,
|
||||
take a look at this class for further documentation on how to run the suite.
|
||||
|
||||
TYPO3 CMS functional test suite also needs phpunit bootstrap code, the
|
||||
file is located next to this .xml as FunctionalTestsBootstrap.php
|
||||
-->
|
||||
<phpunit
|
||||
backupGlobals="true"
|
||||
bootstrap="../../../../../vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTestsBootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
forceCoversAnnotation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false"
|
||||
verbose="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Core tests">
|
||||
<directory>../../Tests/Functional/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>../../Classes/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
27
typo3conf/ext/news/Build/Local/phpunit.xml
Normal file
27
typo3conf/ext/news/Build/Local/phpunit.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<phpunit
|
||||
backupGlobals="true"
|
||||
bootstrap="../../../../../../vendor/typo3/testing-framework/Resources/Core/Build/UnitTestsBootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
forceCoversAnnotation="false"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false"
|
||||
verbose="false"
|
||||
convertDeprecationsToExceptions="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Core tests">
|
||||
<directory>../../Tests/Unit/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>../../Classes/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
28
typo3conf/ext/news/Build/UnitTests.xml
Normal file
28
typo3conf/ext/news/Build/UnitTests.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<phpunit
|
||||
backupGlobals="true"
|
||||
bootstrap="../.Build/vendor/typo3/testing-framework/Resources/Core/Build/UnitTestsBootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
forceCoversAnnotation="false"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false"
|
||||
verbose="false"
|
||||
convertDeprecationsToExceptions="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="tests">
|
||||
<directory>../Tests/Unit/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>../Classes/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</phpunit>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Backend\FormDataProvider;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace GeorgRinger\News\Backend\FieldInformation;
|
||||
|
||||
use TYPO3\CMS\Backend\Form\AbstractNode;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
|
||||
class StaticText extends AbstractNode
|
||||
{
|
||||
/**
|
||||
* Handler for single nodes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function render(): array
|
||||
{
|
||||
$languageService = $this->getLanguageService();
|
||||
|
||||
$labels = [];
|
||||
foreach ((array)($this->data['renderData']['fieldInformationOptions']['labels'] ?? []) as $labelConfiguration) {
|
||||
$label = htmlspecialchars($languageService->sL($labelConfiguration['label']));
|
||||
if (!empty($labelConfiguration['italic'])) {
|
||||
$label = '<em>' . $label . '</em>';
|
||||
}
|
||||
if (!empty($labelConfiguration['bold'])) {
|
||||
$label = '<strong>' . $label . '</strong>';
|
||||
}
|
||||
$labels[] = $label;
|
||||
}
|
||||
|
||||
return [
|
||||
'requireJsModules' => [
|
||||
'TYPO3/CMS/News/TagSuggestWizard',
|
||||
],
|
||||
'html' => '<div class="form-control-wrap news-taggable">'
|
||||
. implode('<br />', $labels)
|
||||
. '</div>',
|
||||
];
|
||||
}
|
||||
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Backend\FormDataProvider;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Fill the news records with default values
|
||||
*/
|
||||
class NewsFlexFormManipulation implements FormDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* Fields which are removed in detail view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFieldsInDetailView = [
|
||||
'sDEF' => [
|
||||
'orderBy', 'orderDirection', 'categories', 'categoryConjunction', 'includeSubCategories',
|
||||
'archiveRestriction', 'timeRestriction', 'timeRestrictionHigh', 'topNewsRestriction',
|
||||
'dateField'
|
||||
],
|
||||
'additional' => [
|
||||
'limit', 'offset', 'hidePagination', 'topNewsFirst', 'listPid', 'list.paginate.itemsPerPage'
|
||||
],
|
||||
'template' => [ 'cropMaxCharacters' ],
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in list view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFieldsInListView = [
|
||||
'sDEF' => [
|
||||
'dateField', 'singleNews', 'previewHiddenRecords'
|
||||
],
|
||||
'additional' => [],
|
||||
'template' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in dateMenu view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFieldsInDateMenuView = [
|
||||
'sDEF' => [
|
||||
'orderBy', 'singleNews'
|
||||
],
|
||||
'additional' => [
|
||||
'limit', 'offset', 'hidePagination', 'topNewsFirst' , 'backPid', 'previewHiddenRecords', 'excludeAlreadyDisplayedNews',
|
||||
'list.paginate.itemsPerPage'
|
||||
],
|
||||
'template' => [
|
||||
'cropMaxCharacters', 'media.maxWidth', 'media.maxHeight'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in search form view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFieldsInSearchFormView = [
|
||||
'sDEF' => [
|
||||
'orderBy', 'orderDirection', 'categories', 'categoryConjunction', 'includeSubCategories',
|
||||
'archiveRestriction', 'timeRestriction', 'timeRestrictionHigh', 'topNewsRestriction',
|
||||
'startingpoint', 'recursive', 'dateField', 'singleNews', 'previewHiddenRecords'
|
||||
],
|
||||
'additional' => [
|
||||
'limit', 'offset', 'hidePagination', 'topNewsFirst', 'detailPid', 'backPid', 'excludeAlreadyDisplayedNews',
|
||||
'list.paginate.itemsPerPage'
|
||||
],
|
||||
'template' => [
|
||||
'cropMaxCharacters', 'media.maxWidth', 'media.maxHeight'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in category list view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFieldsInCategoryListView = [
|
||||
'sDEF' => [
|
||||
'orderBy', 'orderDirection', 'categoryConjunction', 'includeSubCategories',
|
||||
'archiveRestriction', 'timeRestriction', 'timeRestrictionHigh', 'topNewsRestriction',
|
||||
'recursive', 'dateField', 'singleNews', 'previewHiddenRecords',
|
||||
],
|
||||
'additional' => [
|
||||
'limit', 'offset', 'hidePagination', 'topNewsFirst', 'detailPid', 'backPid', 'excludeAlreadyDisplayedNews',
|
||||
'list.paginate.itemsPerPage'
|
||||
],
|
||||
'template' => [
|
||||
'cropMaxCharacters', 'media.maxWidth', 'media.maxHeight'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in tag list view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFieldsInTagListView = [
|
||||
'sDEF' => [
|
||||
'categories', 'categoryConjunction', 'includeSubCategories',
|
||||
'archiveRestriction', 'timeRestriction', 'timeRestrictionHigh', 'topNewsRestriction',
|
||||
'dateField', 'singleNews', 'previewHiddenRecords'
|
||||
],
|
||||
'additional' => [
|
||||
'hidePagination', 'topNewsFirst', 'detailPid', 'backPid', 'excludeAlreadyDisplayedNews',
|
||||
'list.paginate.itemsPerPage'
|
||||
],
|
||||
'template' => [
|
||||
'cropMaxCharacters', 'media.maxWidth', 'media.maxHeight'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var EmConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->configuration = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove fields depending on switchable controller action in tt_content
|
||||
* Restrict category selection based on configuration in tt_content
|
||||
*
|
||||
* @param array $result
|
||||
* @return array
|
||||
*/
|
||||
public function addData(array $result): array
|
||||
{
|
||||
if ($result['tableName'] === 'tt_content'
|
||||
&& $result['databaseRow']['CType'] === 'list'
|
||||
&& $result['databaseRow']['list_type'] === 'news_pi1'
|
||||
&& is_array($result['processedTca']['columns']['pi_flexform']['config']['ds'])
|
||||
) {
|
||||
$result = $this->updateFlexForms($result);
|
||||
if ($this->enabledInTsConfig($result)) {
|
||||
$result = $this->addCategoryConstraints($result);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update flexform configuration if a action is selected
|
||||
*
|
||||
* @param array $result Full data
|
||||
* @return array Modified data
|
||||
*/
|
||||
protected function updateFlexForms(array $result): array
|
||||
{
|
||||
$selectedView = '';
|
||||
$row = $result['databaseRow'];
|
||||
$dataStructure = $result['processedTca']['columns']['pi_flexform']['config']['ds'];
|
||||
|
||||
// get the first selected action
|
||||
$flexformSelection = $row['pi_flexform'];
|
||||
if (is_array($flexformSelection)
|
||||
&& is_array($flexformSelection['data'])
|
||||
&& !empty($flexformSelection['data']['sDEF']['lDEF']['switchableControllerActions']['vDEF'])
|
||||
) {
|
||||
$selectedView = $flexformSelection['data']['sDEF']['lDEF']['switchableControllerActions']['vDEF'];
|
||||
$actionParts = GeneralUtility::trimExplode(';', $selectedView, true);
|
||||
$selectedView = $actionParts[0];
|
||||
} elseif ($result['command'] === 'new') {
|
||||
// new plugin element, use List as starting view
|
||||
$selectedView = 'News->list';
|
||||
}
|
||||
|
||||
if (!empty($selectedView)) {
|
||||
// Modify the flexform structure depending on the first found action
|
||||
switch ($selectedView) {
|
||||
case 'News->list':
|
||||
case 'News->searchResult':
|
||||
$dataStructure = $this->deleteFromStructure($dataStructure, $this->removedFieldsInListView);
|
||||
break;
|
||||
case 'News->detail':
|
||||
$dataStructure = $this->deleteFromStructure($dataStructure, $this->removedFieldsInDetailView);
|
||||
break;
|
||||
case 'News->searchForm':
|
||||
$dataStructure = $this->deleteFromStructure($dataStructure, $this->removedFieldsInSearchFormView);
|
||||
break;
|
||||
case 'News->dateMenu':
|
||||
$dataStructure = $this->deleteFromStructure($dataStructure, $this->removedFieldsInDateMenuView);
|
||||
break;
|
||||
case 'Category->list':
|
||||
$dataStructure = $this->deleteFromStructure($dataStructure, $this->removedFieldsInCategoryListView);
|
||||
break;
|
||||
case 'Tag->list':
|
||||
$dataStructure = $this->deleteFromStructure($dataStructure, $this->removedFieldsInTagListView);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
$params = [
|
||||
'selectedView' => $selectedView,
|
||||
'dataStructure' => &$dataStructure,
|
||||
];
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Hooks/BackendUtility.php']['updateFlexforms'] ?? [] as $reference) {
|
||||
GeneralUtility::callUserFunction($reference, $params, $this);
|
||||
}
|
||||
}
|
||||
|
||||
$result['processedTca']['columns']['pi_flexform']['config']['ds'] = $dataStructure;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add category restriction to flexforms
|
||||
*
|
||||
* @param array $result
|
||||
* @return array Modified result
|
||||
*/
|
||||
protected function addCategoryConstraints($result): array
|
||||
{
|
||||
$structure = $result['processedTca']['columns']['pi_flexform']['config']['ds'];
|
||||
$categoryRestrictionSetting = $this->configuration->getCategoryRestriction();
|
||||
$categoryRestriction = '';
|
||||
switch ($categoryRestrictionSetting) {
|
||||
case 'current_pid':
|
||||
$categoryRestriction = ' AND sys_category.pid=###CURRENT_PID### ';
|
||||
break;
|
||||
case 'siteroot':
|
||||
$categoryRestriction = ' AND sys_category.pid IN (###SITEROOT###) ';
|
||||
break;
|
||||
case 'page_tsconfig':
|
||||
$categoryRestriction = ' AND sys_category.pid IN (###PAGE_TSCONFIG_IDLIST###) ';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($categoryRestriction) && isset($structure['sheets']['sDEF']['ROOT']['el']['settings.categories'])) {
|
||||
$structure['sheets']['sDEF']['ROOT']['el']['settings.categories']['config']['foreign_table_where'] = $categoryRestriction . $structure['sheets']['sDEF']['ROOT']['el']['settings.categories']['config']['foreign_table_where'];
|
||||
}
|
||||
$result['processedTca']['columns']['pi_flexform']['config']['ds'] = $structure;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove fields from flexform structure
|
||||
*
|
||||
* @param array &$dataStructure flexform structure
|
||||
* @param array $fieldsToBeRemoved fields which need to be removed
|
||||
* @return array Modified structure
|
||||
*/
|
||||
protected function deleteFromStructure(array $dataStructure, array $fieldsToBeRemoved): array
|
||||
{
|
||||
foreach ($fieldsToBeRemoved as $sheetName => $fieldsInSheet) {
|
||||
foreach ($fieldsInSheet as $fieldName) {
|
||||
unset($dataStructure['sheets'][$sheetName]['ROOT']['el']['settings.' . $fieldName]);
|
||||
}
|
||||
}
|
||||
return $dataStructure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $result Incoming array
|
||||
* @return bool
|
||||
*/
|
||||
protected function enabledInTsConfig(array $result): bool
|
||||
{
|
||||
return (bool)($result['pageTsConfig']['tx_news.']['categoryRestrictionForFlexForms'] ?? false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Backend\FormDataProvider;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Fill the news records with default values
|
||||
*/
|
||||
class NewsRowInitializeNew implements FormDataProviderInterface
|
||||
{
|
||||
|
||||
/** @var EmConfiguration */
|
||||
protected $emConfiguration;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->emConfiguration = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $result
|
||||
* @return array
|
||||
*/
|
||||
public function addData(array $result): array
|
||||
{
|
||||
if ($result['tableName'] !== 'tx_news_domain_model_news') {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->setTagListingId($result);
|
||||
|
||||
if ($result['command'] === 'new') {
|
||||
$result = $this->fillDateField($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $result
|
||||
* @return array
|
||||
*/
|
||||
protected function fillDateField(array $result): array
|
||||
{
|
||||
if ($this->emConfiguration->getDateTimeRequired()) {
|
||||
$result['databaseRow']['datetime'] = $GLOBALS['EXEC_TIME'];
|
||||
}
|
||||
|
||||
if (isset($result['pageTsConfig']['tx_news.']['predefine.'])
|
||||
&& is_array($result['pageTsConfig']['tx_news.']['predefine.'])
|
||||
) {
|
||||
if (isset($result['pageTsConfig']['tx_news.']['predefine.']['author']) && (int)$result['pageTsConfig']['tx_news.']['predefine.']['author'] === 1) {
|
||||
$result['databaseRow']['author'] = $GLOBALS['BE_USER']->user['realName'];
|
||||
$result['databaseRow']['author_email'] = $GLOBALS['BE_USER']->user['email'];
|
||||
}
|
||||
|
||||
if (isset($result['pageTsConfig']['tx_news.']['predefine.']['archive'])) {
|
||||
$calculatedTime = strtotime($result['pageTsConfig']['tx_news.']['predefine.']['archive']);
|
||||
|
||||
if ($calculatedTime !== false) {
|
||||
$result['databaseRow']['archive'] = $calculatedTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $result
|
||||
* @return array
|
||||
*/
|
||||
protected function setTagListingId(array $result): array
|
||||
{
|
||||
if (!isset($result['pageTsConfig']['tx_news.']['tagPid'])) {
|
||||
return $result;
|
||||
}
|
||||
$tagPid = (int)$result['pageTsConfig']['tx_news.']['tagPid'];
|
||||
if ($tagPid <= 0) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result['processedTca']['columns']['tags']['config']['fieldControl']['listModule']['options']['pid'] = $tagPid;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Backend\RecordList;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList;
|
||||
|
||||
/**
|
||||
* Class for the list rendering of administration module
|
||||
*/
|
||||
class NewsDatabaseRecordList extends DatabaseRecordList
|
||||
{
|
||||
/**
|
||||
* Creates the URL to this script, including all relevant GPvars
|
||||
* Fixed GPvars are id, table, imagemode, returnUrl, search_field, search_levels and showLimit
|
||||
* The GPvars "sortField" and "sortRev" are also included UNLESS they are found in the $excludeList variable.
|
||||
*
|
||||
* @param string $alternativeId Alternative id value. Enter blank string for the current id ($this->id)
|
||||
* @param string $excludeList Comma separated list of fields NOT to include ("sortField" or "sortRev")
|
||||
* @return string
|
||||
*/
|
||||
public function listURL($alternativeId = '', $table = '-1', $excludeList = ''): string
|
||||
{
|
||||
$urlParameters = [];
|
||||
if ((string)$alternativeId !== '') {
|
||||
$urlParameters['id'] = $alternativeId;
|
||||
} else {
|
||||
$urlParameters['id'] = $this->id;
|
||||
}
|
||||
if (isset($this->thumbs)) {
|
||||
$urlParameters['imagemode'] = $this->thumbs;
|
||||
}
|
||||
if ($this->returnUrl) {
|
||||
$urlParameters['returnUrl'] = $this->returnUrl;
|
||||
}
|
||||
if ($this->searchString) {
|
||||
$urlParameters['search_field'] = $this->searchString;
|
||||
}
|
||||
if ($this->searchLevels) {
|
||||
$urlParameters['search_levels'] = $this->searchLevels;
|
||||
}
|
||||
if ($this->showLimit) {
|
||||
$urlParameters['showLimit'] = $this->showLimit;
|
||||
}
|
||||
if (isset($this->firstElementNumber)) {
|
||||
$urlParameters['pointer'] = $this->firstElementNumber;
|
||||
}
|
||||
if ((!$excludeList || !GeneralUtility::inList(
|
||||
$excludeList,
|
||||
'sortField'
|
||||
)) && $this->sortField
|
||||
) {
|
||||
$urlParameters['sortField'] = $this->sortField;
|
||||
}
|
||||
if ((!$excludeList || !GeneralUtility::inList(
|
||||
$excludeList,
|
||||
'sortRev'
|
||||
)) && $this->sortRev
|
||||
) {
|
||||
$urlParameters['sortRev'] = $this->sortRev;
|
||||
}
|
||||
if (GeneralUtility::_GP('SET')) {
|
||||
$urlParameters['SET'] = GeneralUtility::_GP('SET');
|
||||
}
|
||||
if (GeneralUtility::_GP('show')) {
|
||||
$urlParameters['show'] = (int)GeneralUtility::_GP('show');
|
||||
}
|
||||
|
||||
$demand = GeneralUtility::_GET('tx_news_web_newsadministration');
|
||||
if (isset($demand['demand']) && is_array($demand['demand'])) {
|
||||
$urlParameters['tx_news_web_newsadministration']['demand'] = $demand['demand'];
|
||||
}
|
||||
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
return $uriBuilder->buildUriFromRoute('web_NewsAdministration', $urlParameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Backend\RecordList;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Service\CategoryService;
|
||||
use GeorgRinger\News\Utility\ConstraintHelper;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class for the list rendering of administration module
|
||||
*/
|
||||
class RecordListConstraint
|
||||
{
|
||||
const TABLE = 'tx_news_domain_model_news';
|
||||
|
||||
/**
|
||||
* Check if current module is the news administration module
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInAdministrationModule(): bool
|
||||
{
|
||||
$vars = GeneralUtility::_GET('route');
|
||||
return strpos($vars, '/module/web/NewsAdministration') !== false;
|
||||
}
|
||||
|
||||
public function extendQuery(array &$parameters, array $arguments): void
|
||||
{
|
||||
$parameters['whereDoctrine'] = [];
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_news_domain_model_news');
|
||||
|
||||
$expressionBuilder = $queryBuilder->expr();
|
||||
|
||||
// search word
|
||||
if (isset($arguments['searchWord']) && !empty($arguments['searchWord'])) {
|
||||
$words = GeneralUtility::trimExplode(' ', $arguments['searchWord'], true);
|
||||
$fields = ['title', 'teaser', 'bodytext'];
|
||||
$fieldParts = [];
|
||||
foreach ($fields as $field) {
|
||||
$likeParts = [];
|
||||
$nameParts = str_getcsv($arguments['searchWord'], ' ');
|
||||
foreach ($nameParts as $part) {
|
||||
$part = trim($part);
|
||||
if ($part !== '') {
|
||||
$likeParts[] = $expressionBuilder->like($field, $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($part) . '%'));
|
||||
}
|
||||
}
|
||||
if (!empty($likeParts)) {
|
||||
$fieldParts[] = $expressionBuilder->orX(...$likeParts);
|
||||
}
|
||||
}
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->orX(...$fieldParts);
|
||||
$parameters['where'][] = $expressionBuilder->orX(...$fieldParts);
|
||||
}
|
||||
// top news
|
||||
$topNewsSetting = (int)$arguments['topNewsRestriction'];
|
||||
if ($topNewsSetting > 0) {
|
||||
if ($topNewsSetting === 1) {
|
||||
$parameters['where'][] = 'istopnews=1';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('istopnews', 1);
|
||||
} elseif ($topNewsSetting === 2) {
|
||||
$parameters['where'][] = 'istopnews=0';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('istopnews', 0);
|
||||
}
|
||||
}
|
||||
|
||||
// archived (1==active, 2==archived)
|
||||
$archived = (int)$arguments['archived'];
|
||||
if ($archived > 0) {
|
||||
$currentTime = $GLOBALS['EXEC_TIME'];
|
||||
if ($archived === 1) {
|
||||
$parameters['where'][] = '(archive > ' . $currentTime . ' OR archive=0)';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->orX(
|
||||
$expressionBuilder->gt('archive', $currentTime),
|
||||
$expressionBuilder->eq('archive', 0)
|
||||
);
|
||||
} elseif ($archived === 2) {
|
||||
$parameters['where'][] = 'archive > 0 AND archive <' . $currentTime;
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->andX(
|
||||
$expressionBuilder->gt('archive', 0),
|
||||
$expressionBuilder->lt('archive', $currentTime)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// hidden
|
||||
$hidden = (int)$arguments['hidden'];
|
||||
if ($hidden > 0) {
|
||||
if ($hidden === 1) {
|
||||
$parameters['where'][] = 'hidden=1';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('hidden', 1);
|
||||
} elseif ($hidden === 2) {
|
||||
$parameters['where'][] = 'hidden=0';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('hidden', 0);
|
||||
}
|
||||
}
|
||||
|
||||
// time constraint low
|
||||
if (isset($arguments['timeRestriction']) && !empty($arguments['timeRestriction'])) {
|
||||
try {
|
||||
$limit = ConstraintHelper::getTimeRestrictionLow($arguments['timeRestriction']);
|
||||
$parameters['where'][] = 'datetime >=' . $limit;
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->gte('datetime', $limit);
|
||||
} catch (\Exception $e) {
|
||||
// @todo add flash message
|
||||
}
|
||||
}
|
||||
|
||||
// time constraint high
|
||||
if (isset($arguments['timeRestrictionHigh']) && !empty($arguments['timeRestrictionHigh'])) {
|
||||
try {
|
||||
$limit = ConstraintHelper::getTimeRestrictionHigh($arguments['timeRestrictionHigh']);
|
||||
$parameters['where'][] = 'datetime <=' . $limit;
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->lte('datetime', $limit);
|
||||
} catch (\Exception $e) {
|
||||
// @todo add flash message
|
||||
}
|
||||
}
|
||||
|
||||
// categories
|
||||
if (isset($arguments['selectedCategories']) && is_array($arguments['selectedCategories'])) {
|
||||
$categoryMode = strtolower($arguments['categoryConjunction']);
|
||||
foreach ($arguments['selectedCategories'] as $key => $category) {
|
||||
if ((int)$category === 0) {
|
||||
unset($arguments['selectedCategories'][$key]);
|
||||
}
|
||||
}
|
||||
if (!empty($arguments['selectedCategories'])) {
|
||||
if ((int)$arguments['includeSubCategories'] === 1) {
|
||||
$categoryList = implode(',', $arguments['selectedCategories']);
|
||||
$listWithSubCategories = CategoryService::getChildrenCategories($categoryList);
|
||||
$arguments['selectedCategories'] = explode(',', $listWithSubCategories);
|
||||
}
|
||||
switch ($categoryMode) {
|
||||
case 'and':
|
||||
foreach ($arguments['selectedCategories'] as $category) {
|
||||
$idList = $this->getNewsIdsOfCategory($category);
|
||||
if (empty($idList)) {
|
||||
$parameters['where'][] = '1=2';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('uid', 0);
|
||||
} else {
|
||||
$parameters['where'][] = sprintf('uid IN(%s)', implode(',', $idList));
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->in('uid', $idList);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'or':
|
||||
$orConstraint = $orConstraintDoctrine = [];
|
||||
foreach ($arguments['selectedCategories'] as $category) {
|
||||
$idList = $this->getNewsIdsOfCategory($category);
|
||||
if (!empty($idList)) {
|
||||
$orConstraint[] = sprintf('uid IN(%s)', implode(',', $idList));
|
||||
$orConstraintDoctrine[] = $expressionBuilder->in('uid', $idList);
|
||||
}
|
||||
}
|
||||
if (empty($orConstraint)) {
|
||||
$parameters['where'][] = '1=2';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('uid', 0);
|
||||
} else {
|
||||
$parameters['where'][] = implode(' OR ', $orConstraint);
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->orX(...$orConstraintDoctrine);
|
||||
}
|
||||
break;
|
||||
// @todo test that
|
||||
case 'notor':
|
||||
$orConstraint = $orConstraintDoctrine = [];
|
||||
foreach ($arguments['selectedCategories'] as $category) {
|
||||
$idList = $this->getNewsIdsOfCategory($category);
|
||||
if (!empty($idList)) {
|
||||
$orConstraint[] = sprintf('(uid IN (%s))', implode(',', $idList));
|
||||
$orConstraintDoctrine[] = $expressionBuilder->notIn('uid', $idList);
|
||||
} else {
|
||||
$orConstraint[] = '1=2';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('uid', 0);
|
||||
}
|
||||
}
|
||||
if (empty($orConstraint)) {
|
||||
$parameters['where'][] = '1=2';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->eq('uid', 0);
|
||||
} else {
|
||||
$orConstraint = array_unique($orConstraint);
|
||||
$parameters['where'][] = ' NOT (' . implode(' OR ', $orConstraint) . ')';
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->andX(...$orConstraintDoctrine);
|
||||
}
|
||||
break;
|
||||
case 'notand':
|
||||
foreach ($arguments['selectedCategories'] as $category) {
|
||||
$idList = $this->getNewsIdsOfCategory($category);
|
||||
if (!empty($idList)) {
|
||||
$parameters['where'][] = sprintf('uid NOT IN(%s)', implode(',', $idList));
|
||||
$parameters['whereDoctrine'][] = $expressionBuilder->notIn('uid', $idList);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// order
|
||||
if (isset($arguments['sortingField']) && isset($GLOBALS['TCA']['tx_news_domain_model_news']['columns'][$arguments['sortingField']])) {
|
||||
$direction = ($arguments['sortingDirection'] === 'asc' || $arguments['sortingDirection'] === 'desc') ? $arguments['sortingDirection'] : '';
|
||||
$parameters['orderBy'] = [[$arguments['sortingField'], $direction]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $categoryId
|
||||
* @return array
|
||||
*/
|
||||
protected function getNewsIdsOfCategory($categoryId): array
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_news_domain_model_news');
|
||||
$queryBuilder->getRestrictions()
|
||||
->removeByType(StartTimeRestriction::class)
|
||||
->removeByType(HiddenRestriction::class)
|
||||
->removeByType(EndTimeRestriction::class);
|
||||
$res = $queryBuilder
|
||||
->select('tx_news_domain_model_news.uid', 'sys_category.title')
|
||||
->from('tx_news_domain_model_news')
|
||||
->rightJoin(
|
||||
'tx_news_domain_model_news',
|
||||
'sys_category_record_mm',
|
||||
'sys_category_record_mm',
|
||||
$queryBuilder->expr()->eq('tx_news_domain_model_news.uid', $queryBuilder->quoteIdentifier('sys_category_record_mm.uid_foreign'))
|
||||
)
|
||||
->rightJoin(
|
||||
'sys_category_record_mm',
|
||||
'sys_category',
|
||||
'sys_category',
|
||||
$queryBuilder->expr()->eq('sys_category.uid', $queryBuilder->quoteIdentifier('sys_category_record_mm.uid_local'))
|
||||
)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->isNotNull('tx_news_domain_model_news.uid'),
|
||||
$queryBuilder->expr()->eq('sys_category.uid', $queryBuilder->createNamedParameter($categoryId, \PDO::PARAM_INT))
|
||||
)
|
||||
->execute();
|
||||
|
||||
$idList = [];
|
||||
while ($row = $res->fetch()) {
|
||||
$idList[] = $row['uid'];
|
||||
}
|
||||
|
||||
return $idList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Backend\Wizard;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\StringUtility;
|
||||
|
||||
class SuggestWizardReceiver extends SuggestWizardDefaultReceiver
|
||||
{
|
||||
public function queryTable(&$params, $recursionCounter = 0)
|
||||
{
|
||||
$rows = parent::queryTable($params, $recursionCounter);
|
||||
|
||||
$searchString = strtolower($params['value']);
|
||||
$matchRow = array_filter($rows, function ($value) use ($searchString) {
|
||||
return strtolower($value['label']) === $searchString;
|
||||
});
|
||||
|
||||
if (empty($matchRow)) {
|
||||
$newUid = StringUtility::getUniqueId('NEW');
|
||||
$rows[$this->table . '_' . $newUid] = [
|
||||
'class' => '',
|
||||
'label' => $params['value'],
|
||||
'path' => '',
|
||||
'sprite' => $this->iconFactory->getIconForRecord($this->table, [], Icon::SIZE_SMALL)->render(),
|
||||
'style' => '',
|
||||
'table' => $this->table,
|
||||
'text' => sprintf($this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:tag_suggest'), $params['value']),
|
||||
'uid' => $newUid,
|
||||
];
|
||||
$configuration = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
$pid = $configuration->getTagPid();
|
||||
if ($pid === 0) {
|
||||
$pid = $this->getTagPidFromTsConfig($params['uid']);
|
||||
}
|
||||
|
||||
if ($pid !== 0) {
|
||||
$rows[$this->table . '_' . $newUid]['pid'] = $pid;
|
||||
}
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pid for tags from TsConfig
|
||||
*
|
||||
* @param int $newsUid uid of current news record
|
||||
* @return int
|
||||
*/
|
||||
protected function getTagPidFromTsConfig($newsUid): int
|
||||
{
|
||||
$pid = 0;
|
||||
|
||||
$newsRecord = BackendUtilityCore::getRecord('tx_news_domain_model_news', (int)$newsUid);
|
||||
|
||||
$pagesTsConfig = BackendUtilityCore::getPagesTSconfig($newsRecord['pid']);
|
||||
if (isset($pagesTsConfig['tx_news.']) && isset($pagesTsConfig['tx_news.']['tagPid'])) {
|
||||
$pid = (int)$pagesTsConfig['tx_news.']['tagPid'];
|
||||
}
|
||||
|
||||
return $pid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,649 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Controller;
|
||||
|
||||
use GeorgRinger\News\Backend\RecordList\NewsDatabaseRecordList;
|
||||
use GeorgRinger\News\Domain\Model\Dto\AdministrationDemand;
|
||||
use GeorgRinger\News\Domain\Repository\AdministrationRepository;
|
||||
use GeorgRinger\News\Domain\Repository\CategoryRepository;
|
||||
use GeorgRinger\News\Domain\Repository\NewsRepository;
|
||||
use GeorgRinger\News\Domain\Repository\TagRepository;
|
||||
use GeorgRinger\News\Event\AdministrationExtendMenuEvent;
|
||||
use GeorgRinger\News\Event\AdministrationIndexActionEvent;
|
||||
use GeorgRinger\News\Event\AdministrationNewsPidListingActionEvent;
|
||||
use GeorgRinger\News\Utility\Page;
|
||||
use TYPO3\CMS\Backend\Clipboard\Clipboard;
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\Template\Components\Menu\Menu;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
|
||||
use TYPO3\CMS\Backend\View\BackendTemplateView;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Administration controller
|
||||
*/
|
||||
class AdministrationController extends NewsController
|
||||
{
|
||||
/** @var int */
|
||||
protected $pageUid = 0;
|
||||
|
||||
/** @var array */
|
||||
protected $tsConfiguration = [];
|
||||
|
||||
/**
|
||||
* @var \GeorgRinger\News\Domain\Repository\AdministrationRepository
|
||||
*/
|
||||
protected $administrationRepository;
|
||||
|
||||
/** @var ConfigurationManagerInterface */
|
||||
protected $configurationManager;
|
||||
|
||||
/** @var array */
|
||||
protected $pageInformation = [];
|
||||
|
||||
/** @var array */
|
||||
protected $allowedNewTables = [];
|
||||
|
||||
/** @var array */
|
||||
protected $deniedNewTables = [];
|
||||
|
||||
public function initializeAction()
|
||||
{
|
||||
$this->pageUid = (int)GeneralUtility::_GET('id');
|
||||
$this->pageInformation = BackendUtilityCore::readPageAccess($this->pageUid, '');
|
||||
$this->setTsConfig();
|
||||
parent::initializeAction();
|
||||
}
|
||||
|
||||
/** @var BackendTemplateView */
|
||||
protected $view;
|
||||
|
||||
/** @var IconFactory */
|
||||
protected $iconFactory;
|
||||
|
||||
/** @var string */
|
||||
protected $defaultViewObjectName = BackendTemplateView::class;
|
||||
|
||||
/**
|
||||
* AdministrationController constructor.
|
||||
* @param NewsRepository $newsRepository
|
||||
* @param CategoryRepository $categoryRepository
|
||||
* @param TagRepository $tagRepository
|
||||
* @param ConfigurationManagerInterface $configurationManager
|
||||
* @param AdministrationRepository $administrationRepository
|
||||
* @param AdministrationRepository $iconFactory
|
||||
*/
|
||||
public function __construct(
|
||||
NewsRepository $newsRepository,
|
||||
CategoryRepository $categoryRepository,
|
||||
TagRepository $tagRepository,
|
||||
ConfigurationManagerInterface $configurationManager,
|
||||
AdministrationRepository $administrationRepository,
|
||||
IconFactory $iconFactory
|
||||
) {
|
||||
parent::__construct($newsRepository, $categoryRepository, $tagRepository, $configurationManager);
|
||||
$this->administrationRepository = $administrationRepository;
|
||||
$this->iconFactory = $iconFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ViewInterface $view
|
||||
*/
|
||||
protected function initializeView(ViewInterface $view)
|
||||
{
|
||||
parent::initializeView($view);
|
||||
$view->getModuleTemplate()->getDocHeaderComponent()->setMetaInformation([]);
|
||||
|
||||
$pageRenderer = $this->view->getModuleTemplate()->getPageRenderer();
|
||||
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DateTimePicker');
|
||||
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
|
||||
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/AjaxDataHandler');
|
||||
$pageRenderer->loadRequireJsModule('TYPO3/CMS/News/AdministrationModule');
|
||||
$pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf');
|
||||
$dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] : ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY']);
|
||||
$pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
|
||||
|
||||
$web_list_modTSconfig = BackendUtilityCore::getPagesTSconfig($this->pageUid)['mod.']['web_list.'] ?? [];
|
||||
$this->allowedNewTables = GeneralUtility::trimExplode(
|
||||
',',
|
||||
$web_list_modTSconfig['allowedNewTables'] ?? '',
|
||||
true
|
||||
);
|
||||
$this->deniedNewTables = GeneralUtility::trimExplode(
|
||||
',',
|
||||
$web_list_modTSconfig['deniedNewTables'] ?? '',
|
||||
true
|
||||
);
|
||||
|
||||
$this->createMenu();
|
||||
$this->createButtons();
|
||||
|
||||
$view->assign('showSupportArea', $this->showSupportArea());
|
||||
}
|
||||
|
||||
protected function createMenu(): void
|
||||
{
|
||||
$uriBuilder = $this->objectManager->get(UriBuilder::class);
|
||||
$uriBuilder->setRequest($this->request);
|
||||
|
||||
$menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
|
||||
$menu->setIdentifier('news');
|
||||
|
||||
$actions = [
|
||||
['action' => 'index', 'label' => 'newsListing'],
|
||||
['action' => 'newsPidListing', 'label' => 'newsPidListing'],
|
||||
['action' => 'donate', 'label' => 'donate']
|
||||
];
|
||||
foreach ($actions as $action) {
|
||||
$item = $menu->makeMenuItem()
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:module.' . $action['label']))
|
||||
->setHref($uriBuilder->uriFor($action['action'], [], 'Administration', 'News', 'web_NewsAdministration'))
|
||||
->setActive($this->request->getControllerActionName() === $action['action']);
|
||||
$menu->addMenuItem($item);
|
||||
}
|
||||
|
||||
$menu = $this->extendMenu($menu);
|
||||
|
||||
if ($menu instanceof Menu) {
|
||||
$this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
|
||||
}
|
||||
if (is_array($this->pageInformation)) {
|
||||
$this->view->getModuleTemplate()->getDocHeaderComponent()->setMetaInformation($this->pageInformation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends menu selector with items from 3rd party extensions.
|
||||
*
|
||||
* @param Menu $menu
|
||||
* @return Menu
|
||||
*/
|
||||
protected function extendMenu(Menu $menu): Menu
|
||||
{
|
||||
$event = $this->eventDispatcher->dispatch(new AdministrationExtendMenuEvent($this, $menu));
|
||||
|
||||
return $event->getMenu();
|
||||
}
|
||||
|
||||
protected function createButtons(): void
|
||||
{
|
||||
$buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar();
|
||||
|
||||
$uriBuilder = $this->objectManager->get(UriBuilder::class);
|
||||
$uriBuilder->setRequest($this->request);
|
||||
|
||||
if ($this->request->getControllerActionName() === 'index' && $this->isFilteringEnabled()) {
|
||||
$toggleButton = $buttonBar->makeLinkButton()
|
||||
->setHref('#')
|
||||
->setDataAttributes([
|
||||
'togglelink' => '1',
|
||||
'toggle' => 'tooltip',
|
||||
'placement' => 'bottom',
|
||||
])
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:administration.toggleForm'))
|
||||
->setIcon($this->iconFactory->getIcon('actions-filter', Icon::SIZE_SMALL));
|
||||
$buttonBar->addButton($toggleButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
|
||||
}
|
||||
|
||||
$buttons = [
|
||||
[
|
||||
'table' => 'tx_news_domain_model_news',
|
||||
'label' => 'module.createNewNewsRecord',
|
||||
'action' => 'newNews',
|
||||
'icon' => 'ext-news-type-default'
|
||||
],
|
||||
[
|
||||
'table' => 'tx_news_domain_model_tag',
|
||||
'label' => 'module.createNewTag',
|
||||
'action' => 'newTag',
|
||||
'icon' => 'ext-news-tag'
|
||||
],
|
||||
[
|
||||
'table' => 'sys_category',
|
||||
'label' => 'module.createNewCategory',
|
||||
'action' => 'newCategory',
|
||||
'icon' => 'mimetypes-x-sys_category'
|
||||
]
|
||||
];
|
||||
foreach ($buttons as $key => $tableConfiguration) {
|
||||
if ($this->showButton($tableConfiguration['table'])) {
|
||||
$title = $this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:' . $tableConfiguration['label']);
|
||||
$viewButton = $buttonBar->makeLinkButton()
|
||||
->setHref($uriBuilder->reset()->setRequest($this->request)->uriFor(
|
||||
$tableConfiguration['action'],
|
||||
[],
|
||||
'Administration'
|
||||
))
|
||||
->setDataAttributes([
|
||||
'toggle' => 'tooltip',
|
||||
'placement' => 'bottom',
|
||||
'title' => $title])
|
||||
->setTitle($title)
|
||||
->setIcon($this->iconFactory->getIcon($tableConfiguration['icon'], Icon::SIZE_SMALL, 'overlay-new'));
|
||||
$buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
|
||||
}
|
||||
}
|
||||
|
||||
$clipBoard = GeneralUtility::makeInstance(Clipboard::class);
|
||||
$clipBoard->initializeClipboard();
|
||||
$elFromTable = $clipBoard->elFromTable('tx_news_domain_model_news');
|
||||
if (!empty($elFromTable)) {
|
||||
$viewButton = $buttonBar->makeLinkButton()
|
||||
->setHref($clipBoard->pasteUrl('', $this->pageUid))
|
||||
->setOnClick('return ' . $clipBoard->confirmMsgText(
|
||||
'pages',
|
||||
BackendUtilityCore::getRecord('pages', $this->pageUid),
|
||||
'into',
|
||||
$elFromTable
|
||||
))
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:clip_pasteInto'))
|
||||
->setIcon($this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL));
|
||||
$buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
|
||||
}
|
||||
|
||||
// Donation
|
||||
$donationButton = $buttonBar->makeLinkButton()
|
||||
->setHref($uriBuilder->reset()->setRequest($this->request)->uriFor(
|
||||
'donate',
|
||||
[],
|
||||
'Administration'
|
||||
))
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:administration.donation.title'))
|
||||
->setIcon($this->iconFactory->getIcon('ext-news-donation', Icon::SIZE_SMALL));
|
||||
$buttonBar->addButton($donationButton, ButtonBar::BUTTON_POSITION_RIGHT);
|
||||
|
||||
// Refresh
|
||||
$refreshButton = $buttonBar->makeLinkButton()
|
||||
->setHref(GeneralUtility::getIndpEnv('REQUEST_URI'))
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload'))
|
||||
->setIcon($this->iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL));
|
||||
$buttonBar->addButton($refreshButton, ButtonBar::BUTTON_POSITION_RIGHT);
|
||||
|
||||
// Shortcut
|
||||
if ($this->getBackendUser()->mayMakeShortcut()) {
|
||||
$shortcutButton = $buttonBar->makeShortcutButton()
|
||||
->setGetVariables(['route', 'module', 'id'])
|
||||
->setModuleName($this->request->getPluginName())
|
||||
->setDisplayName('Shortcut');
|
||||
$buttonBar->addButton($shortcutButton, ButtonBar::BUTTON_POSITION_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
protected function showButton(string $table): bool
|
||||
{
|
||||
if (!$this->getBackendUser()->check('tables_modify', $table)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No deny/allow tables are set:
|
||||
if (empty($this->allowedNewTables) && empty($this->deniedNewTables)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$showButton = !in_array($table, $this->deniedNewTables, true) &&
|
||||
(empty($this->allowedNewTables) || in_array($table, $this->allowedNewTables, true));
|
||||
|
||||
return $showButton;
|
||||
}
|
||||
|
||||
public function indexAction(): void
|
||||
{
|
||||
$this->redirectToPageOnStart();
|
||||
|
||||
$demandVars = GeneralUtility::_GET('tx_news_web_newsadministration');
|
||||
$demand = GeneralUtility::makeInstance(AdministrationDemand::class);
|
||||
$autoSubmitForm = 0;
|
||||
if (!empty($demandVars['demand'] ?? [])) {
|
||||
foreach ($demandVars['demand'] as $key => $value) {
|
||||
if (property_exists(AdministrationDemand::class, $key)) {
|
||||
$getter = 'set' . ucfirst($key);
|
||||
$demand->$getter($value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Preselect by TsConfig (e.g. tx_news.module.preselect.topNewsRestriction = 1)
|
||||
if (isset($this->tsConfiguration['preselect.'])
|
||||
&& is_array($this->tsConfiguration['preselect.'])
|
||||
) {
|
||||
$anyPropertySet = false;
|
||||
unset($this->tsConfiguration['preselect.']['orderByAllowed']);
|
||||
|
||||
foreach ($this->tsConfiguration['preselect.'] as $propertyName => $propertyValue) {
|
||||
$propertySet = ObjectAccess::setProperty($demand, $propertyName, $propertyValue);
|
||||
if ($propertySet) {
|
||||
$anyPropertySet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($anyPropertySet && !GeneralUtility::_GET('formSubmitted')) {
|
||||
$autoSubmitForm = 1;
|
||||
}
|
||||
}
|
||||
if (!(bool)($this->tsConfiguration['alwaysShowFilter'] ?? false) || !$this->isFilteringEnabled()) {
|
||||
$this->view->assign('hideForm', true);
|
||||
}
|
||||
}
|
||||
$this->view->assign('autoSubmitForm', $autoSubmitForm);
|
||||
|
||||
$categories = $this->categoryRepository->findParentCategoriesByPid($this->pageUid);
|
||||
$idList = [];
|
||||
foreach ($categories as $c) {
|
||||
$idList[] = $c->getUid();
|
||||
}
|
||||
if (empty($idList) && !$this->getBackendUser()->isAdmin()) {
|
||||
$idList = $this->getBackendUser()->getCategoryMountPoints();
|
||||
}
|
||||
|
||||
if (!empty($this->tsConfiguration['allowedCategoryRootIds'])) {
|
||||
$allowedList = GeneralUtility::intExplode(',', $this->tsConfiguration['allowedCategoryRootIds'], true);
|
||||
if (!empty($allowedList)) {
|
||||
$idList = array_intersect($idList, $allowedList);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the dblist object:
|
||||
$dblist = GeneralUtility::makeInstance(NewsDatabaseRecordList::class);
|
||||
$this->view->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ActionDispatcher');
|
||||
$this->view->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
|
||||
|
||||
$pageinfo = BackendUtilityCore::readPageAccess($this->pageUid, $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW));
|
||||
$dblist->pageRow = $pageinfo;
|
||||
$majorVersion = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
|
||||
if ($majorVersion <= 10) {
|
||||
$dblist->calcPerms = Permission::CONTENT_EDIT;
|
||||
} else {
|
||||
$dblist->calcPerms->set(Permission::CONTENT_EDIT);
|
||||
if ($majorVersion >= 11) {
|
||||
$dblist->displayColumnSelector = false;
|
||||
$dblist->displayRecordDownload = false;
|
||||
}
|
||||
}
|
||||
$dblist->disableSingleTableView = true;
|
||||
$dblist->clickTitleMode = 'edit';
|
||||
$dblist->allFields = 1;
|
||||
$dblist->displayFields = false;
|
||||
$dblist->dontShowClipControlPanels = true;
|
||||
$pointer = MathUtility::forceIntegerInRange(GeneralUtility::_GP('pointer'), 0);
|
||||
$limit = isset($this->settings['list']['paginate']['itemsPerPage']) ? (int)$this->settings['list']['paginate']['itemsPerPage'] : 20;
|
||||
$dblist->start(
|
||||
$this->pageUid,
|
||||
'tx_news_domain_model_news',
|
||||
$pointer,
|
||||
'',
|
||||
$demand->getRecursive(),
|
||||
$limit
|
||||
);
|
||||
$dblist->setDispFields();
|
||||
$dblist->noControlPanels = !(bool)($this->tsConfiguration['controlPanels'] ?? false);
|
||||
$dblist->setFields = [
|
||||
'tx_news_domain_model_news' => GeneralUtility::trimExplode(',', $this->tsConfiguration['columns'] ?? 'teaser,istopnews,datetime,categories', true)
|
||||
];
|
||||
|
||||
$tableRendering = $dblist->generateList();
|
||||
if (!$tableRendering && GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() <= 10) {
|
||||
$tableRendering = $dblist->HTMLcode;
|
||||
}
|
||||
$tableRendering = trim($tableRendering);
|
||||
|
||||
$counter = !empty($tableRendering);
|
||||
$this->view->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Recordlist');
|
||||
|
||||
$assignedValues = [
|
||||
'moduleToken' => $this->getToken(true),
|
||||
'page' => $this->pageUid,
|
||||
'demand' => $demand,
|
||||
'news' => $tableRendering,
|
||||
'newsCount' => $counter,
|
||||
'showSearchForm' => (!is_null($demand) || $counter > 0),
|
||||
'requestUri' => GeneralUtility::quoteJSvalue(rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI'))),
|
||||
'categories' => $this->categoryRepository->findTree($idList),
|
||||
'filters' => $this->tsConfiguration['filters.'],
|
||||
'enableFiltering' => $this->isFilteringEnabled(),
|
||||
'dateformat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy']
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new AdministrationIndexActionEvent($this, $assignedValues));
|
||||
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a page tree including count of news + category records
|
||||
*/
|
||||
public function newsPidListingAction(int $treeLevel = 2): void
|
||||
{
|
||||
$tree = Page::pageTree($this->pageUid, $treeLevel);
|
||||
|
||||
$rawTree = [];
|
||||
foreach ($tree->tree as $row) {
|
||||
$this->countRecordsOnPage($row);
|
||||
$rawTree[] = $row;
|
||||
}
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new AdministrationNewsPidListingActionEvent($this, $rawTree, $treeLevel));
|
||||
|
||||
$this->view->assignMultiple([
|
||||
'tree' => $event->getRawTree(),
|
||||
'treeLevel' => $event->getTreeLevel(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function donateAction(): void
|
||||
{
|
||||
$this->view->assignMultiple([
|
||||
'counts' => $this->administrationRepository->getTotalCounts()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to form to create a news record
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function newNewsAction(): void
|
||||
{
|
||||
$this->redirectToCreateNewRecord('tx_news_domain_model_news');
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to form to create a category record
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function newCategoryAction(): void
|
||||
{
|
||||
$this->redirectToCreateNewRecord('sys_category');
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to form to create a tag record
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function newTagAction(): void
|
||||
{
|
||||
$this->redirectToCreateNewRecord('tx_news_domain_model_tag');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update page record array with count of news & category records
|
||||
*
|
||||
* @param array $row page record
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function countRecordsOnPage(array &$row): void
|
||||
{
|
||||
$pageUid = (int)$row['row']['uid'];
|
||||
|
||||
$counts = [
|
||||
'countNews' => 'tx_news_domain_model_news',
|
||||
'countCategories' => 'sys_category',
|
||||
'countTags' => 'tx_news_domain_model_tag',
|
||||
];
|
||||
foreach ($counts as $key => $table) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
$row[$key] = $queryBuilder->count('*')
|
||||
->from($table)
|
||||
->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageUid, \PDO::PARAM_INT)))
|
||||
->execute()
|
||||
->fetchColumn(0);
|
||||
}
|
||||
|
||||
$row['countAll'] = ($row['countNews'] + $row['countCategories'] + $row['countTags']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to creating a new record
|
||||
*
|
||||
* @param string $table table name
|
||||
*/
|
||||
private function redirectToCreateNewRecord(string $table): void
|
||||
{
|
||||
$pid = $this->pageUid;
|
||||
if ($pid === 0 && isset($this->tsConfiguration['defaultPid.'])
|
||||
&& is_array($this->tsConfiguration['defaultPid.'])
|
||||
&& isset($this->tsConfiguration['defaultPid.'][$table])
|
||||
) {
|
||||
$pid = (int)$this->tsConfiguration['defaultPid.'][$table];
|
||||
}
|
||||
|
||||
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
|
||||
|
||||
$returnUrl = $uriBuilder->buildUriFromRoutePath('/module/web/NewsAdministration/', [
|
||||
'id' => $this->pageUid,
|
||||
'token' => $this->getToken(true)
|
||||
]);
|
||||
|
||||
$params = [
|
||||
'edit[' . $table . '][' . $pid . ']' => 'new',
|
||||
'returnUrl' => (string)$returnUrl
|
||||
];
|
||||
$url = $uriBuilder->buildUriFromRoute('record_edit', $params);
|
||||
HttpUtility::redirect((string)$url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TsConfig configuration for the extension
|
||||
*/
|
||||
protected function setTsConfig(): void
|
||||
{
|
||||
$tsConfig = BackendUtilityCore::getPagesTSconfig($this->pageUid);
|
||||
if (isset($tsConfig['tx_news.']['module.']) && is_array($tsConfig['tx_news.']['module.'])) {
|
||||
$this->tsConfiguration = $tsConfig['tx_news.']['module.'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if at least one filter is enabled
|
||||
*/
|
||||
protected function isFilteringEnabled(): bool
|
||||
{
|
||||
if (isset($this->tsConfiguration['filters.'])) {
|
||||
foreach ($this->tsConfiguration['filters.'] as $filter => $enabled) {
|
||||
if ($enabled == 1) {
|
||||
// Check dependencies on other filter
|
||||
if (($filter === 'categoryConjunction' || $filter === 'includeSubCategories')
|
||||
&& $this->tsConfiguration['filters.']['categories'] == 0) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If defined in TsConfig with tx_news.module.redirectToPageOnStart = 123
|
||||
* and the current page id is 0, a redirect to the given page will be done
|
||||
*/
|
||||
protected function redirectToPageOnStart(): void
|
||||
{
|
||||
$allowedPage = (int)($this->tsConfiguration['allowedPage'] ?? 0);
|
||||
$redirectPageOnStart = (int)($this->tsConfiguration['redirectToPageOnStart'] ?? 0);
|
||||
if ($allowedPage > 0 && $this->pageUid !== $allowedPage) {
|
||||
$id = $allowedPage;
|
||||
} elseif ($this->pageUid === 0 && $redirectPageOnStart > 0) {
|
||||
$id = $redirectPageOnStart;
|
||||
}
|
||||
|
||||
if (!empty($id)) {
|
||||
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
|
||||
$uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
|
||||
$url = $uriBuilder->buildUriFromRoutePath('/module/web/NewsAdministration/', [
|
||||
'id' => $id,
|
||||
'token' => $this->getToken(true)
|
||||
]);
|
||||
HttpUtility::redirect((string)$url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a CSRF token
|
||||
*
|
||||
* @param bool $tokenOnly Set it to TRUE to get only the token, otherwise including the &moduleToken= as prefix
|
||||
*/
|
||||
protected function getToken(bool $tokenOnly = false): string
|
||||
{
|
||||
$tokenParameterName = 'token';
|
||||
$token = FormProtectionFactory::get('backend')->generateToken('route', 'web_NewsAdministration');
|
||||
|
||||
if ($tokenOnly) {
|
||||
return $token;
|
||||
}
|
||||
|
||||
return '&' . $tokenParameterName . '=' . $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show support area only for admins in given percent of time
|
||||
*
|
||||
* @param int $probabilityInPercent
|
||||
*/
|
||||
private function showSupportArea(int $probabilityInPercent = 10): bool
|
||||
{
|
||||
if (!$this->getBackendUser()->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mt_rand() % 100 <= $probabilityInPercent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
protected function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
51
typo3conf/ext/news/Classes/Controller/CategoryController.php
Normal file
51
typo3conf/ext/news/Classes/Controller/CategoryController.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Controller;
|
||||
|
||||
use GeorgRinger\News\Event\CategoryListActionEvent;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Category controller
|
||||
*/
|
||||
class CategoryController extends NewsController
|
||||
{
|
||||
/**
|
||||
* List categories
|
||||
*
|
||||
* @param array $overwriteDemand
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function listAction(array $overwriteDemand = null)
|
||||
{
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
if ($overwriteDemand !== null && $this->settings['disableOverrideDemand'] != 1) {
|
||||
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
|
||||
}
|
||||
|
||||
$idList = is_array($demand->getCategories()) ? $demand->getCategories() : explode(',', $demand->getCategories());
|
||||
|
||||
$startingPoint = null;
|
||||
if (!empty($this->settings['startingpoint'])) {
|
||||
$startingPoint = $this->settings['startingpoint'];
|
||||
}
|
||||
|
||||
$assignedValues = [
|
||||
'categories' => $this->categoryRepository->findTree($idList, $startingPoint),
|
||||
'overwriteDemand' => $overwriteDemand,
|
||||
'demand' => $demand,
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new CategoryListActionEvent($this, $assignedValues));
|
||||
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
}
|
||||
}
|
||||
161
typo3conf/ext/news/Classes/Controller/ImportController.php
Normal file
161
typo3conf/ext/news/Classes/Controller/ImportController.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Controller;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use Exception;
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use GeorgRinger\News\Jobs\ImportJobInterface;
|
||||
use GeorgRinger\News\Utility\ImportJob;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\View\BackendTemplateView;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Controller to import news records
|
||||
*/
|
||||
class ImportController extends ActionController
|
||||
{
|
||||
|
||||
/**
|
||||
* Backend Template Container
|
||||
*
|
||||
* @var BackendTemplateView
|
||||
*/
|
||||
protected $defaultViewObjectName = BackendTemplateView::class;
|
||||
|
||||
/**
|
||||
* Retrieve all available import jobs by traversing trough registered
|
||||
* import jobs and checking "isEnabled".
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAvailableJobs(): array
|
||||
{
|
||||
$availableJobs = [];
|
||||
$registeredJobs = ImportJob::getRegisteredJobs();
|
||||
|
||||
foreach ($registeredJobs as $registeredJob) {
|
||||
$jobInstance = $this->objectManager->get($registeredJob['className']);
|
||||
if ($jobInstance instanceof ImportJobInterface && $jobInstance->isEnabled()) {
|
||||
$availableJobs[$registeredJob['className']] = $GLOBALS['LANG']->sL($registeredJob['title']);
|
||||
}
|
||||
}
|
||||
|
||||
return $availableJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the import jobs selection .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function indexAction(): void
|
||||
{
|
||||
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
$pageRenderer->loadRequireJsModule('TYPO3/CMS/News/Import');
|
||||
|
||||
$this->view->assignMultiple(
|
||||
[
|
||||
'error' => $this->checkCorrectConfiguration(),
|
||||
'availableJobs' => array_merge([0 => ''], $this->getAvailableJobs()),
|
||||
'moduleUrl' => $uriBuilder->buildUriFromRoute($this->request->getPluginName())
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for correct configuration
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
* @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
|
||||
*/
|
||||
protected function checkCorrectConfiguration(): string
|
||||
{
|
||||
$error = '';
|
||||
|
||||
$settings = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
|
||||
try {
|
||||
$storageId = (int)$settings->getStorageUidImporter();
|
||||
$path = $settings->getResourceFolderImporter();
|
||||
if ($storageId === 0) {
|
||||
throw new UnexpectedValueException('import.error.configuration.storageUidImporter');
|
||||
}
|
||||
if (empty($path)) {
|
||||
throw new UnexpectedValueException('import.error.configuration.resourceFolderImporter');
|
||||
}
|
||||
$storage = $this->getResourceFactory()->getStorageObject($storageId);
|
||||
$pathExists = $storage->hasFolder($path);
|
||||
if (!$pathExists) {
|
||||
throw new FolderDoesNotExistException('Folder does not exist', 1474827988);
|
||||
}
|
||||
} catch (FolderDoesNotExistException $e) {
|
||||
$error = 'import.error.configuration.resourceFolderImporter.notExist';
|
||||
} catch (UnexpectedValueException $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an actual job.
|
||||
*
|
||||
* @param string $jobClassName
|
||||
* @param int $offset
|
||||
* @return string
|
||||
*/
|
||||
public function runJobAction($jobClassName, $offset = 0): string
|
||||
{
|
||||
/** @var ImportJobInterface $job */
|
||||
$job = $this->objectManager->get($jobClassName);
|
||||
$job->run($offset);
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the job info of a given jobClass
|
||||
*
|
||||
* @param string $jobClassName
|
||||
* @return string
|
||||
*/
|
||||
public function jobInfoAction($jobClassName): string
|
||||
{
|
||||
$response = null;
|
||||
try {
|
||||
/** @var ImportJobInterface $job */
|
||||
$job = $this->objectManager->get($jobClassName);
|
||||
$response = $job->getInfo();
|
||||
} catch (Exception $e) {
|
||||
$response['message'] = $e->getMessage();
|
||||
$response['line'] = $e->getLine();
|
||||
$response['trace'] = $e->getTrace();
|
||||
|
||||
HttpUtility::setResponseCode(HttpUtility::HTTP_STATUS_400);
|
||||
}
|
||||
|
||||
return json_encode($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResourceFactory
|
||||
*/
|
||||
protected function getResourceFactory(): ResourceFactory
|
||||
{
|
||||
return GeneralUtility::makeInstance(ResourceFactory::class);
|
||||
}
|
||||
}
|
||||
166
typo3conf/ext/news/Classes/Controller/NewsBaseController.php
Normal file
166
typo3conf/ext/news/Classes/Controller/NewsBaseController.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Controller;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use TYPO3\CMS\Core\Http\ImmediateResponseException;
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* Base controller
|
||||
*/
|
||||
class NewsBaseController extends ActionController
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes the view before invoking an action method.
|
||||
* Override this method to solve assign variables common for all actions
|
||||
* or prepare the view in another way before the action is called.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\View\ViewInterface $view The view to be initialized
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initializeView(ViewInterface $view)
|
||||
{
|
||||
$view->assign('contentObjectData', $this->configurationManager->getContentObject()->data);
|
||||
$view->assign('emConfiguration', GeneralUtility::makeInstance(EmConfiguration::class));
|
||||
if (isset($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
|
||||
$view->assign('pageData', $GLOBALS['TSFE']->page);
|
||||
}
|
||||
parent::initializeView($view);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @param RequestInterface $request
|
||||
// * @param ResponseInterface $response
|
||||
// * @throws \Exception
|
||||
// */
|
||||
// public function processRequest(RequestInterface $request, ResponseInterface $response)
|
||||
// {
|
||||
// try {
|
||||
// parent::processRequest($request, $response);
|
||||
// } catch (\Exception $exception) {
|
||||
// $this->handleKnownExceptionsElseThrowAgain($exception);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* @param \Exception $exception
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handleKnownExceptionsElseThrowAgain(\Exception $exception): void
|
||||
{
|
||||
$previousException = $exception->getPrevious();
|
||||
|
||||
if (
|
||||
$this->actionMethodName === 'detailAction'
|
||||
&& $previousException instanceof \TYPO3\CMS\Extbase\Property\Exception
|
||||
&& isset($this->settings['detail']['errorHandling'])
|
||||
) {
|
||||
$this->handleNoNewsFoundError($this->settings['detail']['errorHandling']);
|
||||
} else {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handling if no news entry is found
|
||||
*
|
||||
* @param string $configuration configuration what will be done
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function handleNoNewsFoundError($configuration): string
|
||||
{
|
||||
if (empty($configuration)) {
|
||||
return '';
|
||||
}
|
||||
$options = GeneralUtility::trimExplode(',', $configuration, true);
|
||||
|
||||
switch ($options[0]) {
|
||||
case 'redirectToListView':
|
||||
$this->redirect('list');
|
||||
break;
|
||||
case 'redirectToPage':
|
||||
if (count($options) === 1 || count($options) > 3) {
|
||||
$msg = sprintf(
|
||||
'If error handling "%s" is used, either 2 or 3 arguments, split by "," must be used',
|
||||
$options[0]
|
||||
);
|
||||
throw new \InvalidArgumentException($msg);
|
||||
}
|
||||
$this->uriBuilder->reset();
|
||||
$this->uriBuilder->setTargetPageUid($options[1]);
|
||||
$this->uriBuilder->setCreateAbsoluteUri(true);
|
||||
if (GeneralUtility::getIndpEnv('TYPO3_SSL')) {
|
||||
$this->uriBuilder->setAbsoluteUriScheme('https');
|
||||
}
|
||||
$url = $this->uriBuilder->build();
|
||||
|
||||
if (isset($options[2])) {
|
||||
$this->redirectToUri($url, 0, (int)$options[2]);
|
||||
} else {
|
||||
$this->redirectToUri($url);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'pageNotFoundHandler':
|
||||
$typo3Information = GeneralUtility::makeInstance(Typo3Version::class);
|
||||
if ($typo3Information->getMajorVersion() === 9) {
|
||||
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction($GLOBALS['TYPO3_REQUEST'], 'No news entry found.');
|
||||
throw new ImmediateResponseException($response);
|
||||
}
|
||||
$message = 'No news entry found!';
|
||||
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$GLOBALS['TYPO3_REQUEST'],
|
||||
$message
|
||||
);
|
||||
throw new ImmediateResponseException($response, 1590468229);
|
||||
|
||||
break;
|
||||
case 'showStandaloneTemplate':
|
||||
if (isset($options[2])) {
|
||||
$statusCode = constant(HttpUtility::class . '::HTTP_STATUS_' . $options[2]);
|
||||
} else {
|
||||
$statusCode = HttpUtility::HTTP_STATUS_404;
|
||||
}
|
||||
HttpUtility::setResponseCode($statusCode);
|
||||
|
||||
$this->getTypoScriptFrontendController()->set_no_cache('News record not found');
|
||||
|
||||
$standaloneTemplate = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$standaloneTemplate->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($options[1]));
|
||||
return $standaloneTemplate->render();
|
||||
break;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TypoScriptFrontendController
|
||||
*/
|
||||
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
|
||||
{
|
||||
return $GLOBALS['TSFE'];
|
||||
}
|
||||
}
|
||||
703
typo3conf/ext/news/Classes/Controller/NewsController.php
Normal file
703
typo3conf/ext/news/Classes/Controller/NewsController.php
Normal file
@@ -0,0 +1,703 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Controller;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\NewsDemand;
|
||||
use GeorgRinger\News\Domain\Model\Dto\Search;
|
||||
use GeorgRinger\News\Domain\Model\News;
|
||||
use GeorgRinger\News\Domain\Repository\CategoryRepository;
|
||||
use GeorgRinger\News\Domain\Repository\NewsRepository;
|
||||
use GeorgRinger\News\Domain\Repository\TagRepository;
|
||||
use GeorgRinger\News\Event\NewsCheckPidOfNewsRecordFailedInDetailActionEvent;
|
||||
use GeorgRinger\News\Event\NewsDateMenuActionEvent;
|
||||
use GeorgRinger\News\Event\NewsDetailActionEvent;
|
||||
use GeorgRinger\News\Event\NewsListActionEvent;
|
||||
use GeorgRinger\News\Event\NewsListSelectedActionEvent;
|
||||
use GeorgRinger\News\Event\NewsSearchFormActionEvent;
|
||||
use GeorgRinger\News\Event\NewsSearchResultActionEvent;
|
||||
use GeorgRinger\News\Pagination\QueryResultPaginator;
|
||||
use GeorgRinger\News\Seo\NewsTitleProvider;
|
||||
use GeorgRinger\News\Utility\Cache;
|
||||
use GeorgRinger\News\Utility\ClassCacheManager;
|
||||
use GeorgRinger\News\Utility\Page;
|
||||
use GeorgRinger\News\Utility\TypoScript;
|
||||
use GeorgRinger\NumberedPagination\NumberedPagination;
|
||||
use TYPO3\CMS\Core\Pagination\SimplePagination;
|
||||
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter;
|
||||
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
|
||||
use TYPO3\CMS\Fluid\View\TemplateView;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller of news records
|
||||
*
|
||||
*/
|
||||
class NewsController extends NewsBaseController
|
||||
{
|
||||
/**
|
||||
* @var \GeorgRinger\News\Domain\Repository\NewsRepository
|
||||
*/
|
||||
protected $newsRepository;
|
||||
|
||||
/**
|
||||
* @var \GeorgRinger\News\Domain\Repository\CategoryRepository
|
||||
*/
|
||||
protected $categoryRepository;
|
||||
|
||||
/**
|
||||
* @var \GeorgRinger\News\Domain\Repository\TagRepository
|
||||
*/
|
||||
protected $tagRepository;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
|
||||
*/
|
||||
protected $configurationManager;
|
||||
|
||||
/** @var array */
|
||||
protected $ignoredSettingsForOverride = ['demandclass', 'orderbyallowed', 'selectedList'];
|
||||
|
||||
/**
|
||||
* Original settings without any magic done by stdWrap and skipping empty values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $originalSettings = [];
|
||||
|
||||
/**
|
||||
* NewsController constructor.
|
||||
* @param NewsRepository $newsRepository
|
||||
* @param CategoryRepository $categoryRepository
|
||||
* @param TagRepository $tagRepository
|
||||
* @param ConfigurationManagerInterface $configurationManager
|
||||
*/
|
||||
public function __construct(
|
||||
NewsRepository $newsRepository,
|
||||
CategoryRepository $categoryRepository,
|
||||
TagRepository $tagRepository,
|
||||
ConfigurationManagerInterface $configurationManager
|
||||
) {
|
||||
$this->newsRepository = $newsRepository;
|
||||
$this->categoryRepository = $categoryRepository;
|
||||
$this->tagRepository = $tagRepository;
|
||||
$this->configurationManager = $configurationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the current action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initializeAction()
|
||||
{
|
||||
GeneralUtility::makeInstance(ClassCacheManager::class)->reBuildSimple();
|
||||
$this->buildSettings();
|
||||
if (isset($this->settings['format'])) {
|
||||
$this->request->setFormat($this->settings['format']);
|
||||
}
|
||||
// Only do this in Frontend Context
|
||||
if (!empty($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
|
||||
// We only want to set the tag once in one request, so we have to cache that statically if it has been done
|
||||
static $cacheTagsSet = false;
|
||||
|
||||
/** @var $typoScriptFrontendController \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController */
|
||||
$typoScriptFrontendController = $GLOBALS['TSFE'];
|
||||
if (!$cacheTagsSet) {
|
||||
$typoScriptFrontendController->addCacheTags(['tx_news']);
|
||||
$cacheTagsSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the demand object which define which records will get shown
|
||||
*
|
||||
* @param array $settings
|
||||
* @param string $class optional class which must be an instance of \GeorgRinger\News\Domain\Model\Dto\NewsDemand
|
||||
* @return \GeorgRinger\News\Domain\Model\Dto\NewsDemand
|
||||
*/
|
||||
protected function createDemandObjectFromSettings(
|
||||
array $settings,
|
||||
$class = NewsDemand::class
|
||||
): \GeorgRinger\News\Domain\Model\Dto\NewsDemand {
|
||||
$class = isset($settings['demandClass']) && !empty($settings['demandClass']) ? $settings['demandClass'] : $class;
|
||||
|
||||
/* @var $demand NewsDemand */
|
||||
$demand = GeneralUtility::makeInstance($class, $settings);
|
||||
if (!$demand instanceof NewsDemand) {
|
||||
throw new \UnexpectedValueException(
|
||||
sprintf(
|
||||
'The demand object must be an instance of %s, but %s given!',
|
||||
NewsDemand::class,
|
||||
$class
|
||||
),
|
||||
1423157953
|
||||
);
|
||||
}
|
||||
|
||||
$demand->setCategories(GeneralUtility::trimExplode(',', $settings['categories'] ?? '', true));
|
||||
$demand->setCategoryConjunction((string)($settings['categoryConjunction'] ?? ''));
|
||||
$demand->setIncludeSubCategories((bool)($settings['includeSubCategories'] ?? false));
|
||||
$demand->setTags((string)($settings['tags'] ?? ''));
|
||||
|
||||
$demand->setTopNewsRestriction((int)($settings['topNewsRestriction'] ?? 0));
|
||||
$demand->setTimeRestriction($settings['timeRestriction'] ?? '');
|
||||
$demand->setTimeRestrictionHigh($settings['timeRestrictionHigh'] ?? '');
|
||||
$demand->setArchiveRestriction((string)($settings['archiveRestriction'] ?? ''));
|
||||
$demand->setExcludeAlreadyDisplayedNews((bool)($settings['excludeAlreadyDisplayedNews'] ?? false));
|
||||
$demand->setHideIdList((string)($settings['hideIdList'] ?? ''));
|
||||
|
||||
if ($settings['orderBy'] ?? '') {
|
||||
$demand->setOrder($settings['orderBy'] . ' ' . $settings['orderDirection']);
|
||||
}
|
||||
$demand->setOrderByAllowed((string)($settings['orderByAllowed'] ?? ''));
|
||||
|
||||
$demand->setTopNewsFirst((bool)($settings['topNewsFirst'] ?? false));
|
||||
|
||||
$demand->setLimit((int)($settings['limit'] ?? 0));
|
||||
$demand->setOffset((int)($settings['offset'] ?? 0));
|
||||
|
||||
$demand->setSearchFields((string)($settings['search']['fields'] ?? ''));
|
||||
$demand->setDateField((string)($settings['dateField'] ?? ''));
|
||||
$demand->setMonth((int)($settings['month'] ?? 0));
|
||||
$demand->setYear((int)($settings['year'] ?? 0));
|
||||
|
||||
$demand->setStoragePage(Page::extendPidListByChildren(
|
||||
(string)($settings['startingpoint'] ?? ''),
|
||||
(int)($settings['recursive'] ?? 0)
|
||||
));
|
||||
|
||||
if ($hooks = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Controller/NewsController.php']['createDemandObjectFromSettings'] ?? []) {
|
||||
$params = [
|
||||
'demand' => $demand,
|
||||
'settings' => $settings,
|
||||
'class' => $class,
|
||||
];
|
||||
foreach ($hooks as $reference) {
|
||||
GeneralUtility::callUserFunction($reference, $params, $this);
|
||||
}
|
||||
}
|
||||
return $demand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites a given demand object by an propertyName => $propertyValue array
|
||||
*
|
||||
* @param \GeorgRinger\News\Domain\Model\Dto\NewsDemand $demand
|
||||
* @param array $overwriteDemand
|
||||
* @return \GeorgRinger\News\Domain\Model\Dto\NewsDemand
|
||||
*/
|
||||
protected function overwriteDemandObject(NewsDemand $demand, array $overwriteDemand): \GeorgRinger\News\Domain\Model\Dto\NewsDemand
|
||||
{
|
||||
foreach ($this->ignoredSettingsForOverride as $property) {
|
||||
unset($overwriteDemand[$property]);
|
||||
}
|
||||
|
||||
foreach ($overwriteDemand as $propertyName => $propertyValue) {
|
||||
if (in_array(strtolower($propertyName), $this->ignoredSettingsForOverride, true)) {
|
||||
continue;
|
||||
}
|
||||
if ($propertyValue !== '' || $this->settings['allowEmptyStringsForOverwriteDemand']) {
|
||||
if (in_array($propertyName, ['categories'], true)) {
|
||||
$propertyValue = GeneralUtility::trimExplode(',', $propertyValue, true);
|
||||
}
|
||||
ObjectAccess::setProperty($demand, $propertyName, $propertyValue);
|
||||
}
|
||||
}
|
||||
return $demand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a list view of news
|
||||
*
|
||||
* @param array|null $overwriteDemand
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function listAction(array $overwriteDemand = null)
|
||||
{
|
||||
$this->forwardToDetailActionWhenRequested();
|
||||
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
if ((int)($this->settings['disableOverrideDemand'] ?? 1) !== 1 && $overwriteDemand !== null) {
|
||||
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
|
||||
}
|
||||
$newsRecords = $this->newsRepository->findDemanded($demand);
|
||||
|
||||
// pagination
|
||||
$paginationConfiguration = $this->settings['list']['paginate'] ?? [];
|
||||
$itemsPerPage = (int)(($paginationConfiguration['itemsPerPage'] ?? '') ?: 10);
|
||||
$maximumNumberOfLinks = (int)($paginationConfiguration['maximumNumberOfLinks'] ?? 0);
|
||||
|
||||
$currentPage = $this->request->hasArgument('currentPage') ? (int)$this->request->getArgument('currentPage') : 1;
|
||||
$paginator = GeneralUtility::makeInstance(QueryResultPaginator::class, $newsRecords, $currentPage, $itemsPerPage, (int)($this->settings['limit'] ?? 0), (int)($this->settings['offset'] ?? 0));
|
||||
$paginationClass = $paginationConfiguration['class'] ?? SimplePagination::class;
|
||||
if (class_exists(NumberedPagination::class) && $paginationClass === NumberedPagination::class && $maximumNumberOfLinks) {
|
||||
$pagination = GeneralUtility::makeInstance(NumberedPagination::class, $paginator, $maximumNumberOfLinks);
|
||||
} elseif (class_exists($paginationClass)) {
|
||||
$pagination = GeneralUtility::makeInstance($paginationClass, $paginator);
|
||||
} else {
|
||||
$pagination = GeneralUtility::makeInstance(SimplePagination::class, $paginator);
|
||||
}
|
||||
|
||||
$assignedValues = [
|
||||
'news' => $newsRecords,
|
||||
'overwriteDemand' => $overwriteDemand,
|
||||
'demand' => $demand,
|
||||
'categories' => null,
|
||||
'tags' => null,
|
||||
'settings' => $this->settings,
|
||||
'pagination' => [
|
||||
'currentPage' => $currentPage,
|
||||
'paginator' => $paginator,
|
||||
'pagination' => $pagination,
|
||||
]
|
||||
];
|
||||
|
||||
if ($demand->getCategories() !== '') {
|
||||
$categoriesList = $demand->getCategories();
|
||||
if (is_string($categoriesList)) {
|
||||
$categoriesList = GeneralUtility::trimExplode(',', $categoriesList);
|
||||
}
|
||||
if (!empty($categoriesList)) {
|
||||
$assignedValues['categories'] = $this->categoryRepository->findByIdList($categoriesList);
|
||||
}
|
||||
}
|
||||
|
||||
if ($demand->getTags() !== '') {
|
||||
$tagList = $demand->getTags();
|
||||
if (!is_array($tagList)) {
|
||||
$tagList = GeneralUtility::trimExplode(',', $tagList);
|
||||
}
|
||||
if (!empty($tagList)) {
|
||||
$assignedValues['tags'] = $this->tagRepository->findByIdList($tagList);
|
||||
}
|
||||
}
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new NewsListActionEvent($this, $assignedValues));
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
|
||||
Cache::addPageCacheTagsByDemandObject($demand);
|
||||
}
|
||||
|
||||
/**
|
||||
* When list action is called along with a news argument, we forward to detail action.
|
||||
*/
|
||||
protected function forwardToDetailActionWhenRequested()
|
||||
{
|
||||
if (!$this->isActionAllowed('detail')
|
||||
|| !$this->request->hasArgument('news')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->forward('detail', null, null, ['news' => $this->request->getArgument('news')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an action is enabled in switchableControllerActions configuration
|
||||
*
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
protected function isActionAllowed(string $action): bool
|
||||
{
|
||||
$frameworkConfiguration = $this->configurationManager->getConfiguration($this->configurationManager::CONFIGURATION_TYPE_FRAMEWORK);
|
||||
$allowedActions = $frameworkConfiguration['controllerConfiguration']['News']['actions'] ?? [];
|
||||
|
||||
return \in_array($action, $allowedActions, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a selected list view of news
|
||||
*/
|
||||
public function selectedListAction(): void
|
||||
{
|
||||
$newsRecords = [];
|
||||
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
if (empty($this->originalSettings['orderBy'] ?? '')) {
|
||||
$idList = GeneralUtility::trimExplode(',', $this->settings['selectedList'], true);
|
||||
foreach ($idList as $id) {
|
||||
$news = $this->newsRepository->findByIdentifier($id);
|
||||
if ($news) {
|
||||
$newsRecords[] = $news;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$demand->setIdList($this->settings['selectedList']);
|
||||
$newsRecords = $this->newsRepository->findDemanded($demand);
|
||||
}
|
||||
|
||||
$assignedValues = [
|
||||
'news' => $newsRecords,
|
||||
'demand' => $demand,
|
||||
'settings' => $this->settings
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new NewsListSelectedActionEvent($this, $assignedValues));
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
|
||||
if (!empty($newsRecords) && is_a($newsRecords[0], News::class)) {
|
||||
Cache::addCacheTagsByNewsRecords($newsRecords);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Single view of a news record
|
||||
*
|
||||
* @param \GeorgRinger\News\Domain\Model\News $news news item
|
||||
* @param int $currentPage current page for optional pagination
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function detailAction(News $news = null, $currentPage = 1)
|
||||
{
|
||||
if ($news === null || ($this->settings['isShortcut'] ?? false)) {
|
||||
$previewNewsId = (int)($this->settings['singleNews'] ?? 0);
|
||||
if ($this->request->hasArgument('news_preview')) {
|
||||
$previewNewsId = (int)$this->request->getArgument('news_preview');
|
||||
}
|
||||
|
||||
if ($previewNewsId > 0) {
|
||||
if ($this->isPreviewOfHiddenRecordsEnabled()) {
|
||||
$news = $this->newsRepository->findByUid($previewNewsId, false);
|
||||
} else {
|
||||
$news = $this->newsRepository->findByUid($previewNewsId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_a($news, News::class) && ($this->settings['detail']['checkPidOfNewsRecord'] ?? false)
|
||||
) {
|
||||
$news = $this->checkPidOfNewsRecord($news);
|
||||
}
|
||||
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
$assignedValues = [
|
||||
'newsItem' => $news,
|
||||
'currentPage' => (int)$currentPage,
|
||||
'demand' => $demand,
|
||||
'settings' => $this->settings
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new NewsDetailActionEvent($this, $assignedValues));
|
||||
$assignedValues = $event->getAssignedValues();
|
||||
|
||||
$news = $assignedValues['newsItem'];
|
||||
$this->view->assignMultiple($assignedValues);
|
||||
|
||||
// reset news if type is internal or external
|
||||
if ($news && !($this->settings['isShortcut'] ?? false) && ($news->getType() === '1' || $news->getType() === '2')) {
|
||||
$news = null;
|
||||
}
|
||||
|
||||
if ($news !== null) {
|
||||
Page::setRegisterProperties($this->settings['detail']['registerProperties'] ?? false, $news);
|
||||
Cache::addCacheTagsByNewsRecords([$news]);
|
||||
|
||||
if ($this->settings['detail']['pageTitle']['_typoScriptNodeValue'] ?? false) {
|
||||
$providerConfiguration = $this->settings['detail']['pageTitle'] ?? [];
|
||||
$providerClass = $providerConfiguration['provider'] ?? NewsTitleProvider::class;
|
||||
|
||||
/** @var NewsTitleProvider $provider */
|
||||
$provider = GeneralUtility::makeInstance($providerClass);
|
||||
$provider->setTitleByNews($news, $providerConfiguration);
|
||||
}
|
||||
} elseif (isset($this->settings['detail']['errorHandling'])) {
|
||||
$errorContent = $this->handleNoNewsFoundError($this->settings['detail']['errorHandling']);
|
||||
if ($errorContent) {
|
||||
return $errorContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the news pid could be found in the startingpoint settings of the detail plugin and
|
||||
* if the pid could not be found it return NULL instead of the news object.
|
||||
*
|
||||
* @param \GeorgRinger\News\Domain\Model\News $news
|
||||
* @return \GeorgRinger\News\Domain\Model\News|null
|
||||
*/
|
||||
protected function checkPidOfNewsRecord(News $news): ?\GeorgRinger\News\Domain\Model\News
|
||||
{
|
||||
$allowedStoragePages = GeneralUtility::trimExplode(
|
||||
',',
|
||||
Page::extendPidListByChildren(
|
||||
$this->settings['startingpoint'],
|
||||
$this->settings['recursive']
|
||||
),
|
||||
true
|
||||
);
|
||||
if (count($allowedStoragePages) > 0 && !in_array($news->getPid(), $allowedStoragePages)) {
|
||||
$this->eventDispatcher->dispatch(new NewsCheckPidOfNewsRecordFailedInDetailActionEvent($this, $news));
|
||||
$news = null;
|
||||
}
|
||||
return $news;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if preview is enabled either in TS or FlexForm
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isPreviewOfHiddenRecordsEnabled(): bool
|
||||
{
|
||||
if (!empty($this->settings['previewHiddenRecords']) && $this->settings['previewHiddenRecords'] == 2) {
|
||||
$previewEnabled = !empty($this->settings['enablePreviewOfHiddenRecords']);
|
||||
} else {
|
||||
$previewEnabled = !empty($this->settings['previewHiddenRecords']);
|
||||
}
|
||||
return $previewEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a menu by dates, e.g. years, months or dates
|
||||
*
|
||||
* @param array|null $overwriteDemand
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dateMenuAction(array $overwriteDemand = null): void
|
||||
{
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
if ($this->settings['disableOverrideDemand'] != 1 && $overwriteDemand !== null) {
|
||||
$overwriteDemandTemp = $overwriteDemand;
|
||||
unset($overwriteDemandTemp['year']);
|
||||
unset($overwriteDemandTemp['month']);
|
||||
$demand = $this->overwriteDemandObject(
|
||||
$demand,
|
||||
$overwriteDemandTemp
|
||||
);
|
||||
unset($overwriteDemandTemp);
|
||||
}
|
||||
|
||||
// It might be that those are set, @see http://forge.typo3.org/issues/44759
|
||||
$demand->setLimit(0);
|
||||
$demand->setOffset(0);
|
||||
// @todo: find a better way to do this related to #13856
|
||||
if (!$dateField = $this->settings['dateField']) {
|
||||
$dateField = 'datetime';
|
||||
}
|
||||
$demand->setOrder($dateField . ' ' . $this->settings['orderDirection']);
|
||||
$newsRecords = $this->newsRepository->findDemanded($demand);
|
||||
|
||||
$demand->setOrder($this->settings['orderDirection']);
|
||||
$statistics = $this->newsRepository->countByDate($demand);
|
||||
|
||||
$assignedValues = [
|
||||
'listPid' => ($this->settings['listPid'] ? $this->settings['listPid'] : $GLOBALS['TSFE']->id),
|
||||
'dateField' => $dateField,
|
||||
'data' => $statistics,
|
||||
'news' => $newsRecords,
|
||||
'overwriteDemand' => $overwriteDemand,
|
||||
'demand' => $demand,
|
||||
'settings' => $this->settings
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new NewsDateMenuActionEvent($this, $assignedValues));
|
||||
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the search form
|
||||
*
|
||||
* @param \GeorgRinger\News\Domain\Model\Dto\Search $search
|
||||
* @param array $overwriteDemand
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function searchFormAction(
|
||||
Search $search = null,
|
||||
array $overwriteDemand = []
|
||||
): void {
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
if ($this->settings['disableOverrideDemand'] != 1 && $overwriteDemand !== null) {
|
||||
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
|
||||
}
|
||||
|
||||
if (is_null($search)) {
|
||||
$search = GeneralUtility::makeInstance(Search::class);
|
||||
}
|
||||
$demand->setSearch($search);
|
||||
|
||||
$assignedValues = [
|
||||
'search' => $search,
|
||||
'overwriteDemand' => $overwriteDemand,
|
||||
'demand' => $demand,
|
||||
'settings' => $this->settings
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new NewsSearchFormActionEvent($this, $assignedValues));
|
||||
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the search result
|
||||
*
|
||||
* @param \GeorgRinger\News\Domain\Model\Dto\Search $search
|
||||
* @param array $overwriteDemand
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function searchResultAction(
|
||||
Search $search = null,
|
||||
array $overwriteDemand = []
|
||||
): void {
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
if ($this->settings['disableOverrideDemand'] != 1 && $overwriteDemand !== null) {
|
||||
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
|
||||
}
|
||||
|
||||
if (!is_null($search)) {
|
||||
$search->setFields($this->settings['search']['fields']);
|
||||
$search->setDateField($this->settings['dateField']);
|
||||
$search->setSplitSubjectWords((bool)$this->settings['search']['splitSearchWord']);
|
||||
}
|
||||
|
||||
$demand->setSearch($search);
|
||||
|
||||
$assignedValues = [
|
||||
'news' => $this->newsRepository->findDemanded($demand),
|
||||
'overwriteDemand' => $overwriteDemand,
|
||||
'search' => $search,
|
||||
'demand' => $demand,
|
||||
'settings' => $this->settings
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new NewsSearchResultActionEvent($this, $assignedValues));
|
||||
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize search result action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initializeSearchResultAction(): void
|
||||
{
|
||||
$this->initializeSearchActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize search form action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initializeSearchFormAction(): void
|
||||
{
|
||||
$this->initializeSearchActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize searchForm and searchResult actions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initializeSearchActions(): void
|
||||
{
|
||||
if ($this->arguments->hasArgument('search')) {
|
||||
$propertyMappingConfiguration = $this->arguments['search']->getPropertyMappingConfiguration();
|
||||
$propertyMappingConfiguration->allowAllProperties();
|
||||
$propertyMappingConfiguration->setTypeConverterOption('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* helper
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Injects the Configuration Manager and is initializing the framework settings
|
||||
*
|
||||
* @param ConfigurationManagerInterface $configurationManager Instance of the Configuration Manager
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function buildSettings()
|
||||
{
|
||||
$tsSettings = $this->configurationManager->getConfiguration(
|
||||
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
|
||||
'news',
|
||||
'news_pi1'
|
||||
);
|
||||
$originalSettings = $this->configurationManager->getConfiguration(
|
||||
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS
|
||||
);
|
||||
|
||||
$propertiesNotAllowedViaFlexForms = ['orderByAllowed'];
|
||||
foreach ($propertiesNotAllowedViaFlexForms as $property) {
|
||||
$originalSettings[$property] = ($tsSettings['settings'] ?? [])[$property] ?? ($originalSettings[$property] ?? '');
|
||||
}
|
||||
$this->originalSettings = $originalSettings;
|
||||
|
||||
// Use stdWrap for given defined settings
|
||||
|
||||
if (isset($originalSettings['useStdWrap']) && !empty($originalSettings['useStdWrap'])) {
|
||||
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
|
||||
$typoScriptArray = $typoScriptService->convertPlainArrayToTypoScriptArray($originalSettings);
|
||||
$stdWrapProperties = GeneralUtility::trimExplode(',', $originalSettings['useStdWrap'], true);
|
||||
foreach ($stdWrapProperties as $key) {
|
||||
if (is_array($typoScriptArray[$key . '.'])) {
|
||||
$originalSettings[$key] = $this->configurationManager->getContentObject()->stdWrap(
|
||||
$typoScriptArray[$key],
|
||||
$typoScriptArray[$key . '.']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// start override
|
||||
if (isset($tsSettings['settings']['overrideFlexformSettingsIfEmpty'])) {
|
||||
$typoScriptUtility = GeneralUtility::makeInstance(TypoScript::class);
|
||||
$originalSettings = $typoScriptUtility->override($originalSettings, $tsSettings);
|
||||
}
|
||||
|
||||
foreach ($hooks = ($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Controller/NewsController.php']['overrideSettings'] ?? []) as $_funcRef) {
|
||||
$_params = [
|
||||
'originalSettings' => $originalSettings,
|
||||
'tsSettings' => $tsSettings,
|
||||
];
|
||||
$originalSettings = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
|
||||
}
|
||||
|
||||
$this->settings = $originalSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a view.
|
||||
* This function is for testing purposes only.
|
||||
*
|
||||
* @param \TYPO3\CMS\Fluid\View\TemplateView $view the view to inject
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setView(TemplateView $view): void
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
}
|
||||
50
typo3conf/ext/news/Classes/Controller/TagController.php
Normal file
50
typo3conf/ext/news/Classes/Controller/TagController.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Controller;
|
||||
|
||||
use GeorgRinger\News\Event\TagListActionEvent;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tag controller
|
||||
*/
|
||||
class TagController extends NewsController
|
||||
{
|
||||
/**
|
||||
* List tags
|
||||
*
|
||||
* @param array $overwriteDemand
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function listAction(array $overwriteDemand = null)
|
||||
{
|
||||
// Default value is wrong for tags
|
||||
if ($this->settings['orderBy'] === 'datetime') {
|
||||
unset($this->settings['orderBy']);
|
||||
}
|
||||
|
||||
$demand = $this->createDemandObjectFromSettings($this->settings);
|
||||
$demand->setActionAndClass(__METHOD__, __CLASS__);
|
||||
|
||||
if ($overwriteDemand !== null && $this->settings['disableOverrideDemand'] != 1) {
|
||||
$demand = $this->overwriteDemandObject($demand, $overwriteDemand);
|
||||
}
|
||||
|
||||
$assignedValues = [
|
||||
'tags' => $this->tagRepository->findDemanded($demand),
|
||||
'overwriteDemand' => $overwriteDemand,
|
||||
'demand' => $demand,
|
||||
];
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new TagListActionEvent($this, $assignedValues));
|
||||
|
||||
$this->view->assignMultiple($event->getAssignedValues());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\DataProcessing;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add the current news record to any menu, e.g. breadcrumb
|
||||
*
|
||||
* 20 = GeorgRinger\News\DataProcessing\AddNewsToMenuProcessor
|
||||
* 20.menus = breadcrumbMenu,specialMenu
|
||||
*/
|
||||
class AddNewsToMenuProcessor implements DataProcessorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ContentObjectRenderer $cObj
|
||||
* @param array $contentObjectConfiguration
|
||||
* @param array $processorConfiguration
|
||||
* @param array $processedData
|
||||
* @return array
|
||||
*/
|
||||
public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData): array
|
||||
{
|
||||
if (!$processorConfiguration['menus']) {
|
||||
return $processedData;
|
||||
}
|
||||
$newsRecord = $this->getNewsRecord();
|
||||
if ($newsRecord) {
|
||||
$menus = GeneralUtility::trimExplode(',', $processorConfiguration['menus'], true);
|
||||
foreach ($menus as $menu) {
|
||||
if (isset($processedData[$menu])) {
|
||||
$this->addNewsRecordToMenu($newsRecord, $processedData[$menu]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $processedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the news record to the menu items
|
||||
*
|
||||
* @param array $newsRecord
|
||||
* @param array $menu
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addNewsRecordToMenu(array $newsRecord, array &$menu): void
|
||||
{
|
||||
foreach ($menu as &$menuItem) {
|
||||
$menuItem['current'] = 0;
|
||||
}
|
||||
|
||||
$menu[] = [
|
||||
'data' => $newsRecord,
|
||||
'title' => $newsRecord['title'],
|
||||
'active' => 1,
|
||||
'current' => 1,
|
||||
'link' => GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'),
|
||||
'isNews' => true
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news record including possible translations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNewsRecord(): array
|
||||
{
|
||||
$newsId = 0;
|
||||
$vars = GeneralUtility::_GET('tx_news_pi1');
|
||||
if (isset($vars['news'])) {
|
||||
$newsId = (int)$vars['news'];
|
||||
}
|
||||
|
||||
if ($newsId) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_news_domain_model_news');
|
||||
$row = $queryBuilder
|
||||
->select('*')
|
||||
->from('tx_news_domain_model_news')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT))
|
||||
)
|
||||
->execute()
|
||||
->fetch();
|
||||
|
||||
if ($row) {
|
||||
$row = $this->getTsfe()->sys_page->getRecordOverlay('tx_news_domain_model_news', $row, $this->getCurrentLanguage());
|
||||
}
|
||||
|
||||
if (is_array($row) && !empty($row)) {
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current language
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getCurrentLanguage(): int
|
||||
{
|
||||
$languageId = 0;
|
||||
$context = GeneralUtility::makeInstance(Context::class);
|
||||
try {
|
||||
$languageId = $context->getPropertyFromAspect('language', 'contentId');
|
||||
} catch (AspectNotFoundException $e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
return (int)$languageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TypoScriptFrontendController
|
||||
*/
|
||||
protected function getTsfe(): TypoScriptFrontendController
|
||||
{
|
||||
return $GLOBALS['TSFE'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\DataProcessing;
|
||||
|
||||
use GeorgRinger\News\Seo\NewsAvailability;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Disable language item on a detail page if the news is not translated
|
||||
*
|
||||
* 20 = GeorgRinger\News\DataProcessing\DisableLanguageMenuProcessor
|
||||
* 20.menus = languageMenu
|
||||
*/
|
||||
class DisableLanguageMenuProcessor implements DataProcessorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ContentObjectRenderer $cObj
|
||||
* @param array $contentObjectConfiguration
|
||||
* @param array $processorConfiguration
|
||||
* @param array $processedData
|
||||
* @return array
|
||||
*/
|
||||
public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData): array
|
||||
{
|
||||
if (!$processorConfiguration['menus']) {
|
||||
return $processedData;
|
||||
}
|
||||
$newsId = $this->getNewsId();
|
||||
if ($newsId === 0) {
|
||||
return $processedData;
|
||||
}
|
||||
$menus = GeneralUtility::trimExplode(',', $processorConfiguration['menus'], true);
|
||||
foreach ($menus as $menu) {
|
||||
if (isset($processedData[$menu])) {
|
||||
$this->handleMenu($newsId, $processedData[$menu]);
|
||||
}
|
||||
}
|
||||
return $processedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $newsId
|
||||
* @param array $menu
|
||||
*/
|
||||
protected function handleMenu(int $newsId, array &$menu): void
|
||||
{
|
||||
$newsAvailability = GeneralUtility::makeInstance(NewsAvailability::class);
|
||||
foreach ($menu as &$item) {
|
||||
if (!$item['available']) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$availability = $newsAvailability->check((int)$item['languageId'], $newsId);
|
||||
if (!$availability) {
|
||||
$item['available'] = false;
|
||||
$item['availableReason'] = 'news';
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
protected function getNewsId(): int
|
||||
{
|
||||
$newsId = 0;
|
||||
/** @var PageArguments $pageArguments */
|
||||
$pageArguments = $this->getRequest()->getAttribute('routing');
|
||||
if (isset($pageArguments->getRouteArguments()['tx_news_pi1']['news'])) {
|
||||
$newsId = (int)$pageArguments->getRouteArguments()['tx_news_pi1']['news'];
|
||||
} elseif (isset($this->getRequest()->getQueryParams()['tx_news_pi1']['news'])) {
|
||||
$newsId = (int)$this->getRequest()->getQueryParams()['tx_news_pi1']['news'];
|
||||
}
|
||||
|
||||
return $newsId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
protected function getRequest(): ServerRequestInterface
|
||||
{
|
||||
return $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
}
|
||||
383
typo3conf/ext/news/Classes/Domain/Model/Category.php
Normal file
383
typo3conf/ext/news/Classes/Domain/Model/Category.php
Normal file
@@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Category Model
|
||||
*/
|
||||
class Category extends AbstractEntity
|
||||
{
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $sorting = 0;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $crdate;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $tstamp;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $starttime;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $endtime;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $hidden = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $sysLanguageUid = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $l10nParent = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var \GeorgRinger\News\Domain\Model\Category
|
||||
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
|
||||
*/
|
||||
protected $parentcategory;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\GeorgRinger\News\Domain\Model\FileReference>
|
||||
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
|
||||
*/
|
||||
protected $images;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $shortcut = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $singlePid = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $importId = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $importSource = '';
|
||||
|
||||
/**
|
||||
* keep it as string as it should be only used during imports
|
||||
* @var string
|
||||
*/
|
||||
protected $feGroup = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoTitle = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoDescription = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoHeadline = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoText = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $slug = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->images = new ObjectStorage();
|
||||
}
|
||||
|
||||
public function getSorting(): int
|
||||
{
|
||||
return $this->sorting;
|
||||
}
|
||||
|
||||
public function setSorting(int $sorting): void
|
||||
{
|
||||
$this->sorting = $sorting;
|
||||
}
|
||||
|
||||
public function getCrdate(): ?\DateTime
|
||||
{
|
||||
return $this->crdate;
|
||||
}
|
||||
|
||||
public function setCrdate(\DateTime $crdate): void
|
||||
{
|
||||
$this->crdate = $crdate;
|
||||
}
|
||||
|
||||
public function getTstamp(): ?\DateTime
|
||||
{
|
||||
return $this->tstamp;
|
||||
}
|
||||
|
||||
public function setTstamp(\DateTime $tstamp): void
|
||||
{
|
||||
$this->tstamp = $tstamp;
|
||||
}
|
||||
|
||||
public function getStarttime(): ?\DateTime
|
||||
{
|
||||
return $this->starttime;
|
||||
}
|
||||
|
||||
public function setStarttime(\DateTime $starttime): void
|
||||
{
|
||||
$this->starttime = $starttime;
|
||||
}
|
||||
|
||||
public function getEndtime(): ?\DateTime
|
||||
{
|
||||
return $this->endtime;
|
||||
}
|
||||
|
||||
public function setEndtime(\DateTime $endtime): void
|
||||
{
|
||||
$this->endtime = $endtime;
|
||||
}
|
||||
|
||||
public function getHidden(): bool
|
||||
{
|
||||
return $this->hidden;
|
||||
}
|
||||
|
||||
public function setHidden(bool $hidden): void
|
||||
{
|
||||
$this->hidden = $hidden;
|
||||
}
|
||||
|
||||
public function getSysLanguageUid(): int
|
||||
{
|
||||
// int cast is needed as $this->_languageUid is null by default
|
||||
return (int)$this->_languageUid;
|
||||
}
|
||||
|
||||
public function setSysLanguageUid(int $sysLanguageUid): void
|
||||
{
|
||||
$this->_languageUid = $sysLanguageUid;
|
||||
}
|
||||
|
||||
public function getL10nParent(): int
|
||||
{
|
||||
return $this->l10nParent;
|
||||
}
|
||||
|
||||
public function setL10nParent(int $l10nParent): void
|
||||
{
|
||||
$this->l10nParent = $l10nParent;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function getParentcategory(): ?\GeorgRinger\News\Domain\Model\Category
|
||||
{
|
||||
return $this->parentcategory instanceof LazyLoadingProxy
|
||||
? $this->parentcategory->_loadRealInstance()
|
||||
: $this->parentcategory;
|
||||
}
|
||||
|
||||
public function setParentcategory(\GeorgRinger\News\Domain\Model\Category $category): void
|
||||
{
|
||||
$this->parentcategory = $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return ObjectStorage<FileReference>
|
||||
*/
|
||||
public function getImages(): ObjectStorage
|
||||
{
|
||||
return $this->images;
|
||||
}
|
||||
|
||||
public function setImages(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $images): void
|
||||
{
|
||||
$this->images = $images;
|
||||
}
|
||||
|
||||
public function addImage(FileReference $image): void
|
||||
{
|
||||
$this->images->attach($image);
|
||||
}
|
||||
|
||||
public function removeImage(FileReference $image): void
|
||||
{
|
||||
$this->images->detach($image);
|
||||
}
|
||||
|
||||
public function getFirstImage(): ?FileReference
|
||||
{
|
||||
$images = $this->getImages();
|
||||
$images->rewind();
|
||||
|
||||
return $images->valid() ? $images->current() : null;
|
||||
}
|
||||
|
||||
public function getShortcut(): int
|
||||
{
|
||||
return $this->shortcut;
|
||||
}
|
||||
|
||||
public function setShortcut(int $shortcut): void
|
||||
{
|
||||
$this->shortcut = $shortcut;
|
||||
}
|
||||
|
||||
public function getSinglePid(): int
|
||||
{
|
||||
return $this->singlePid;
|
||||
}
|
||||
|
||||
public function setSinglePid(int $singlePid): void
|
||||
{
|
||||
$this->singlePid = $singlePid;
|
||||
}
|
||||
|
||||
public function getImportId(): string
|
||||
{
|
||||
return $this->importId;
|
||||
}
|
||||
|
||||
public function setImportId(string $importId): void
|
||||
{
|
||||
$this->importId = $importId;
|
||||
}
|
||||
|
||||
public function getImportSource(): string
|
||||
{
|
||||
return $this->importSource;
|
||||
}
|
||||
|
||||
public function setImportSource(string $importSource): void
|
||||
{
|
||||
$this->importSource = $importSource;
|
||||
}
|
||||
|
||||
public function getFeGroup(): string
|
||||
{
|
||||
return $this->feGroup;
|
||||
}
|
||||
|
||||
public function setFeGroup(string $feGroup): void
|
||||
{
|
||||
$this->feGroup = $feGroup;
|
||||
}
|
||||
|
||||
public function getSeoTitle(): string
|
||||
{
|
||||
return $this->seoTitle;
|
||||
}
|
||||
|
||||
public function setSeoTitle(string $seoTitle): void
|
||||
{
|
||||
$this->seoTitle = $seoTitle;
|
||||
}
|
||||
|
||||
public function getSeoDescription(): string
|
||||
{
|
||||
return $this->seoDescription;
|
||||
}
|
||||
|
||||
public function setSeoDescription(string $seoDescription): void
|
||||
{
|
||||
$this->seoDescription = $seoDescription;
|
||||
}
|
||||
|
||||
public function getSeoHeadline(): string
|
||||
{
|
||||
return $this->seoHeadline;
|
||||
}
|
||||
|
||||
public function setSeoHeadline(string $seoHeadline): void
|
||||
{
|
||||
$this->seoHeadline = $seoHeadline;
|
||||
}
|
||||
|
||||
public function getSeoText(): string
|
||||
{
|
||||
return $this->seoText;
|
||||
}
|
||||
|
||||
public function setSeoText(string $seoText): void
|
||||
{
|
||||
$this->seoText = $seoText;
|
||||
}
|
||||
|
||||
public function getSlug(): string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function setSlug(string $slug): void
|
||||
{
|
||||
$this->slug = $slug;
|
||||
}
|
||||
}
|
||||
17
typo3conf/ext/news/Classes/Domain/Model/DemandInterface.php
Normal file
17
typo3conf/ext/news/Classes/Domain/Model/DemandInterface.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Demanded repository interface
|
||||
*/
|
||||
interface DemandInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model\Dto;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Administration Demand model
|
||||
*/
|
||||
class AdministrationDemand extends NewsDemand
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $recursive = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $selectedCategories = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sortingField = 'datetime';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sortingDirection = 'desc';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $searchWord = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $hidden = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $archived = 0;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRecursive(): string
|
||||
{
|
||||
return $this->recursive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $recursive
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRecursive(string $recursive): void
|
||||
{
|
||||
$this->recursive = $recursive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSelectedCategories(): array
|
||||
{
|
||||
return $this->selectedCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $selectedCategories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSelectedCategories($selectedCategories)
|
||||
{
|
||||
if ($selectedCategories === '0' || $selectedCategories === ['0']) {
|
||||
return;
|
||||
}
|
||||
if (is_string($selectedCategories)) {
|
||||
$selectedCategories = explode(',', $selectedCategories);
|
||||
}
|
||||
$this->selectedCategories = $selectedCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSortingField(): string
|
||||
{
|
||||
return $this->sortingField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sortingField
|
||||
* @return void
|
||||
*/
|
||||
public function setSortingField(string $sortingField): void
|
||||
{
|
||||
$this->sortingField = $sortingField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSortingDirection(): string
|
||||
{
|
||||
return $this->sortingDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sortingDirection
|
||||
* @return void
|
||||
*/
|
||||
public function setSortingDirection(string $sortingDirection): void
|
||||
{
|
||||
$this->sortingDirection = $sortingDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSearchWord(): string
|
||||
{
|
||||
return $this->searchWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $searchWord
|
||||
* @return void
|
||||
*/
|
||||
public function setSearchWord(string $searchWord): void
|
||||
{
|
||||
$this->searchWord = $searchWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getHidden(): int
|
||||
{
|
||||
return $this->hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $hidden
|
||||
* @return void
|
||||
*/
|
||||
public function setHidden(int $hidden): void
|
||||
{
|
||||
$this->hidden = $hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getArchived(): int
|
||||
{
|
||||
return $this->archived;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $archived
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setArchived(int $archived): void
|
||||
{
|
||||
$this->archived = $archived;
|
||||
}
|
||||
}
|
||||
188
typo3conf/ext/news/Classes/Domain/Model/Dto/EmConfiguration.php
Normal file
188
typo3conf/ext/news/Classes/Domain/Model/Dto/EmConfiguration.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model\Dto;
|
||||
|
||||
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extension Manager configuration
|
||||
*/
|
||||
class EmConfiguration
|
||||
{
|
||||
|
||||
/**
|
||||
* Fill the properties properly
|
||||
*
|
||||
* @param array $configuration em configuration
|
||||
*/
|
||||
public function __construct(array $configuration = [])
|
||||
{
|
||||
if (empty($configuration)) {
|
||||
try {
|
||||
$extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class);
|
||||
$configuration = $extensionConfiguration->get('news');
|
||||
} catch (\Exception $exception) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
foreach ($configuration as $key => $value) {
|
||||
if (property_exists(__CLASS__, $key)) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var int */
|
||||
protected $tagPid = 0;
|
||||
|
||||
/** @var bool */
|
||||
protected $prependAtCopy = true;
|
||||
|
||||
/** @var string */
|
||||
protected $categoryRestriction = '';
|
||||
|
||||
/** @var bool */
|
||||
protected $categoryBeGroupTceFormsRestriction = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $contentElementRelation = true;
|
||||
|
||||
/** @var bool */
|
||||
protected $contentElementPreview = true;
|
||||
|
||||
/** @var bool */
|
||||
protected $manualSorting = false;
|
||||
|
||||
/** @var string */
|
||||
protected $archiveDate = 'date';
|
||||
|
||||
/** @var bool */
|
||||
protected $dateTimeNotRequired = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $showImporter = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $rteForTeaser = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $showAdministrationModule = true;
|
||||
|
||||
/** @var bool */
|
||||
protected $hidePageTreeForAdministrationModule = false;
|
||||
|
||||
/** @var int */
|
||||
protected $storageUidImporter = 1;
|
||||
|
||||
/** @var string */
|
||||
protected $resourceFolderImporter = '/news_import';
|
||||
|
||||
/** @var bool */
|
||||
protected $advancedMediaPreview = true;
|
||||
|
||||
/** @var string */
|
||||
protected $slugBehaviour = 'unique';
|
||||
|
||||
public function getTagPid(): int
|
||||
{
|
||||
return (int)$this->tagPid;
|
||||
}
|
||||
|
||||
public function getPrependAtCopy(): bool
|
||||
{
|
||||
return (bool)$this->prependAtCopy;
|
||||
}
|
||||
|
||||
public function getCategoryRestriction(): string
|
||||
{
|
||||
return $this->categoryRestriction;
|
||||
}
|
||||
|
||||
public function getCategoryBeGroupTceFormsRestriction(): bool
|
||||
{
|
||||
return (bool)$this->categoryBeGroupTceFormsRestriction;
|
||||
}
|
||||
|
||||
public function getContentElementRelation(): bool
|
||||
{
|
||||
return (bool)$this->contentElementRelation;
|
||||
}
|
||||
|
||||
public function getContentElementPreview(): bool
|
||||
{
|
||||
return (bool)$this->contentElementPreview;
|
||||
}
|
||||
|
||||
public function getManualSorting(): bool
|
||||
{
|
||||
return (bool)$this->manualSorting;
|
||||
}
|
||||
|
||||
public function getArchiveDate(): string
|
||||
{
|
||||
return $this->archiveDate;
|
||||
}
|
||||
|
||||
public function getShowImporter(): bool
|
||||
{
|
||||
return (bool)$this->showImporter;
|
||||
}
|
||||
|
||||
public function setShowAdministrationModule($showAdministrationModule): void
|
||||
{
|
||||
$this->showAdministrationModule = $showAdministrationModule;
|
||||
}
|
||||
|
||||
public function getShowAdministrationModule(): bool
|
||||
{
|
||||
return (bool)$this->showAdministrationModule;
|
||||
}
|
||||
|
||||
public function getRteForTeaser(): bool
|
||||
{
|
||||
return (bool)$this->rteForTeaser;
|
||||
}
|
||||
|
||||
public function getResourceFolderImporter(): string
|
||||
{
|
||||
return $this->resourceFolderImporter;
|
||||
}
|
||||
|
||||
public function getStorageUidImporter(): int
|
||||
{
|
||||
return (int)$this->storageUidImporter;
|
||||
}
|
||||
|
||||
public function getDateTimeNotRequired(): bool
|
||||
{
|
||||
return (bool)$this->dateTimeNotRequired;
|
||||
}
|
||||
|
||||
public function getDateTimeRequired(): bool
|
||||
{
|
||||
return !(bool)$this->dateTimeNotRequired;
|
||||
}
|
||||
|
||||
public function getHidePageTreeForAdministrationModule(): bool
|
||||
{
|
||||
return (bool)$this->hidePageTreeForAdministrationModule;
|
||||
}
|
||||
|
||||
public function isAdvancedMediaPreview(): bool
|
||||
{
|
||||
return (bool)$this->advancedMediaPreview;
|
||||
}
|
||||
|
||||
public function getSlugBehaviour(): string
|
||||
{
|
||||
return $this->slugBehaviour;
|
||||
}
|
||||
}
|
||||
734
typo3conf/ext/news/Classes/Domain/Model/Dto/NewsDemand.php
Normal file
734
typo3conf/ext/news/Classes/Domain/Model/Dto/NewsDemand.php
Normal file
@@ -0,0 +1,734 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model\Dto;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Domain\Model\DemandInterface;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
|
||||
/**
|
||||
* News Demand object which holds all information to get the correct news records.
|
||||
*/
|
||||
class NewsDemand extends AbstractEntity implements DemandInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $categories = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $categoryConjunction = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $includeSubCategories = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $author = '';
|
||||
|
||||
/** @var string */
|
||||
protected $tags = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $archiveRestriction = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $timeRestriction = '';
|
||||
|
||||
/** @var string */
|
||||
protected $timeRestrictionHigh = '';
|
||||
|
||||
/** @var int */
|
||||
protected $topNewsRestriction = 0;
|
||||
|
||||
/** @var string */
|
||||
protected $dateField = '';
|
||||
|
||||
/** @var int */
|
||||
protected $month = 0;
|
||||
|
||||
/** @var int */
|
||||
protected $year = 0;
|
||||
|
||||
/** @var int */
|
||||
protected $day = 0;
|
||||
|
||||
/** @var string */
|
||||
protected $searchFields = '';
|
||||
|
||||
/** @var Search */
|
||||
protected $search;
|
||||
|
||||
/** @var string */
|
||||
protected $order = '';
|
||||
|
||||
/** @var string */
|
||||
protected $orderByAllowed = '';
|
||||
|
||||
/** @var bool */
|
||||
protected $topNewsFirst = false;
|
||||
|
||||
/** @var string */
|
||||
protected $storagePage = '';
|
||||
|
||||
/** @var int */
|
||||
protected $limit = 0;
|
||||
|
||||
/** @var int */
|
||||
protected $offset = 0;
|
||||
|
||||
/** @var bool */
|
||||
protected $excludeAlreadyDisplayedNews = false;
|
||||
|
||||
/** @var string */
|
||||
protected $hideIdList = '';
|
||||
|
||||
/** @var string */
|
||||
protected $idList = '';
|
||||
|
||||
/** @var string */
|
||||
protected $action = '';
|
||||
|
||||
/** @var string */
|
||||
protected $class = '';
|
||||
|
||||
/**
|
||||
* List of allowed types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $types = [];
|
||||
|
||||
/**
|
||||
* Holding custom data, use e.g. your ext key as array key
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_customSettings = [];
|
||||
|
||||
/**
|
||||
* Set archive settings
|
||||
*
|
||||
* @param string $archiveRestriction archive setting
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setArchiveRestriction(string $archiveRestriction): NewsDemand
|
||||
{
|
||||
$this->archiveRestriction = $archiveRestriction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archive setting
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getArchiveRestriction(): string
|
||||
{
|
||||
return $this->archiveRestriction;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of allowed categories
|
||||
*
|
||||
* @param array $categories categories
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setCategories(array $categories): NewsDemand
|
||||
{
|
||||
$this->categories = $categories;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed categories
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCategories(): array
|
||||
{
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set category mode
|
||||
*
|
||||
* @param string $categoryConjunction
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setCategoryConjunction(string $categoryConjunction): NewsDemand
|
||||
{
|
||||
$this->categoryConjunction = $categoryConjunction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category mode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCategoryConjunction(): string
|
||||
{
|
||||
return $this->categoryConjunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get include sub categories
|
||||
* @return bool
|
||||
*/
|
||||
public function getIncludeSubCategories(): bool
|
||||
{
|
||||
return (boolean)$this->includeSubCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $includeSubCategories
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setIncludeSubCategories(bool $includeSubCategories): NewsDemand
|
||||
{
|
||||
$this->includeSubCategories = $includeSubCategories;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set author
|
||||
*
|
||||
* @param string $author
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setAuthor(string $author): NewsDemand
|
||||
{
|
||||
$this->author = $author;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get author
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthor(): string
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Tags
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTags(): string
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Tags
|
||||
*
|
||||
* @param string $tags tags
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setTags(string $tags): NewsDemand
|
||||
{
|
||||
$this->tags = $tags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time limit low, either integer or string
|
||||
*
|
||||
* @param string|int $timeRestriction
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setTimeRestriction($timeRestriction): NewsDemand
|
||||
{
|
||||
$this->timeRestriction = $timeRestriction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time limit low
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function getTimeRestriction()
|
||||
{
|
||||
return $this->timeRestriction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time limit high
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function getTimeRestrictionHigh()
|
||||
{
|
||||
return $this->timeRestrictionHigh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time limit high
|
||||
*
|
||||
* @param string|int $timeRestrictionHigh
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setTimeRestrictionHigh($timeRestrictionHigh): NewsDemand
|
||||
{
|
||||
$this->timeRestrictionHigh = $timeRestrictionHigh;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order
|
||||
*
|
||||
* @param string $order order
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setOrder(string $order): NewsDemand
|
||||
{
|
||||
$this->order = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrder(): string
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order allowed
|
||||
*
|
||||
* @param string $orderByAllowed allowed fields for ordering
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setOrderByAllowed(string $orderByAllowed): NewsDemand
|
||||
{
|
||||
$this->orderByAllowed = $orderByAllowed;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed order fields
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrderByAllowed(): string
|
||||
{
|
||||
return $this->orderByAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order respect top news flag
|
||||
*
|
||||
* @param bool $topNewsFirst respect top news flag
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setTopNewsFirst(bool $topNewsFirst): NewsDemand
|
||||
{
|
||||
$this->topNewsFirst = $topNewsFirst;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order respect top news flag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getTopNewsFirst(): bool
|
||||
{
|
||||
return $this->topNewsFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set search fields
|
||||
*
|
||||
* @param string $searchFields search fields
|
||||
*
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setSearchFields(string $searchFields): NewsDemand
|
||||
{
|
||||
$this->searchFields = $searchFields;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search fields
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSearchFields(): string
|
||||
{
|
||||
return $this->searchFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set top news setting
|
||||
*
|
||||
* @param int $topNewsRestriction top news settings
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setTopNewsRestriction(int $topNewsRestriction): NewsDemand
|
||||
{
|
||||
$this->topNewsRestriction = $topNewsRestriction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get top news setting
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTopNewsRestriction(): int
|
||||
{
|
||||
return $this->topNewsRestriction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set list of storage pages
|
||||
*
|
||||
* @param string $storagePage storage page list
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setStoragePage(string $storagePage): NewsDemand
|
||||
{
|
||||
$this->storagePage = $storagePage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of storage pages
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStoragePage(): string
|
||||
{
|
||||
return $this->storagePage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get day restriction
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDay(): int
|
||||
{
|
||||
return $this->day;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set day restriction
|
||||
*
|
||||
* @param int $day
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setDay(int $day): NewsDemand
|
||||
{
|
||||
$this->day = (int)$day;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get month restriction
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMonth(): int
|
||||
{
|
||||
return $this->month;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set month restriction
|
||||
*
|
||||
* @param int $month month
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setMonth(int $month): NewsDemand
|
||||
{
|
||||
$this->month = (int)$month;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get year restriction
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getYear(): int
|
||||
{
|
||||
return $this->year;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set year restriction
|
||||
*
|
||||
* @param int $year year
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setYear(int $year): NewsDemand
|
||||
{
|
||||
$this->year = (int)$year;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set limit
|
||||
*
|
||||
* @param int $limit limit
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setLimit(int $limit): NewsDemand
|
||||
{
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get limit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLimit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set offset
|
||||
*
|
||||
* @param int $offset offset
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setOffset(int $offset): NewsDemand
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get offset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getOffset(): int
|
||||
{
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set date field which is used for datemenu
|
||||
*
|
||||
* @param string $dateField datefield
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setDateField(string $dateField): NewsDemand
|
||||
{
|
||||
$this->dateField = $dateField;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date field which is used for datemenu
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDateField(): string
|
||||
{
|
||||
if (in_array($this->dateField, ['datetime', 'archive'], true)
|
||||
|| isset($GLOBALS['TCA']['tx_news_domain_model_news']['columns'][$this->dateField])) {
|
||||
return $this->dateField;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search object
|
||||
*
|
||||
* @return null|Search
|
||||
*/
|
||||
public function getSearch(): ?Search
|
||||
{
|
||||
return $this->search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set search object
|
||||
*
|
||||
* @param null|Search $search search object
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setSearch(Search $search = null): NewsDemand
|
||||
{
|
||||
$this->search = $search;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flag if displayed news records should be excluded
|
||||
*
|
||||
* @param bool $excludeAlreadyDisplayedNews
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setExcludeAlreadyDisplayedNews(bool $excludeAlreadyDisplayedNews): NewsDemand
|
||||
{
|
||||
$this->excludeAlreadyDisplayedNews = $excludeAlreadyDisplayedNews;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flag if displayed news records should be excluded
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getExcludeAlreadyDisplayedNews(): bool
|
||||
{
|
||||
return $this->excludeAlreadyDisplayedNews;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHideIdList(): string
|
||||
{
|
||||
return $this->hideIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hideIdList
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setHideIdList(string $hideIdList): NewsDemand
|
||||
{
|
||||
$this->hideIdList = $hideIdList;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIdList(): string
|
||||
{
|
||||
return $this->idList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $idList
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setIdList(string $idList): NewsDemand
|
||||
{
|
||||
$this->idList = $idList;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAction(): string
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setAction(string $action): NewsDemand
|
||||
{
|
||||
$this->action = $action;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setClass(string $class): NewsDemand
|
||||
{
|
||||
$this->class = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @param string $controller
|
||||
* @return NewsDemand
|
||||
*/
|
||||
public function setActionAndClass(string $action, string $controller): NewsDemand
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->class = $controller;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTypes(): array
|
||||
{
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set allowed types
|
||||
*
|
||||
* @param array $types
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTypes(array $types): void
|
||||
{
|
||||
$this->types = $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCustomSettings(): array
|
||||
{
|
||||
return $this->_customSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $customSettings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCustomSettings(array $customSettings): void
|
||||
{
|
||||
$this->_customSettings = $customSettings;
|
||||
}
|
||||
}
|
||||
172
typo3conf/ext/news/Classes/Domain/Model/Dto/Search.php
Normal file
172
typo3conf/ext/news/Classes/Domain/Model/Dto/Search.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model\Dto;
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* News Demand object which holds all information to get the correct
|
||||
* news records.
|
||||
*/
|
||||
class Search extends AbstractEntity
|
||||
{
|
||||
|
||||
/**
|
||||
* Basic search word
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $subject = '';
|
||||
|
||||
/**
|
||||
* Search fields
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fields = '';
|
||||
|
||||
/**
|
||||
* Minimum date
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $minimumDate = '';
|
||||
|
||||
/**
|
||||
* Maximum date
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $maximumDate = '';
|
||||
|
||||
/**
|
||||
* Field using for date queries
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dateField = '';
|
||||
|
||||
/** @var bool */
|
||||
protected $splitSubjectWords = false;
|
||||
|
||||
/**
|
||||
* Get the subject
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject(): string
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set subject
|
||||
*
|
||||
* @param string $subject
|
||||
*/
|
||||
public function setSubject(string $subject): void
|
||||
{
|
||||
$this->subject = $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fields
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFields(): string
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fields
|
||||
*
|
||||
* @param string $fields
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFields(string $fields): void
|
||||
{
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $maximumDate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMaximumDate($maximumDate): void
|
||||
{
|
||||
$this->maximumDate = $maximumDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMaximumDate(): string
|
||||
{
|
||||
return $this->maximumDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $minimumDate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMinimumDate(string $minimumDate): void
|
||||
{
|
||||
$this->minimumDate = $minimumDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMinimumDate(): string
|
||||
{
|
||||
return $this->minimumDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dateField
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDateField($dateField): void
|
||||
{
|
||||
$this->dateField = $dateField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDateField(): string
|
||||
{
|
||||
return $this->dateField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSplitSubjectWords(): bool
|
||||
{
|
||||
return $this->splitSubjectWords;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $splitSubjectWords
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSplitSubjectWords($splitSubjectWords): void
|
||||
{
|
||||
$this->splitSubjectWords = $splitSubjectWords;
|
||||
}
|
||||
}
|
||||
184
typo3conf/ext/news/Classes/Domain/Model/FileReference.php
Normal file
184
typo3conf/ext/news/Classes/Domain/Model/FileReference.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* File Reference
|
||||
*/
|
||||
class FileReference extends \TYPO3\CMS\Extbase\Domain\Model\FileReference
|
||||
{
|
||||
const VIEW_DETAIL_ONLY = 0;
|
||||
const VIEW_LIST_AND_DETAIL = 1;
|
||||
const VIEW_LIST_ONLY = 2;
|
||||
|
||||
/**
|
||||
* Obsolete when foreign_selector is supported by ExtBase persistence layer
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $uidLocal = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $alternative = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $link = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $showinpreview = 0;
|
||||
|
||||
/**
|
||||
* Set File uid
|
||||
*
|
||||
* @param int $fileUid
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFileUid($fileUid): void
|
||||
{
|
||||
$this->uidLocal = $fileUid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get File UID
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFileUid(): int
|
||||
{
|
||||
return $this->uidLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set alternative
|
||||
*
|
||||
* @param string $alternative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAlternative($alternative): void
|
||||
{
|
||||
$this->alternative = $alternative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alternative
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlternative(): string
|
||||
{
|
||||
return (string)($this->alternative !== '' ? $this->alternative : $this->getOriginalResource()->getAlternative());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set description
|
||||
*
|
||||
* @param string $description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDescription($description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return (string)($this->description !== '' ? $this->description : $this->getOriginalResource()->getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set link
|
||||
*
|
||||
* @param string $link
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLink($link): void
|
||||
{
|
||||
$this->link = $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get link
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
return (string)($this->link !== '' ? $this->link : $this->getOriginalResource()->getLink());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTitle($title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return (string)($this->title !== '' ? $this->title : $this->getOriginalResource()->getTitle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set showinpreview
|
||||
*
|
||||
* @param int $showinpreview
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setShowinpreview($showinpreview): void
|
||||
{
|
||||
$this->showinpreview = $showinpreview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get showinpreview
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShowinpreview(): int
|
||||
{
|
||||
return $this->showinpreview;
|
||||
}
|
||||
}
|
||||
202
typo3conf/ext/news/Classes/Domain/Model/Link.php
Normal file
202
typo3conf/ext/news/Classes/Domain/Model/Link.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Link model
|
||||
*/
|
||||
class Link extends AbstractValueObject
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $crdate;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $tstamp;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $uri = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $l10nParent = 0;
|
||||
|
||||
/**
|
||||
* Get creation date
|
||||
*
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
public function getCrdate(): ?\DateTime
|
||||
{
|
||||
return $this->crdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set creation date
|
||||
*
|
||||
* @param \DateTime $crdate creation date
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCrdate($crdate): void
|
||||
{
|
||||
$this->crdate = $crdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timestamp
|
||||
*
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
public function getTstamp(): ?\DateTime
|
||||
{
|
||||
return $this->tstamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timestamp
|
||||
*
|
||||
* @param \DateTime $tstamp timestamp
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTstamp($tstamp): void
|
||||
{
|
||||
$this->tstamp = $tstamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title
|
||||
*
|
||||
* @param string $title title
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTitle($title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set description
|
||||
*
|
||||
* @param string $description description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDescription($description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uri
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUri(): string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set uri
|
||||
*
|
||||
* @param string $uri uri
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUri($uri): void
|
||||
{
|
||||
$this->uri = $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sys language
|
||||
*
|
||||
* @param int $sysLanguageUid
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSysLanguageUid($sysLanguageUid): void
|
||||
{
|
||||
$this->_languageUid = $sysLanguageUid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sys language
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSysLanguageUid(): int
|
||||
{
|
||||
return $this->_languageUid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set l10n parent
|
||||
*
|
||||
* @param int $l10nParent
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setL10nParent($l10nParent): void
|
||||
{
|
||||
$this->l10nParent = $l10nParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get l10n parent
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getL10nParent(): int
|
||||
{
|
||||
return $this->l10nParent;
|
||||
}
|
||||
}
|
||||
1665
typo3conf/ext/news/Classes/Domain/Model/News.php
Normal file
1665
typo3conf/ext/news/Classes/Domain/Model/News.php
Normal file
File diff suppressed because it is too large
Load Diff
17
typo3conf/ext/news/Classes/Domain/Model/NewsDefault.php
Normal file
17
typo3conf/ext/news/Classes/Domain/Model/NewsDefault.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* News model for default news
|
||||
*/
|
||||
class NewsDefault extends News
|
||||
{
|
||||
}
|
||||
17
typo3conf/ext/news/Classes/Domain/Model/NewsExternal.php
Normal file
17
typo3conf/ext/news/Classes/Domain/Model/NewsExternal.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* News model for external news
|
||||
*/
|
||||
class NewsExternal extends News
|
||||
{
|
||||
}
|
||||
17
typo3conf/ext/news/Classes/Domain/Model/NewsInternal.php
Normal file
17
typo3conf/ext/news/Classes/Domain/Model/NewsInternal.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* News model for internal news
|
||||
*/
|
||||
class NewsInternal extends News
|
||||
{
|
||||
}
|
||||
212
typo3conf/ext/news/Classes/Domain/Model/Tag.php
Normal file
212
typo3conf/ext/news/Classes/Domain/Model/Tag.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Tag model
|
||||
*/
|
||||
class Tag extends AbstractValueObject
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $crdate;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $tstamp;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoTitle = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoDescription = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoHeadline = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seoText = '';
|
||||
|
||||
/** @var string */
|
||||
protected $slug = '';
|
||||
|
||||
/**
|
||||
* Get crdate
|
||||
*
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
public function getCrdate(): ?\DateTime
|
||||
{
|
||||
return $this->crdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set crdate
|
||||
*
|
||||
* @param \DateTime $crdate crdate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCrdate($crdate): void
|
||||
{
|
||||
$this->crdate = $crdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Tstamp
|
||||
*
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
public function getTstamp(): ?\DateTime
|
||||
{
|
||||
return $this->tstamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tstamp
|
||||
*
|
||||
* @param \DateTime $tstamp tstamp
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTstamp($tstamp): void
|
||||
{
|
||||
$this->tstamp = $tstamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title
|
||||
*
|
||||
* @param string $title title
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTitle($title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSeoTitle(): string
|
||||
{
|
||||
return $this->seoTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $seoTitle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSeoTitle($seoTitle): void
|
||||
{
|
||||
$this->seoTitle = $seoTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSeoDescription(): string
|
||||
{
|
||||
return $this->seoDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $seoDescription
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSeoDescription($seoDescription): void
|
||||
{
|
||||
$this->seoDescription = $seoDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSeoHeadline(): string
|
||||
{
|
||||
return $this->seoHeadline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $seoHeadline
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSeoHeadline($seoHeadline): void
|
||||
{
|
||||
$this->seoHeadline = $seoHeadline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSeoText(): string
|
||||
{
|
||||
return $this->seoText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $seoText
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSeoText($seoText): void
|
||||
{
|
||||
$this->seoText = $seoText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSlug(): string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slug
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSlug($slug): void
|
||||
{
|
||||
$this->slug = $slug;
|
||||
}
|
||||
}
|
||||
573
typo3conf/ext/news/Classes/Domain/Model/TtContent.php
Normal file
573
typo3conf/ext/news/Classes/Domain/Model/TtContent.php
Normal file
@@ -0,0 +1,573 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Model of tt_content
|
||||
*/
|
||||
class TtContent extends AbstractEntity
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $crdate;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $tstamp;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $CType = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $header = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $headerPosition = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bodytext = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $colPos = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $image = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $imagewidth = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $imageorient = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $imagecaption = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $imagecols = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $imageborder = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $media = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $layout = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $cols = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $subheader = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $headerLink = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $imageLink = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $imageZoom = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $altText = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $titleText = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $headerLayout = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $listType = '';
|
||||
|
||||
/**
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
public function getCrdate(): ?\DateTime
|
||||
{
|
||||
return $this->crdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $crdate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCrdate($crdate): void
|
||||
{
|
||||
$this->crdate = $crdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|\DateTime
|
||||
*/
|
||||
public function getTstamp(): ?\DateTime
|
||||
{
|
||||
return $this->tstamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $tstamp
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTstamp($tstamp): void
|
||||
{
|
||||
$this->tstamp = $tstamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCType(): string
|
||||
{
|
||||
return $this->CType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $ctype
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCType(string $ctype): void
|
||||
{
|
||||
$this->CType = $ctype;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHeader(): string
|
||||
{
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $header
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHeader(string $header): void
|
||||
{
|
||||
$this->header = $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHeaderPosition(): string
|
||||
{
|
||||
return $this->headerPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $headerPosition
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHeaderPosition(string $headerPosition): void
|
||||
{
|
||||
$this->headerPosition = $headerPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBodytext(): string
|
||||
{
|
||||
return $this->bodytext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bodytext
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setBodytext(string $bodytext): void
|
||||
{
|
||||
$this->bodytext = $bodytext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the colpos
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getColPos(): int
|
||||
{
|
||||
return (int)$this->colPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set colpos
|
||||
*
|
||||
* @param int $colPos
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setColPos($colPos): void
|
||||
{
|
||||
$this->colPos = $colPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getImage(): string
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $image
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImage(string $image): void
|
||||
{
|
||||
$this->image = $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getImagewidth(): int
|
||||
{
|
||||
return $this->imagewidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $imagewidth
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImagewidth(int $imagewidth): void
|
||||
{
|
||||
$this->imagewidth = $imagewidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getImageorient(): int
|
||||
{
|
||||
return $this->imageorient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $imageorient
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImageorient(int $imageorient): void
|
||||
{
|
||||
$this->imageorient = $imageorient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getImagecaption(): string
|
||||
{
|
||||
return $this->imagecaption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $imagecaption
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImagecaption(string $imagecaption): void
|
||||
{
|
||||
$this->imagecaption = $imagecaption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getImagecols(): int
|
||||
{
|
||||
return $this->imagecols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $imagecols
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImagecols(int $imagecols): void
|
||||
{
|
||||
$this->imagecols = $imagecols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getImageborder(): int
|
||||
{
|
||||
return $this->imageborder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $imageborder
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImageborder(int $imageborder): void
|
||||
{
|
||||
$this->imageborder = $imageborder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMedia(): string
|
||||
{
|
||||
return $this->media;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $media
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMedia(string $media): void
|
||||
{
|
||||
$this->media = $media;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLayout(): string
|
||||
{
|
||||
return $this->layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $layout
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLayout(string $layout): void
|
||||
{
|
||||
$this->layout = $layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCols(): int
|
||||
{
|
||||
return $this->cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $cols
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCols(int $cols): void
|
||||
{
|
||||
$this->cols = $cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSubheader(): string
|
||||
{
|
||||
return $this->subheader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $subheader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSubheader(string $subheader): void
|
||||
{
|
||||
$this->subheader = $subheader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHeaderLink(): string
|
||||
{
|
||||
return $this->headerLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $headerLink
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHeaderLink(string $headerLink): void
|
||||
{
|
||||
$this->headerLink = $headerLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getImageLink(): string
|
||||
{
|
||||
return $this->imageLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $imageLink
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImageLink(string $imageLink): void
|
||||
{
|
||||
$this->imageLink = $imageLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getImageZoom(): string
|
||||
{
|
||||
return $this->imageZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $imageZoom
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setImageZoom(string $imageZoom): void
|
||||
{
|
||||
$this->imageZoom = $imageZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAltText(): string
|
||||
{
|
||||
return $this->altText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $altText
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAltText(string $altText): void
|
||||
{
|
||||
$this->altText = $altText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleText(): string
|
||||
{
|
||||
return $this->titleText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $titleText
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTitleText(string $titleText): void
|
||||
{
|
||||
$this->titleText = $titleText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHeaderLayout(): string
|
||||
{
|
||||
return $this->headerLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $headerLayout
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHeaderLayout(string $headerLayout): void
|
||||
{
|
||||
$this->headerLayout = $headerLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getListType(): string
|
||||
{
|
||||
return $this->listType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $listType
|
||||
* @return void
|
||||
*/
|
||||
public function setListType(string $listType): void
|
||||
{
|
||||
$this->listType = $listType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\DemandInterface;
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract demanded repository
|
||||
*/
|
||||
abstract class AbstractDemandedRepository extends Repository implements DemandedRepositoryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface
|
||||
*/
|
||||
protected $storageBackend;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function injectStorageBackend(
|
||||
BackendInterface $storageBackend
|
||||
): void {
|
||||
$this->storageBackend = $storageBackend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of constraints created from a given demand object.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
|
||||
* @param DemandInterface $demand
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface[]
|
||||
* @abstract
|
||||
*/
|
||||
abstract protected function createConstraintsFromDemand(
|
||||
QueryInterface $query,
|
||||
DemandInterface $demand
|
||||
): array;
|
||||
|
||||
/**
|
||||
* Returns an array of orderings created from a given demand object.
|
||||
*
|
||||
* @param DemandInterface $demand
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface[]
|
||||
* @abstract
|
||||
*/
|
||||
abstract protected function createOrderingsFromDemand(DemandInterface $demand): array;
|
||||
|
||||
/**
|
||||
* Returns the objects of this repository matching the demand.
|
||||
*
|
||||
* @param DemandInterface $demand
|
||||
* @param bool $respectEnableFields
|
||||
* @param bool $disableLanguageOverlayMode
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
|
||||
*/
|
||||
public function findDemanded(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false)
|
||||
{
|
||||
$query = $this->generateQuery($demand, $respectEnableFields, $disableLanguageOverlayMode);
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database query to get the matching result
|
||||
*
|
||||
* @param DemandInterface $demand
|
||||
* @param bool $respectEnableFields
|
||||
* @param bool $disableLanguageOverlayMode
|
||||
* @return string
|
||||
*/
|
||||
public function findDemandedRaw(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false): string
|
||||
{
|
||||
$query = $this->generateQuery($demand, $respectEnableFields, $disableLanguageOverlayMode);
|
||||
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class);
|
||||
if ($versionInformation->getMajorVersion() >= 11) {
|
||||
$queryParser = GeneralUtility::makeInstance(Typo3DbQueryParser::class);
|
||||
} else {
|
||||
$queryParser = $this->objectManager->get(Typo3DbQueryParser::class);
|
||||
}
|
||||
|
||||
$queryBuilder = $queryParser->convertQueryToDoctrineQueryBuilder($query);
|
||||
$queryParameters = $queryBuilder->getParameters();
|
||||
$params = [];
|
||||
foreach ($queryParameters as $key => $value) {
|
||||
// prefix array keys with ':'
|
||||
$params[':' . $key] = (is_numeric($value)) ? $value : "'" . $value . "'"; //all non numeric values have to be quoted
|
||||
unset($params[$key]);
|
||||
}
|
||||
// replace placeholders with real values
|
||||
$query = strtr($queryBuilder->getSQL(), $params);
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DemandInterface $demand
|
||||
* @param bool $respectEnableFields
|
||||
* @param bool $disableLanguageOverlayMode
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryInterface
|
||||
*/
|
||||
protected function generateQuery(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false): \TYPO3\CMS\Extbase\Persistence\QueryInterface
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
|
||||
if ($disableLanguageOverlayMode) {
|
||||
$query->getQuerySettings()->setLanguageOverlayMode(false);
|
||||
}
|
||||
|
||||
$constraints = $this->createConstraintsFromDemand($query, $demand);
|
||||
|
||||
// Call hook functions for additional constraints
|
||||
if ($hooks = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Domain/Repository/AbstractDemandedRepository.php']['findDemanded'] ?? []) {
|
||||
$params = [
|
||||
'demand' => $demand,
|
||||
'respectEnableFields' => &$respectEnableFields,
|
||||
'query' => $query,
|
||||
'constraints' => &$constraints,
|
||||
];
|
||||
foreach ($hooks as $reference) {
|
||||
GeneralUtility::callUserFunction($reference, $params, $this);
|
||||
}
|
||||
}
|
||||
|
||||
if ($respectEnableFields === false) {
|
||||
$query->getQuerySettings()->setIgnoreEnableFields(true);
|
||||
|
||||
$constraints[] = $query->equals('deleted', 0);
|
||||
}
|
||||
|
||||
if (!empty($constraints)) {
|
||||
$query->matching(
|
||||
$query->logicalAnd($constraints)
|
||||
);
|
||||
}
|
||||
|
||||
if ($orderings = $this->createOrderingsFromDemand($demand)) {
|
||||
$query->setOrderings($orderings);
|
||||
}
|
||||
|
||||
// @todo consider moving this to a separate function as well
|
||||
if ($demand->getLimit() != null) {
|
||||
$query->setLimit((int)$demand->getLimit());
|
||||
}
|
||||
|
||||
// @todo consider moving this to a separate function as well
|
||||
if ($demand->getOffset() != null) {
|
||||
if (!$query->getLimit()) {
|
||||
$query->setLimit(PHP_INT_MAX);
|
||||
}
|
||||
$query->setOffset((int)$demand->getOffset());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number objects of this repository matching the demand.
|
||||
*
|
||||
* @param DemandInterface $demand
|
||||
* @return int
|
||||
*/
|
||||
public function countDemanded(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false): int
|
||||
{
|
||||
$query = $this->generateQuery($demand, $respectEnableFields, $disableLanguageOverlayMode);
|
||||
|
||||
if ($constraints = $this->createConstraintsFromDemand($query, $demand)) {
|
||||
$query->matching(
|
||||
$query->logicalAnd($constraints)
|
||||
);
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
return $result->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Get data used in the administration view
|
||||
*/
|
||||
class AdministrationRepository
|
||||
{
|
||||
public function getTotalCounts(): array
|
||||
{
|
||||
$count = [];
|
||||
|
||||
$queryBuilder = $this->getQueryBuilder('tx_news_domain_model_news');
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
|
||||
$count['tx_news_domain_model_news'] = $queryBuilder
|
||||
->count('*')
|
||||
->from('tx_news_domain_model_news')
|
||||
->execute()->fetchColumn(0);
|
||||
|
||||
$queryBuilder = $this->getQueryBuilder('sys_category_record_mm');
|
||||
$count['category_relations'] = $queryBuilder
|
||||
->count('*')
|
||||
->from('sys_category_record_mm')
|
||||
->where($queryBuilder->expr()->like(
|
||||
'tablenames',
|
||||
$queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)
|
||||
))
|
||||
->execute()->fetchColumn(0);
|
||||
|
||||
if ($count['tx_news_domain_model_news'] > 0 && $count['category_relations']) {
|
||||
$count['_both'] = true;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function getConnection(string $table): Connection
|
||||
{
|
||||
return GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
|
||||
}
|
||||
|
||||
private function getQueryBuilder(string $table): QueryBuilder
|
||||
{
|
||||
return $this->getConnection($table)->createQueryBuilder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use Doctrine\DBAL\Connection;
|
||||
use GeorgRinger\News\Domain\Model\Category;
|
||||
use GeorgRinger\News\Domain\Model\DemandInterface;
|
||||
use GeorgRinger\News\Service\CategoryService;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
|
||||
/**
|
||||
* Category repository with all callable functionality
|
||||
*/
|
||||
class CategoryRepository extends AbstractDemandedRepository
|
||||
{
|
||||
protected function createConstraintsFromDemand(QueryInterface $query, DemandInterface $demand): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function createOrderingsFromDemand(DemandInterface $demand): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find category by import source and import id
|
||||
*
|
||||
* @param string $importSource import source
|
||||
* @param int $importId import id
|
||||
* @param bool $asArray return result as array
|
||||
* @return Category|array
|
||||
*/
|
||||
public function findOneByImportSourceAndImportId($importSource, $importId, $asArray = false)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
$query->getQuerySettings()->setIgnoreEnableFields(true);
|
||||
|
||||
$result = $query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('importSource', $importSource),
|
||||
$query->equals('importId', $importId)
|
||||
)
|
||||
)->execute($asArray);
|
||||
if ($asArray) {
|
||||
if (isset($result[0])) {
|
||||
return $result[0];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return $result->getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find categories by a given pid
|
||||
*
|
||||
* @param int $pid pid
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
|
||||
*/
|
||||
public function findParentCategoriesByPid($pid)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
return $query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('pid', (int)$pid),
|
||||
$query->equals('parentcategory', 0)
|
||||
)
|
||||
)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find category tree
|
||||
*
|
||||
* @param array $rootIdList list of id s
|
||||
* @return QueryInterface|array
|
||||
*/
|
||||
public function findTree(array $rootIdList, $startingPoint = null)
|
||||
{
|
||||
$subCategories = CategoryService::getChildrenCategories(implode(',', $rootIdList));
|
||||
|
||||
$idList = explode(',', $subCategories);
|
||||
if (empty($idList)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ordering = ['sorting' => QueryInterface::ORDER_ASCENDING];
|
||||
$categories = $this->findByIdList($idList, $ordering, $startingPoint);
|
||||
$flatCategories = [];
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
$flatCategories[$category->getUid()] = [
|
||||
'item' => $category,
|
||||
'parent' => ($category->getParentcategory()) ? $category->getParentcategory()->getUid() : null
|
||||
];
|
||||
}
|
||||
|
||||
$tree = [];
|
||||
|
||||
// If leaves are selected without its parents selected, those are shown as parent
|
||||
foreach ($flatCategories as $id => &$flatCategory) {
|
||||
if (!isset($flatCategories[$flatCategory['parent']])) {
|
||||
$flatCategory['parent'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($flatCategories as $id => &$node) {
|
||||
if ($node['parent'] === null) {
|
||||
$tree[$id] = &$node;
|
||||
} else {
|
||||
$flatCategories[$node['parent']]['children'][$id] = &$node;
|
||||
}
|
||||
}
|
||||
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find categories by a given pid
|
||||
*
|
||||
* @param array $idList list of id s
|
||||
* @param array $ordering ordering
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
|
||||
*/
|
||||
public function findByIdList(array $idList, array $ordering = [], $startingPoint = null)
|
||||
{
|
||||
if (empty($idList)) {
|
||||
throw new \InvalidArgumentException('The given id list is empty.', 1484823597);
|
||||
}
|
||||
$query = $this->createQuery();
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
$query->getQuerySettings()->setRespectSysLanguage(true);
|
||||
|
||||
if (count($ordering) > 0) {
|
||||
$query->setOrderings($ordering);
|
||||
}
|
||||
$this->overlayTranslatedCategoryIds($idList);
|
||||
|
||||
$conditions = [];
|
||||
$conditions[] = $query->in('uid', $idList);
|
||||
|
||||
if (is_null($startingPoint) === false) {
|
||||
$conditions[] = $query->in('pid', GeneralUtility::trimExplode(',', $startingPoint, true));
|
||||
}
|
||||
|
||||
return $query->matching(
|
||||
$query->logicalAnd(
|
||||
$conditions
|
||||
)
|
||||
)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find categories by a given parent
|
||||
*
|
||||
* @param int $parent parent
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
|
||||
*/
|
||||
public function findChildren($parent)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
return $query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('parentcategory', (int)$parent)
|
||||
)
|
||||
)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overlay the category ids with the ones from current language
|
||||
*
|
||||
* @param array $idList
|
||||
* return void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function overlayTranslatedCategoryIds(array &$idList): void
|
||||
{
|
||||
$language = $this->getSysLanguageUid();
|
||||
if ($language > 0 && !empty($idList)) {
|
||||
if (isset($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category');
|
||||
$rows = $queryBuilder
|
||||
->select('l10n_parent', 'uid', 'sys_language_uid')
|
||||
->from('sys_category')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->in('l10n_parent', $queryBuilder->createNamedParameter($idList, Connection::PARAM_INT_ARRAY))
|
||||
)
|
||||
->execute()->fetchAll();
|
||||
|
||||
$idList = $this->replaceCategoryIds($idList, $rows);
|
||||
}
|
||||
// @todo currently only implemented for the frontend
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current sys language uid
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getSysLanguageUid(): int
|
||||
{
|
||||
$sysLanguage = 0;
|
||||
|
||||
if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() === 10) {
|
||||
$sysLanguage = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id');
|
||||
} elseif (isset($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
|
||||
$sysLanguage = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'contentId');
|
||||
} elseif ((int)GeneralUtility::_GP('L')) {
|
||||
$sysLanguage = (int)GeneralUtility::_GP('L');
|
||||
}
|
||||
|
||||
return $sysLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace ids in array by the given ones
|
||||
*
|
||||
* @param array $idList
|
||||
* @param array $rows
|
||||
* @return array
|
||||
*/
|
||||
protected function replaceCategoryIds(array $idList, array $rows): array
|
||||
{
|
||||
foreach ($rows as $row) {
|
||||
$pos = array_search($row['l10n_parent'], $idList);
|
||||
if ($pos !== false) {
|
||||
$idList[$pos] = (int)$row['uid'];
|
||||
}
|
||||
}
|
||||
|
||||
return $idList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Domain\Model\DemandInterface;
|
||||
|
||||
/**
|
||||
* Demand domain model interface
|
||||
*/
|
||||
interface DemandedRepositoryInterface
|
||||
{
|
||||
public function findDemanded(DemandInterface $demand, $respectEnableFields = true);
|
||||
|
||||
public function countDemanded(DemandInterface $demand);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Repository for file objects
|
||||
*/
|
||||
class FileRepository extends Repository
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Repository for link objects
|
||||
*/
|
||||
class LinkRepository extends Repository
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Repository for media objects
|
||||
*/
|
||||
class MediaRepository extends Repository
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* News repository with all the callable functionality
|
||||
*/
|
||||
class NewsDefaultRepository extends NewsRepository
|
||||
{
|
||||
}
|
||||
519
typo3conf/ext/news/Classes/Domain/Repository/NewsRepository.php
Normal file
519
typo3conf/ext/news/Classes/Domain/Repository/NewsRepository.php
Normal file
@@ -0,0 +1,519 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Domain\Model\DemandInterface;
|
||||
use GeorgRinger\News\Domain\Model\Dto\NewsDemand;
|
||||
use GeorgRinger\News\Service\CategoryService;
|
||||
use GeorgRinger\News\Utility\ConstraintHelper;
|
||||
use GeorgRinger\News\Utility\Validation;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
|
||||
/**
|
||||
* News repository with all the callable functionality
|
||||
*/
|
||||
class NewsRepository extends AbstractDemandedRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a category constraint created by
|
||||
* a given list of categories and a junction string
|
||||
*
|
||||
* @param QueryInterface $query
|
||||
* @param array $categories
|
||||
* @param string $conjunction
|
||||
* @param bool $includeSubCategories
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|null
|
||||
*/
|
||||
protected function createCategoryConstraint(
|
||||
QueryInterface $query,
|
||||
$categories,
|
||||
$conjunction,
|
||||
$includeSubCategories = false
|
||||
): ?\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface {
|
||||
$constraint = null;
|
||||
$categoryConstraints = [];
|
||||
|
||||
// If "ignore category selection" is used, nothing needs to be done
|
||||
if (empty($conjunction)) {
|
||||
return $constraint;
|
||||
}
|
||||
|
||||
if (!is_array($categories)) {
|
||||
$categories = GeneralUtility::intExplode(',', $categories, true);
|
||||
}
|
||||
foreach ($categories as $category) {
|
||||
if ($includeSubCategories) {
|
||||
$subCategories = GeneralUtility::trimExplode(
|
||||
',',
|
||||
CategoryService::getChildrenCategories($category, 0, '', true),
|
||||
true
|
||||
);
|
||||
$subCategoryConstraint = [];
|
||||
$subCategoryConstraint[] = $query->contains('categories', $category);
|
||||
if (count($subCategories) > 0) {
|
||||
foreach ($subCategories as $subCategory) {
|
||||
$subCategoryConstraint[] = $query->contains('categories', $subCategory);
|
||||
}
|
||||
}
|
||||
if ($subCategoryConstraint) {
|
||||
$categoryConstraints[] = $query->logicalOr($subCategoryConstraint);
|
||||
}
|
||||
} else {
|
||||
$categoryConstraints[] = $query->contains('categories', $category);
|
||||
}
|
||||
}
|
||||
|
||||
if ($categoryConstraints) {
|
||||
switch (strtolower($conjunction)) {
|
||||
case 'or':
|
||||
$constraint = $query->logicalOr($categoryConstraints);
|
||||
break;
|
||||
case 'notor':
|
||||
$constraint = $query->logicalNot($query->logicalOr($categoryConstraints));
|
||||
break;
|
||||
case 'notand':
|
||||
$constraint = $query->logicalNot($query->logicalAnd($categoryConstraints));
|
||||
break;
|
||||
case 'and':
|
||||
default:
|
||||
$constraint = $query->logicalAnd($categoryConstraints);
|
||||
}
|
||||
}
|
||||
|
||||
return $constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of constraints created from a given demand object.
|
||||
*
|
||||
* @param QueryInterface $query
|
||||
* @param DemandInterface $demand
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return (\TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface|null)[]
|
||||
*
|
||||
* @psalm-return array<string, \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface|null>
|
||||
*/
|
||||
protected function createConstraintsFromDemand(QueryInterface $query, DemandInterface $demand): array
|
||||
{
|
||||
/** @var NewsDemand $demand */
|
||||
$constraints = [];
|
||||
|
||||
if ($demand->getCategories() && $demand->getCategories() !== '0') {
|
||||
$constraints['categories'] = $this->createCategoryConstraint(
|
||||
$query,
|
||||
$demand->getCategories(),
|
||||
$demand->getCategoryConjunction(),
|
||||
$demand->getIncludeSubCategories()
|
||||
);
|
||||
}
|
||||
|
||||
if ($demand->getAuthor()) {
|
||||
$constraints['author'] = $query->equals('author', $demand->getAuthor());
|
||||
}
|
||||
|
||||
if ($demand->getTypes()) {
|
||||
$constraints['types'] = $query->in('type', $demand->getTypes());
|
||||
}
|
||||
|
||||
// archived
|
||||
if ($demand->getArchiveRestriction() === 'archived') {
|
||||
$constraints['archived'] = $query->logicalAnd(
|
||||
$query->lessThan('archive', $GLOBALS['SIM_EXEC_TIME']),
|
||||
$query->greaterThan('archive', 0)
|
||||
);
|
||||
} elseif ($demand->getArchiveRestriction() === 'active') {
|
||||
$constraints['active'] = $query->logicalOr(
|
||||
$query->greaterThanOrEqual('archive', $GLOBALS['SIM_EXEC_TIME']),
|
||||
$query->equals('archive', 0)
|
||||
);
|
||||
}
|
||||
|
||||
// Time restriction greater than or equal
|
||||
$timeRestrictionField = $demand->getDateField();
|
||||
$timeRestrictionField = (empty($timeRestrictionField)) ? 'datetime' : $timeRestrictionField;
|
||||
|
||||
if ($demand->getTimeRestriction()) {
|
||||
$timeLimit = ConstraintHelper::getTimeRestrictionLow($demand->getTimeRestriction());
|
||||
|
||||
$constraints['timeRestrictionGreater'] = $query->greaterThanOrEqual(
|
||||
$timeRestrictionField,
|
||||
$timeLimit
|
||||
);
|
||||
}
|
||||
|
||||
// Time restriction less than or equal
|
||||
if ($demand->getTimeRestrictionHigh()) {
|
||||
$timeLimit = ConstraintHelper::getTimeRestrictionHigh($demand->getTimeRestrictionHigh());
|
||||
|
||||
$constraints['timeRestrictionLess'] = $query->lessThanOrEqual(
|
||||
$timeRestrictionField,
|
||||
$timeLimit
|
||||
);
|
||||
}
|
||||
|
||||
// top news
|
||||
if ($demand->getTopNewsRestriction() == 1) {
|
||||
$constraints['topNews1'] = $query->equals('istopnews', 1);
|
||||
} elseif ($demand->getTopNewsRestriction() == 2) {
|
||||
$constraints['topNews2'] = $query->equals('istopnews', 0);
|
||||
}
|
||||
|
||||
// storage page
|
||||
if ($demand->getStoragePage()) {
|
||||
$pidList = GeneralUtility::intExplode(',', $demand->getStoragePage(), true);
|
||||
$constraints['pid'] = $query->in('pid', $pidList);
|
||||
}
|
||||
|
||||
// month & year OR year only
|
||||
if ($demand->getYear() > 0) {
|
||||
if (null === $demand->getDateField()) {
|
||||
throw new \InvalidArgumentException('No Datefield is set, therefore no Datemenu is possible!');
|
||||
}
|
||||
if ($demand->getMonth() > 0) {
|
||||
if ($demand->getDay() > 0) {
|
||||
$begin = mktime(0, 0, 0, $demand->getMonth(), $demand->getDay(), $demand->getYear());
|
||||
$end = mktime(23, 59, 59, $demand->getMonth(), $demand->getDay(), $demand->getYear());
|
||||
} else {
|
||||
$begin = mktime(0, 0, 0, $demand->getMonth(), 1, $demand->getYear());
|
||||
$end = mktime(23, 59, 59, ($demand->getMonth() + 1), 0, $demand->getYear());
|
||||
}
|
||||
} else {
|
||||
$begin = mktime(0, 0, 0, 1, 1, $demand->getYear());
|
||||
$end = mktime(23, 59, 59, 12, 31, $demand->getYear());
|
||||
}
|
||||
$constraints['datetime'] = $query->logicalAnd([
|
||||
$query->greaterThanOrEqual($demand->getDateField(), $begin),
|
||||
$query->lessThanOrEqual($demand->getDateField(), $end)
|
||||
]);
|
||||
}
|
||||
|
||||
// Tags
|
||||
$tags = $demand->getTags();
|
||||
if ($tags && is_string($tags)) {
|
||||
$tagList = explode(',', $tags);
|
||||
|
||||
$subConstraints = [];
|
||||
foreach ($tagList as $singleTag) {
|
||||
$subConstraints[] = $query->contains('tags', $singleTag);
|
||||
}
|
||||
if (count($subConstraints) > 0) {
|
||||
$constraints['tags'] = $query->logicalOr($subConstraints);
|
||||
}
|
||||
}
|
||||
|
||||
// Search
|
||||
$searchConstraints = $this->getSearchConstraints($query, $demand);
|
||||
if (!empty($searchConstraints)) {
|
||||
$constraints['search'] = $query->logicalAnd($searchConstraints);
|
||||
}
|
||||
|
||||
// Exclude already displayed
|
||||
if ($demand->getExcludeAlreadyDisplayedNews() && isset($GLOBALS['EXT']['news']['alreadyDisplayed']) && !empty($GLOBALS['EXT']['news']['alreadyDisplayed'])) {
|
||||
$constraints['excludeAlreadyDisplayedNews'] = $query->logicalNot(
|
||||
$query->in(
|
||||
'uid',
|
||||
$GLOBALS['EXT']['news']['alreadyDisplayed']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Hide id list
|
||||
$hideIdList = $demand->getHideIdList();
|
||||
if ($hideIdList) {
|
||||
$constraints['hideIdInList'] = $query->logicalNot(
|
||||
$query->in(
|
||||
'uid',
|
||||
GeneralUtility::intExplode(',', $hideIdList, true)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Id list
|
||||
$idList = $demand->getIdList();
|
||||
if ($idList) {
|
||||
$constraints['idList'] = $query->in('uid', GeneralUtility::intExplode(',', $idList, true));
|
||||
}
|
||||
|
||||
// Clean not used constraints
|
||||
foreach ($constraints as $key => $value) {
|
||||
if (null === $value) {
|
||||
unset($constraints[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of orderings created from a given demand object.
|
||||
*
|
||||
* @param DemandInterface $demand
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
protected function createOrderingsFromDemand(DemandInterface $demand): array
|
||||
{
|
||||
$orderings = [];
|
||||
if ($demand->getTopNewsFirst()) {
|
||||
$orderings['istopnews'] = QueryInterface::ORDER_DESCENDING;
|
||||
}
|
||||
|
||||
if (Validation::isValidOrdering($demand->getOrder(), $demand->getOrderByAllowed())) {
|
||||
$orderList = GeneralUtility::trimExplode(',', $demand->getOrder(), true);
|
||||
|
||||
if (!empty($orderList)) {
|
||||
// go through every order statement
|
||||
foreach ($orderList as $orderItem) {
|
||||
$orderSplit = GeneralUtility::trimExplode(' ', $orderItem, true);
|
||||
$orderField = $orderSplit[0];
|
||||
$ascDesc = $orderSplit[1] ?? '';
|
||||
if ($ascDesc) {
|
||||
$orderings[$orderField] = ((strtolower($ascDesc) === 'desc') ?
|
||||
QueryInterface::ORDER_DESCENDING :
|
||||
QueryInterface::ORDER_ASCENDING);
|
||||
} else {
|
||||
$orderings[$orderField] = QueryInterface::ORDER_ASCENDING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $orderings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first news by import and source id
|
||||
*
|
||||
* @param string $importSource import source
|
||||
* @param int $importId import id
|
||||
* @param bool $asArray return result as array
|
||||
* @return \GeorgRinger\News\Domain\Model\News|array
|
||||
*/
|
||||
public function findOneByImportSourceAndImportId($importSource, $importId, $asArray = false)
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
$query->getQuerySettings()->setRespectSysLanguage(false);
|
||||
$query->getQuerySettings()->setIgnoreEnableFields(true);
|
||||
|
||||
$result = $query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('importSource', $importSource),
|
||||
$query->equals('importId', $importId)
|
||||
)
|
||||
)->execute($asArray);
|
||||
if ($asArray) {
|
||||
if (isset($result[0])) {
|
||||
return $result[0];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return $result->getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override default findByUid function to enable also the option to turn of
|
||||
* the enableField setting
|
||||
*
|
||||
* @param int $uid id of record
|
||||
* @param bool $respectEnableFields if set to false, hidden records are shown
|
||||
* @return \GeorgRinger\News\Domain\Model\News|null
|
||||
*/
|
||||
public function findByUid($uid, $respectEnableFields = true): ?\GeorgRinger\News\Domain\Model\News
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
$query->getQuerySettings()->setRespectSysLanguage(false);
|
||||
|
||||
if (!$respectEnableFields) {
|
||||
$query->getQuerySettings()->setIgnoreEnableFields(true);
|
||||
$query->getQuerySettings()->setLanguageOverlayMode(false);
|
||||
}
|
||||
|
||||
return $query->matching(
|
||||
$query->logicalAnd(
|
||||
$query->equals('uid', $uid),
|
||||
$query->equals('deleted', 0)
|
||||
)
|
||||
)->execute()->getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of news records by month/year and
|
||||
* returns the result compiled as array
|
||||
*
|
||||
* @param DemandInterface $demand
|
||||
* @return array
|
||||
*/
|
||||
public function countByDate(DemandInterface $demand): array
|
||||
{
|
||||
$data = [];
|
||||
$sql = $this->findDemandedRaw($demand);
|
||||
|
||||
// strip unwanted order by
|
||||
$sql = $this->stripOrderBy($sql);
|
||||
|
||||
// Get the month/year into the result
|
||||
$field = $demand->getDateField();
|
||||
$field = empty($field) ? 'datetime' : $field;
|
||||
|
||||
$sql = 'SELECT MONTH(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND ) AS "_Month",' .
|
||||
' YEAR(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND) AS "_Year" ,' .
|
||||
' count(MONTH(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND )) as count_month,' .
|
||||
' count(YEAR(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND)) as count_year' .
|
||||
' FROM tx_news_domain_model_news ' . substr($sql, strpos($sql, 'WHERE '));
|
||||
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable('tx_news_domain_model_news');
|
||||
|
||||
if (TYPO3_MODE === 'FE') {
|
||||
$sql .= $GLOBALS['TSFE']->sys_page->enableFields('tx_news_domain_model_news');
|
||||
} else {
|
||||
$expressionBuilder = $connection
|
||||
->createQueryBuilder()
|
||||
->expr();
|
||||
$sql .= BackendUtility::BEenableFields('tx_news_domain_model_news') .
|
||||
' AND ' . $expressionBuilder->eq('deleted', 0);
|
||||
}
|
||||
|
||||
// group by custom month/year fields
|
||||
$orderDirection = strtolower($demand->getOrder());
|
||||
if ($orderDirection !== 'desc' && $orderDirection !== 'asc') {
|
||||
$orderDirection = 'asc';
|
||||
}
|
||||
$sql .= ' GROUP BY _Month, _Year ORDER BY _Year ' . $orderDirection . ', _Month ' . $orderDirection;
|
||||
|
||||
$res = $connection->query($sql);
|
||||
while ($row = $res->fetch()) {
|
||||
$month = strlen($row['_Month']) === 1 ? ('0' . $row['_Month']) : $row['_Month'];
|
||||
$data['single'][$row['_Year']][$month] = $row['count_month'];
|
||||
}
|
||||
|
||||
// Add totals
|
||||
if (is_array($data['single'])) {
|
||||
foreach ($data['single'] as $year => $months) {
|
||||
$countOfYear = 0;
|
||||
foreach ($months as $month) {
|
||||
$countOfYear += $month;
|
||||
}
|
||||
$data['total'][$year] = $countOfYear;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the search constraints
|
||||
*
|
||||
* @param QueryInterface $query
|
||||
* @param DemandInterface $demand
|
||||
* @return array
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function getSearchConstraints(QueryInterface $query, DemandInterface $demand): array
|
||||
{
|
||||
$constraints = [];
|
||||
if ($demand->getSearch() === null) {
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
/* @var $searchObject \GeorgRinger\News\Domain\Model\Dto\Search */
|
||||
$searchObject = $demand->getSearch();
|
||||
|
||||
$searchSubject = $searchObject->getSubject();
|
||||
if (!empty($searchSubject)) {
|
||||
$queryBuilder = $this->getQueryBuilder('tx_news_domain_model_news');
|
||||
|
||||
$searchFields = GeneralUtility::trimExplode(',', $searchObject->getFields(), true);
|
||||
$searchConstraints = [];
|
||||
|
||||
if (count($searchFields) === 0) {
|
||||
throw new \UnexpectedValueException('No search fields defined', 1318497755);
|
||||
}
|
||||
$searchSubjectSplitted = str_getcsv($searchSubject, ' ');
|
||||
if ($searchObject->isSplitSubjectWords()) {
|
||||
foreach ($searchFields as $field) {
|
||||
$subConstraints = [];
|
||||
foreach ($searchSubjectSplitted as $searchSubjectSplittedPart) {
|
||||
$searchSubjectSplittedPart = trim($searchSubjectSplittedPart);
|
||||
if ($searchSubjectSplittedPart) {
|
||||
$subConstraints[] = $query->like($field, '%' . $searchSubjectSplittedPart . '%');
|
||||
}
|
||||
}
|
||||
$searchConstraints[] = $query->logicalAnd($subConstraints);
|
||||
}
|
||||
if (count($searchConstraints)) {
|
||||
$constraints[] = $query->logicalOr($searchConstraints);
|
||||
}
|
||||
} else {
|
||||
if (!empty($searchSubject)) {
|
||||
foreach ($searchFields as $field) {
|
||||
$searchConstraints[] = $query->like($field, '%' . $searchSubject . '%');
|
||||
}
|
||||
}
|
||||
if (count($searchConstraints)) {
|
||||
$constraints[] = $query->logicalOr($searchConstraints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$minimumDate = strtotime($searchObject->getMinimumDate());
|
||||
if ($minimumDate) {
|
||||
$field = $searchObject->getDateField();
|
||||
if (empty($field)) {
|
||||
throw new \UnexpectedValueException('No date field is defined', 1396348732);
|
||||
}
|
||||
$constraints[] = $query->greaterThanOrEqual($field, $minimumDate);
|
||||
}
|
||||
$maximumDate = strtotime($searchObject->getMaximumDate());
|
||||
if ($maximumDate) {
|
||||
$field = $searchObject->getDateField();
|
||||
if (empty($field)) {
|
||||
throw new \UnexpectedValueException('No date field is defined', 1396348733);
|
||||
}
|
||||
$maximumDate += 86400;
|
||||
$constraints[] = $query->lessThanOrEqual($field, $maximumDate);
|
||||
}
|
||||
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table table name
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
protected function getQueryBuilder(string $table): QueryBuilder
|
||||
{
|
||||
return GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return stripped order sql
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
private function stripOrderBy(string $str): string
|
||||
{
|
||||
/** @noinspection NotOptimalRegularExpressionsInspection */
|
||||
return preg_replace('/(?:ORDER[[:space:]]*BY[[:space:]]*.*)+/i', '', trim($str));
|
||||
}
|
||||
}
|
||||
125
typo3conf/ext/news/Classes/Domain/Repository/TagRepository.php
Normal file
125
typo3conf/ext/news/Classes/Domain/Repository/TagRepository.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Domain\Model\DemandInterface;
|
||||
use GeorgRinger\News\Utility\Validation;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
|
||||
/**
|
||||
* Repository for tag objects
|
||||
*/
|
||||
class TagRepository extends AbstractDemandedRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* Find categories by a given pid
|
||||
*
|
||||
* @param array $idList list of id s
|
||||
* @param array $ordering ordering
|
||||
* @param string $startingPoint starting point uid or comma separated list
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
|
||||
*/
|
||||
public function findByIdList(array $idList, array $ordering = [], $startingPoint = null)
|
||||
{
|
||||
if (empty($idList)) {
|
||||
throw new \InvalidArgumentException('The given id list is empty.', 1484823596);
|
||||
}
|
||||
$query = $this->createQuery();
|
||||
$query->getQuerySettings()->setRespectStoragePage(false);
|
||||
$query->getQuerySettings()->setRespectSysLanguage(false);
|
||||
|
||||
if (count($ordering) > 0) {
|
||||
$query->setOrderings($ordering);
|
||||
}
|
||||
|
||||
$conditions = [];
|
||||
$conditions[] = $query->in('uid', $idList);
|
||||
|
||||
if ($startingPoint !== null) {
|
||||
$conditions[] = $query->in('pid', GeneralUtility::trimExplode(',', $startingPoint, true));
|
||||
}
|
||||
|
||||
return $query->matching(
|
||||
$query->logicalAnd(
|
||||
$conditions
|
||||
)
|
||||
)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of constraints created from a given demand object.
|
||||
*
|
||||
* @param QueryInterface $query
|
||||
* @param DemandInterface $demand
|
||||
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface[]
|
||||
*/
|
||||
protected function createConstraintsFromDemand(QueryInterface $query, DemandInterface $demand): array
|
||||
{
|
||||
$constraints = [];
|
||||
|
||||
// Storage page
|
||||
if ($demand->getStoragePage()) {
|
||||
$pidList = GeneralUtility::intExplode(',', $demand->getStoragePage(), true);
|
||||
$constraints[] = $query->in('pid', $pidList);
|
||||
}
|
||||
|
||||
// Tags
|
||||
if ($demand->getTags()) {
|
||||
$tagList = GeneralUtility::intExplode(',', $demand->getTags(), true);
|
||||
$constraints[] = $query->in('uid', $tagList);
|
||||
}
|
||||
|
||||
// Clean not used constraints
|
||||
foreach ($constraints as $key => $value) {
|
||||
if (is_null($value)) {
|
||||
unset($constraints[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of orderings created from a given demand object.
|
||||
*
|
||||
* @param DemandInterface $demand
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
protected function createOrderingsFromDemand(DemandInterface $demand): array
|
||||
{
|
||||
$orderings = [];
|
||||
|
||||
if (Validation::isValidOrdering($demand->getOrder(), $demand->getOrderByAllowed())) {
|
||||
$orderList = GeneralUtility::trimExplode(',', $demand->getOrder(), true);
|
||||
|
||||
if (!empty($orderList)) {
|
||||
// go through every order statement
|
||||
foreach ($orderList as $orderItem) {
|
||||
list($orderField, $ascDesc) = GeneralUtility::trimExplode(' ', $orderItem, true);
|
||||
// count == 1 means that no direction is given
|
||||
if ($ascDesc) {
|
||||
$orderings[$orderField] = ((strtolower($ascDesc) == 'desc') ?
|
||||
QueryInterface::ORDER_DESCENDING :
|
||||
QueryInterface::ORDER_ASCENDING);
|
||||
} else {
|
||||
$orderings[$orderField] = QueryInterface::ORDER_ASCENDING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $orderings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Repository for tt_content objects
|
||||
*/
|
||||
class TtContentRepository extends Repository
|
||||
{
|
||||
protected $objectType = '\GeorgRinger\News\Domain\Model\Ttcontent';
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Service;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use GeorgRinger\News\Domain\Repository\CategoryRepository;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\Folder;
|
||||
use TYPO3\CMS\Core\Resource\Index\FileIndexRepository;
|
||||
use TYPO3\CMS\Core\Resource\ProcessedFile;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Resource\ResourceStorage;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
class AbstractImportService implements LoggerAwareInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
const UPLOAD_PATH = 'uploads/tx_news/';
|
||||
|
||||
/**
|
||||
* @var ObjectManager
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @var PersistenceManager
|
||||
*/
|
||||
protected $persistenceManager;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $postPersistQueue = [];
|
||||
|
||||
/**
|
||||
* @var EmConfiguration
|
||||
*/
|
||||
protected $emSettings;
|
||||
|
||||
/**
|
||||
* @var Folder
|
||||
*/
|
||||
protected $importFolder;
|
||||
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* @var CategoryRepository
|
||||
*/
|
||||
protected $categoryRepository;
|
||||
/**
|
||||
* AbstractImportService constructor.
|
||||
* @param PersistenceManager $persistenceManager
|
||||
* @param EmConfiguration $emSettings
|
||||
* @param ObjectManager $objectManager
|
||||
* @param CategoryRepository $categoryRepository
|
||||
* @param EventDispatcherInterface $eventDispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
PersistenceManager $persistenceManager,
|
||||
ObjectManager $objectManager,
|
||||
CategoryRepository $categoryRepository,
|
||||
EventDispatcherInterface $eventDispatcher
|
||||
) {
|
||||
$this->emSettings = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
$this->persistenceManager = $persistenceManager;
|
||||
$this->objectManager = $objectManager;
|
||||
$this->categoryRepository = $categoryRepository;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares 2 files by using its filesize
|
||||
*
|
||||
* @param string $file1 Absolute path and filename to file1
|
||||
* @param string $file2 Absolute path and filename to file2
|
||||
* @return bool
|
||||
*/
|
||||
protected function filesAreEqual($file1, $file2): bool
|
||||
{
|
||||
return filesize($file1) === filesize($file2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a existing file by its hash
|
||||
*
|
||||
* @param string $hash
|
||||
*
|
||||
* @return File|ProcessedFile|null
|
||||
*/
|
||||
protected function findFileByHash($hash)
|
||||
{
|
||||
$file = null;
|
||||
|
||||
$files = $this->getFileIndexRepository()->findByContentHash($hash);
|
||||
if (count($files)) {
|
||||
foreach ($files as $fileInfo) {
|
||||
if ($fileInfo['storage'] > 0) {
|
||||
$file = $this->getResourceFactory()->getFileObjectByStorageAndIdentifier(
|
||||
$fileInfo['storage'],
|
||||
$fileInfo['identifier']
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get import Folder
|
||||
*
|
||||
* TODO: catch exception when storage/folder does not exist and return readable message to the user
|
||||
*
|
||||
* @return Folder
|
||||
*/
|
||||
protected function getImportFolder(): Folder
|
||||
{
|
||||
if ($this->importFolder === null) {
|
||||
$this->importFolder = $this->getResourceFactory()->getFolderObjectFromCombinedIdentifier($this->emSettings->getStorageUidImporter() . ':' . $this->emSettings->getResourceFolderImporter());
|
||||
}
|
||||
return $this->importFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the FileIndexRepository
|
||||
*
|
||||
* @return FileIndexRepository
|
||||
*/
|
||||
protected function getFileIndexRepository(): FileIndexRepository
|
||||
{
|
||||
return FileIndexRepository::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resource storage
|
||||
*
|
||||
* @return ResourceStorage
|
||||
*/
|
||||
protected function getResourceStorage(): ResourceStorage
|
||||
{
|
||||
return $this->getResourceFactory()->getStorageObject($this->emSettings->getStorageUidImporter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResourceFactory
|
||||
*/
|
||||
protected function getResourceFactory(): ResourceFactory
|
||||
{
|
||||
/** @var ResourceFactory $resourceFactory */
|
||||
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
|
||||
return $resourceFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Service;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Category;
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use GeorgRinger\News\Domain\Model\FileReference;
|
||||
use GeorgRinger\News\Domain\Repository\CategoryRepository;
|
||||
use GeorgRinger\News\Event\CategoryImportPostHydrateEvent;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
class CategoryImportService extends AbstractImportService
|
||||
{
|
||||
const ACTION_SET_PARENT_CATEGORY = 1;
|
||||
const ACTION_CREATE_L10N_CHILDREN_CATEGORY = 2;
|
||||
|
||||
/**
|
||||
* CategoryImportService constructor.
|
||||
* @param PersistenceManager $persistenceManager
|
||||
* @param EmConfiguration $emSettings
|
||||
* @param ObjectManager $objectManager
|
||||
* @param CategoryRepository $categoryRepository
|
||||
* @param EventDispatcherInterface $eventDispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
PersistenceManager $persistenceManager,
|
||||
ObjectManager $objectManager,
|
||||
CategoryRepository $categoryRepository,
|
||||
EventDispatcherInterface $eventDispatcher
|
||||
) {
|
||||
parent::__construct($persistenceManager, $objectManager, $categoryRepository, $eventDispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $importArray
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function import(array $importArray): void
|
||||
{
|
||||
$this->logger->info(sprintf('Starting import for %s categories', count($importArray)));
|
||||
|
||||
// Sort import array to import the default language first
|
||||
foreach ($importArray as $importItem) {
|
||||
$category = $this->hydrateCategory($importItem);
|
||||
|
||||
if (!empty($importItem['title_lang_ol'])) {
|
||||
$this->postPersistQueue[$importItem['import_id']] = [
|
||||
'category' => $category,
|
||||
'importItem' => $importItem,
|
||||
'action' => self::ACTION_CREATE_L10N_CHILDREN_CATEGORY,
|
||||
'titleLanguageOverlay' => $importItem['title_lang_ol']
|
||||
];
|
||||
}
|
||||
|
||||
if ($importItem['parentcategory']) {
|
||||
$this->postPersistQueue[$importItem['import_id']] = [
|
||||
'category' => $category,
|
||||
'action' => self::ACTION_SET_PARENT_CATEGORY,
|
||||
'parentCategoryOriginUid' => $importItem['parentcategory']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->persistenceManager->persistAll();
|
||||
|
||||
foreach ($this->postPersistQueue as $queueItem) {
|
||||
switch ($queueItem['action']) {
|
||||
case self::ACTION_SET_PARENT_CATEGORY:
|
||||
$this->setParentCategory($queueItem);
|
||||
break;
|
||||
case self::ACTION_CREATE_L10N_CHILDREN_CATEGORY:
|
||||
$this->createL10nChildrenCategory($queueItem);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->persistenceManager->persistAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate a category record with the given array
|
||||
*
|
||||
* @param array $importItem
|
||||
*
|
||||
* @return array|object
|
||||
*/
|
||||
protected function hydrateCategory(array $importItem)
|
||||
{
|
||||
$category = $this->categoryRepository->findOneByImportSourceAndImportId(
|
||||
$importItem['import_source'],
|
||||
$importItem['import_id']
|
||||
);
|
||||
|
||||
$this->logger->info(sprintf(
|
||||
'Import of category from source "%s" with id "%s"',
|
||||
$importItem['import_source'],
|
||||
$importItem['import_id']
|
||||
));
|
||||
|
||||
if (is_null($category)) {
|
||||
$this->logger->info('Category is new');
|
||||
|
||||
$category = GeneralUtility::makeInstance(Category::class);
|
||||
$this->categoryRepository->add($category);
|
||||
} else {
|
||||
$this->logger->info(sprintf('Category exists already with id "%s".', $category->getUid()));
|
||||
}
|
||||
|
||||
$category->setPid($importItem['pid']);
|
||||
$category->setHidden($importItem['hidden']);
|
||||
$category->setStarttime($importItem['starttime']);
|
||||
$category->setEndtime($importItem['endtime']);
|
||||
$category->setCrdate($importItem['crdate']);
|
||||
$category->setTstamp($importItem['tstamp']);
|
||||
$category->setTitle($importItem['title']);
|
||||
$category->setDescription($importItem['description']);
|
||||
if (!empty($importItem['image'])) {
|
||||
$this->setFileRelationFromImage($category, $importItem['image']);
|
||||
}
|
||||
$category->setShortcut($importItem['shortcut']);
|
||||
$category->setSinglePid($importItem['single_pid']);
|
||||
|
||||
$category->setImportId($importItem['import_id']);
|
||||
$category->setImportSource($importItem['import_source']);
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(new CategoryImportPostHydrateEvent($this, $importItem, $category));
|
||||
|
||||
return $event->getCategory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add category image when not already present
|
||||
*
|
||||
* @param Category $category
|
||||
* @param $image
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setFileRelationFromImage($category, $image)
|
||||
{
|
||||
|
||||
// get fileObject by given identifier (file UID, combined identifier or path/filename)
|
||||
try {
|
||||
$newImage = $this->getResourceFactory()->retrieveFileOrFolderObject($image);
|
||||
} catch (ResourceDoesNotExistException $exception) {
|
||||
$newImage = false;
|
||||
}
|
||||
|
||||
// only proceed if image is found
|
||||
if (!$newImage instanceof File) {
|
||||
return;
|
||||
}
|
||||
|
||||
// new image found check if this isn't already
|
||||
$existingImages = $category->getImages();
|
||||
if (!is_null($existingImages) && $existingImages->count() !== 0) {
|
||||
/** @var $item FileReference */
|
||||
foreach ($existingImages as $item) {
|
||||
// only check already persisted items
|
||||
if ($item->getFileUid() === (int)$newImage->getUid()
|
||||
||
|
||||
($item->getUid() &&
|
||||
$item->getOriginalResource()->getName() === $newImage->getName() &&
|
||||
$item->getOriginalResource()->getSize() === (int)$newImage->getSize())
|
||||
) {
|
||||
$newImage = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($newImage) {
|
||||
// file not inside a storage then search for existing file or copy the one form storage 0 to the import folder
|
||||
if ($newImage->getStorage()->getUid() === 0) {
|
||||
|
||||
// search DB for same file based on hash (to prevent duplicates)
|
||||
$existingFile = $this->findFileByHash($newImage->getSha1());
|
||||
|
||||
// no exciting file then copy file to import folder
|
||||
if ($existingFile === null) {
|
||||
$newImage = $this->getResourceStorage()->copyFile($newImage, $this->getImportFolder());
|
||||
} else {
|
||||
$newImage = $existingFile;
|
||||
}
|
||||
}
|
||||
|
||||
$fileReference = GeneralUtility::makeInstance(FileReference::class);
|
||||
$fileReference->setFileUid($newImage->getUid());
|
||||
$fileReference->setPid($category->getPid());
|
||||
$category->addImage($fileReference);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parent category
|
||||
*
|
||||
* @param array $queueItem
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setParentCategory(array $queueItem): void
|
||||
{
|
||||
/** @var $category Category */
|
||||
$category = $queueItem['category'];
|
||||
$parentCategoryOriginUid = $queueItem['parentCategoryOriginUid'];
|
||||
|
||||
if (is_null($parentCategory = $this->postPersistQueue[$parentCategoryOriginUid]['category'])) {
|
||||
$parentCategory = $this->categoryRepository->findOneByImportSourceAndImportId(
|
||||
$category->getImportSource(),
|
||||
$parentCategoryOriginUid
|
||||
);
|
||||
}
|
||||
|
||||
if ($parentCategory !== null) {
|
||||
$category->setParentcategory($parentCategory);
|
||||
$this->categoryRepository->update($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create l10n relation
|
||||
*
|
||||
* @param array $queueItem
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createL10nChildrenCategory(array $queueItem): void
|
||||
{
|
||||
/** @var $category Category */
|
||||
$category = $queueItem['category'];
|
||||
$titleLanguageOverlay = GeneralUtility::trimExplode('|', $queueItem['titleLanguageOverlay']);
|
||||
|
||||
foreach ($titleLanguageOverlay as $key => $title) {
|
||||
$sysLanguageUid = $key + 1;
|
||||
|
||||
$importItem = $queueItem['importItem'];
|
||||
$importItem['import_id'] = $importItem['import_id'] . '|L:' . $sysLanguageUid;
|
||||
|
||||
/** @var $l10nChildrenCategory Category */
|
||||
$l10nChildrenCategory = $this->hydrateCategory($importItem);
|
||||
$this->categoryRepository->add($l10nChildrenCategory);
|
||||
|
||||
$l10nChildrenCategory->setTitle($title);
|
||||
$l10nChildrenCategory->setL10nParent((int)$category->getUid());
|
||||
$l10nChildrenCategory->setSysLanguageUid((int)$sysLanguageUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
411
typo3conf/ext/news/Classes/Domain/Service/NewsImportService.php
Normal file
411
typo3conf/ext/news/Classes/Domain/Service/NewsImportService.php
Normal file
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Domain\Service;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use GeorgRinger\News\Domain\Model\FileReference;
|
||||
use GeorgRinger\News\Domain\Model\Link;
|
||||
use GeorgRinger\News\Domain\Model\News;
|
||||
use GeorgRinger\News\Domain\Repository\CategoryRepository;
|
||||
use GeorgRinger\News\Domain\Repository\NewsRepository;
|
||||
use GeorgRinger\News\Domain\Repository\TtContentRepository;
|
||||
use GeorgRinger\News\Event\NewsImportPostHydrateEvent;
|
||||
use GeorgRinger\News\Event\NewsImportPreHydrateEvent;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
class NewsImportService extends AbstractImportService
|
||||
{
|
||||
const ACTION_IMPORT_L10N_OVERLAY = 1;
|
||||
|
||||
/**
|
||||
* @var NewsRepository
|
||||
*/
|
||||
protected $newsRepository;
|
||||
|
||||
/**
|
||||
* @var TtContentRepository
|
||||
*/
|
||||
protected $ttContentRepository;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = [];
|
||||
|
||||
/**
|
||||
* NewsImportService constructor.
|
||||
* @param PersistenceManager $persistenceManager
|
||||
* @param EmConfiguration $emSettings
|
||||
* @param ObjectManager $objectManager
|
||||
* @param CategoryRepository $categoryRepository
|
||||
* @param EventDispatcherInterface $eventDispatcher
|
||||
* @param NewsRepository $newsRepository
|
||||
* @param TtContentRepository $ttContentRepository
|
||||
*/
|
||||
public function __construct(
|
||||
PersistenceManager $persistenceManager,
|
||||
ObjectManager $objectManager,
|
||||
CategoryRepository $categoryRepository,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
NewsRepository $newsRepository,
|
||||
TtContentRepository $ttContentRepository
|
||||
) {
|
||||
parent::__construct($persistenceManager, $objectManager, $categoryRepository, $eventDispatcher);
|
||||
$this->newsRepository = $newsRepository;
|
||||
$this->ttContentRepository = $ttContentRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $importItem
|
||||
*
|
||||
* @return array|object
|
||||
*/
|
||||
protected function initializeNewsRecord(array $importItem)
|
||||
{
|
||||
$news = null;
|
||||
|
||||
$this->logger->info(sprintf(
|
||||
'Import of news from source "%s" with id "%s"',
|
||||
$importItem['import_source'],
|
||||
$importItem['import_id']
|
||||
));
|
||||
|
||||
if ($importItem['import_source'] && $importItem['import_id']) {
|
||||
$news = $this->newsRepository->findOneByImportSourceAndImportId(
|
||||
$importItem['import_source'],
|
||||
$importItem['import_id']
|
||||
);
|
||||
}
|
||||
|
||||
if ($news === null) {
|
||||
$news = GeneralUtility::makeInstance(News::class);
|
||||
$this->newsRepository->add($news);
|
||||
} else {
|
||||
$this->logger->info(sprintf('News exists already with id "%s".', $news->getUid()));
|
||||
$this->newsRepository->update($news);
|
||||
}
|
||||
|
||||
return $news;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param News $news
|
||||
* @param array $importItem
|
||||
* @param array $importItemOverwrite
|
||||
* @return News
|
||||
*/
|
||||
protected function hydrateNewsRecord(
|
||||
News $news,
|
||||
array $importItem,
|
||||
array $importItemOverwrite
|
||||
): News {
|
||||
if (!empty($importItemOverwrite)) {
|
||||
$importItem = array_merge($importItem, $importItemOverwrite);
|
||||
}
|
||||
$news->setPid($importItem['pid']);
|
||||
$news->setHidden($importItem['hidden']);
|
||||
if ($importItem['starttime']) {
|
||||
$news->setStarttime($importItem['starttime']);
|
||||
}
|
||||
if ($importItem['endtime']) {
|
||||
$news->setStarttime($importItem['endtime']);
|
||||
}
|
||||
if (!empty($importItem['fe_group'])) {
|
||||
$news->setFeGroup((string)$importItem['fe_group']);
|
||||
}
|
||||
$news->setTstamp($importItem['tstamp']);
|
||||
$news->setCrdate($importItem['crdate']);
|
||||
$news->setSysLanguageUid($importItem['sys_language_uid']);
|
||||
$news->setSorting((int)$importItem['sorting']);
|
||||
|
||||
$news->setTitle($importItem['title']);
|
||||
$news->setTeaser($importItem['teaser']);
|
||||
$news->setBodytext($importItem['bodytext']);
|
||||
|
||||
$news->setType((string)$importItem['type']);
|
||||
$news->setKeywords($importItem['keywords']);
|
||||
$news->setDescription($importItem['description']);
|
||||
$news->setDatetime(new \DateTime(date('Y-m-d H:i:sP', $importItem['datetime'])));
|
||||
$news->setArchive(new \DateTime(date('Y-m-d H:i:sP', $importItem['archive'])));
|
||||
|
||||
$contentElementUidArray = GeneralUtility::trimExplode(',', $importItem['content_elements'], true);
|
||||
foreach ($contentElementUidArray as $contentElementUid) {
|
||||
if (is_object($contentElement = $this->ttContentRepository->findByUid($contentElementUid))) {
|
||||
$news->addContentElement($contentElement);
|
||||
}
|
||||
}
|
||||
|
||||
$news->setInternalurl($importItem['internalurl']);
|
||||
$news->setExternalurl($importItem['externalurl']);
|
||||
|
||||
$news->setAuthor($importItem['author']);
|
||||
$news->setAuthorEmail($importItem['author_email']);
|
||||
|
||||
$news->setImportId($importItem['import_id']);
|
||||
$news->setImportSource($importItem['import_source']);
|
||||
|
||||
$news->setPathSegment($importItem['path_segment']);
|
||||
|
||||
if (is_array($importItem['categories'])) {
|
||||
foreach ($importItem['categories'] as $categoryUid) {
|
||||
if ($this->settings['findCategoriesByImportSource']) {
|
||||
$category = $this->categoryRepository->findOneByImportSourceAndImportId(
|
||||
$this->settings['findCategoriesByImportSource'],
|
||||
$categoryUid
|
||||
);
|
||||
} else {
|
||||
$category = $this->categoryRepository->findByUid($categoryUid);
|
||||
}
|
||||
|
||||
if ($category) {
|
||||
$news->addCategory($category);
|
||||
} else {
|
||||
$this->logger->warning(sprintf('Category with ID "%s" was not found', $categoryUid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// media relation
|
||||
if (is_array($importItem['media'])) {
|
||||
foreach ($importItem['media'] as $mediaItem) {
|
||||
// get fileobject by given identifier (file UID, combined identifier or path/filename)
|
||||
try {
|
||||
$file = $this->getResourceFactory()->retrieveFileOrFolderObject($mediaItem['image']);
|
||||
} catch (ResourceDoesNotExistException $exception) {
|
||||
$file = null;
|
||||
}
|
||||
|
||||
// no file found skip processing of this item
|
||||
if ($file === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// file not inside a storage then search for same file based on hash (to prevent duplicates)
|
||||
if ($file->getStorage()->getUid() === 0) {
|
||||
$existingFile = $this->findFileByHash($file->getSha1());
|
||||
if ($existingFile !== null) {
|
||||
$file = $existingFile;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var $media FileReference */
|
||||
if (!$media = $this->getIfFalRelationIfAlreadyExists($news->getFalMedia(), $file)) {
|
||||
|
||||
// file not inside a storage copy the one form storage 0 to the import folder
|
||||
if ($file->getStorage()->getUid() === 0) {
|
||||
$file = $this->getResourceStorage()->copyFile($file, $this->getImportFolder());
|
||||
}
|
||||
|
||||
$media = GeneralUtility::makeInstance(FileReference::class);
|
||||
$media->setFileUid($file->getUid());
|
||||
$news->addFalMedia($media);
|
||||
}
|
||||
|
||||
if ($media) {
|
||||
$media->setTitle($mediaItem['title']);
|
||||
$media->setAlternative($mediaItem['alt']);
|
||||
$media->setDescription($mediaItem['caption']);
|
||||
$media->setShowinpreview($mediaItem['showinpreview']);
|
||||
$media->setPid($importItem['pid']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// related files
|
||||
if (is_array($importItem['related_files'])) {
|
||||
foreach ($importItem['related_files'] as $fileItem) {
|
||||
|
||||
// get fileObject by given identifier (file UID, combined identifier or path/filename)
|
||||
try {
|
||||
$file = $this->getResourceFactory()->retrieveFileOrFolderObject($fileItem['file']);
|
||||
} catch (ResourceDoesNotExistException $exception) {
|
||||
$file = null;
|
||||
}
|
||||
|
||||
// no file found skip processing of this item
|
||||
if ($file === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// file not inside a storage then search for same file based on hash (to prevent duplicates)
|
||||
if ($file->getStorage()->getUid() === 0) {
|
||||
$existingFile = $this->findFileByHash($file->getSha1());
|
||||
if ($existingFile !== null) {
|
||||
$file = $existingFile;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var $relatedFile FileReference */
|
||||
if (!$relatedFile = $this->getIfFalRelationIfAlreadyExists($news->getFalRelatedFiles(), $file)) {
|
||||
|
||||
// file not inside a storage copy the one form storage 0 to the import folder
|
||||
if ($file->getStorage()->getUid() === 0) {
|
||||
$file = $this->getResourceStorage()->copyFile($file, $this->getImportFolder());
|
||||
}
|
||||
|
||||
$relatedFile = GeneralUtility::makeInstance(FileReference::class);
|
||||
$relatedFile->setFileUid($file->getUid());
|
||||
$news->addFalRelatedFile($relatedFile);
|
||||
}
|
||||
|
||||
if ($relatedFile) {
|
||||
$relatedFile->setTitle($fileItem['title']);
|
||||
$relatedFile->setDescription($fileItem['description']);
|
||||
$relatedFile->setPid($importItem['pid']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($importItem['related_links'])) {
|
||||
foreach ($importItem['related_links'] as $link) {
|
||||
/** @var $relatedLink Link */
|
||||
if (($relatedLink = $this->getRelatedLinkIfAlreadyExists($news, $link['uri'])) === false) {
|
||||
$relatedLink = GeneralUtility::makeInstance(Link::class);
|
||||
$relatedLink->setUri($link['uri']);
|
||||
$news->addRelatedLink($relatedLink);
|
||||
}
|
||||
$relatedLink->setTitle($link['title']);
|
||||
$relatedLink->setDescription($link['description']);
|
||||
$relatedLink->setPid($importItem['pid']);
|
||||
}
|
||||
}
|
||||
$event = $this->eventDispatcher->dispatch(new NewsImportPostHydrateEvent($this, $importItem, $news));
|
||||
|
||||
return $event->getNews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import
|
||||
*
|
||||
* @param array $importData
|
||||
* @param array $importItemOverwrite
|
||||
* @param array $settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function import(array $importData, array $importItemOverwrite = [], $settings = []): void
|
||||
{
|
||||
$this->settings = $settings;
|
||||
$this->logger->info(sprintf('Starting import for %s news', count($importData)));
|
||||
|
||||
foreach ($importData as $importItem) {
|
||||
$event = $this->eventDispatcher->dispatch(new NewsImportPreHydrateEvent($this, $importItem));
|
||||
$importItem = $event->getImportItem();
|
||||
|
||||
// Store language overlay in post persist queue
|
||||
if ((int)$importItem['sys_language_uid'] > 0 && (string)$importItem['l10n_parent'] !== '0') {
|
||||
$this->postPersistQueue[$importItem['import_id']] = [
|
||||
'action' => self::ACTION_IMPORT_L10N_OVERLAY,
|
||||
'category' => null,
|
||||
'importItem' => $importItem
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$news = $this->initializeNewsRecord($importItem);
|
||||
|
||||
$this->hydrateNewsRecord($news, $importItem, $importItemOverwrite);
|
||||
}
|
||||
|
||||
$this->persistenceManager->persistAll();
|
||||
|
||||
foreach ($this->postPersistQueue as $queueItem) {
|
||||
if ($queueItem['action'] == self::ACTION_IMPORT_L10N_OVERLAY) {
|
||||
$this->importL10nOverlay($queueItem, $importItemOverwrite);
|
||||
}
|
||||
}
|
||||
|
||||
$this->persistenceManager->persistAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $queueItem
|
||||
* @param array $importItemOverwrite
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function importL10nOverlay(array $queueItem, array $importItemOverwrite): void
|
||||
{
|
||||
$importItem = $queueItem['importItem'];
|
||||
$parentNews = $this->newsRepository->findOneByImportSourceAndImportId(
|
||||
$importItem['import_source'],
|
||||
$importItem['l10n_parent'],
|
||||
true
|
||||
);
|
||||
|
||||
if (!empty($parentNews)) {
|
||||
$news = $this->initializeNewsRecord($importItem);
|
||||
|
||||
$this->hydrateNewsRecord($news, $importItem, $importItemOverwrite);
|
||||
|
||||
$news->setSysLanguageUid($importItem['sys_language_uid']);
|
||||
$news->setL10nParent($parentNews['uid']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing items from the references that matches the file
|
||||
*
|
||||
* @param ObjectStorage $items
|
||||
* @param \TYPO3\CMS\Core\Resource\File $file
|
||||
*
|
||||
* @return bool|FileReference
|
||||
*/
|
||||
protected function getIfFalRelationIfAlreadyExists(
|
||||
ObjectStorage $items,
|
||||
File $file
|
||||
) {
|
||||
$result = false;
|
||||
if ($items->count() !== 0) {
|
||||
/** @var $item FileReference */
|
||||
foreach ($items as $item) {
|
||||
// only check already persisted items
|
||||
if ($item->getFileUid() === (int)$file->getUid()
|
||||
||
|
||||
($item->getUid() &&
|
||||
$item->getOriginalResource()->getName() === $file->getName() &&
|
||||
$item->getOriginalResource()->getSize() === (int)$file->getSize())
|
||||
) {
|
||||
$result = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing related link object
|
||||
*
|
||||
* @param News $news
|
||||
* @param string $uri
|
||||
* @return bool|Link
|
||||
*/
|
||||
protected function getRelatedLinkIfAlreadyExists(News $news, $uri)
|
||||
{
|
||||
$result = false;
|
||||
$links = $news->getRelatedLinks();
|
||||
|
||||
if (!empty($links) && $links->count() !== 0) {
|
||||
foreach ($links as $link) {
|
||||
if ($link->getUri() === $uri) {
|
||||
$result = $link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\AdministrationController;
|
||||
use TYPO3\CMS\Backend\Template\Components\Menu\Menu;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class AdministrationExtendMenuEvent
|
||||
{
|
||||
/**
|
||||
* @var AdministrationController
|
||||
*/
|
||||
private $administrationController;
|
||||
|
||||
/**
|
||||
* @var Menu
|
||||
*/
|
||||
private $menu;
|
||||
|
||||
public function __construct(AdministrationController $administrationController, Menu $menu)
|
||||
{
|
||||
$this->administrationController = $administrationController;
|
||||
$this->menu = $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the administration controller
|
||||
*/
|
||||
public function getAdministrationController(): AdministrationController
|
||||
{
|
||||
return $this->administrationController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the administration controller
|
||||
*/
|
||||
public function setAdministrationController(AdministrationController $administrationController): self
|
||||
{
|
||||
$this->administrationController = $administrationController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the menu
|
||||
*/
|
||||
public function getMenu(): Menu
|
||||
{
|
||||
return $this->menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the menu
|
||||
*/
|
||||
public function setMenu(Menu $menu): self
|
||||
{
|
||||
$this->menu = $menu;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\AdministrationController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class AdministrationIndexActionEvent
|
||||
{
|
||||
/**
|
||||
* @var AdministrationController
|
||||
*/
|
||||
private $administrationController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(AdministrationController $administrationController, array $assignedValues)
|
||||
{
|
||||
$this->administrationController = $administrationController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the administration controller
|
||||
*/
|
||||
public function getAdministrationController(): AdministrationController
|
||||
{
|
||||
return $this->administrationController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the administration controller
|
||||
*/
|
||||
public function setAdministrationController(AdministrationController $administrationController): self
|
||||
{
|
||||
$this->administrationController = $administrationController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\AdministrationController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class AdministrationNewsPidListingActionEvent
|
||||
{
|
||||
/**
|
||||
* @var AdministrationController
|
||||
*/
|
||||
private $administrationController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $rawTree;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $treeLevel;
|
||||
|
||||
public function __construct(AdministrationController $administrationController, array $rawTree, int $treeLevel)
|
||||
{
|
||||
$this->administrationController = $administrationController;
|
||||
$this->rawTree = $rawTree;
|
||||
$this->treeLevel = $treeLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the administration controller
|
||||
*/
|
||||
public function getAdministrationController(): AdministrationController
|
||||
{
|
||||
return $this->administrationController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the administration controller
|
||||
*/
|
||||
public function setAdministrationController(AdministrationController $administrationController): self
|
||||
{
|
||||
$this->administrationController = $administrationController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rawTree
|
||||
*/
|
||||
public function getRawTree(): array
|
||||
{
|
||||
return $this->rawTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rawTree
|
||||
*/
|
||||
public function setRawTree(array $rawTree): self
|
||||
{
|
||||
$this->rawTree = $rawTree;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the treeLevel
|
||||
*/
|
||||
public function getTreeLevel(): int
|
||||
{
|
||||
return $this->treeLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the treeLevel
|
||||
*/
|
||||
public function setTreeLevel(int $treeLevel): self
|
||||
{
|
||||
$this->treeLevel = $treeLevel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Category;
|
||||
use GeorgRinger\News\Domain\Service\CategoryImportService;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class CategoryImportPostHydrateEvent
|
||||
{
|
||||
/**
|
||||
* @var CategoryImportService
|
||||
*/
|
||||
private $categoryImportService;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $importItem;
|
||||
|
||||
/**
|
||||
* @var Category
|
||||
*/
|
||||
private $category;
|
||||
|
||||
public function __construct(CategoryImportService $categoryImportService, array $importItem, Category $category)
|
||||
{
|
||||
$this->categoryImportService = $categoryImportService;
|
||||
$this->importItem = $importItem;
|
||||
$this->category = $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the importer service
|
||||
*/
|
||||
public function getCategoryImportService(): CategoryImportService
|
||||
{
|
||||
return $this->categoryImportService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the importer Service
|
||||
*/
|
||||
public function setCategoryImportService(CategoryImportService $categoryImportService): self
|
||||
{
|
||||
$this->categoryImportService = $categoryImportService;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the importItem
|
||||
*/
|
||||
public function getImportItem(): array
|
||||
{
|
||||
return $this->importItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the importItem
|
||||
*/
|
||||
public function setImportItem(array $importItem): self
|
||||
{
|
||||
$this->importItem = $importItem;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category
|
||||
*/
|
||||
public function getCategory(): Category
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the category
|
||||
*/
|
||||
public function setCategory(Category $category): self
|
||||
{
|
||||
$this->category = $category;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
66
typo3conf/ext/news/Classes/Event/CategoryListActionEvent.php
Normal file
66
typo3conf/ext/news/Classes/Event/CategoryListActionEvent.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\CategoryController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class CategoryListActionEvent
|
||||
{
|
||||
/**
|
||||
* @var CategoryController
|
||||
*/
|
||||
private $categoryController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(CategoryController $categoryController, array $assignedValues)
|
||||
{
|
||||
$this->categoryController = $categoryController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category controller
|
||||
*/
|
||||
public function getCategoryController(): CategoryController
|
||||
{
|
||||
return $this->categoryController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the category controller
|
||||
*/
|
||||
public function setCategoryController(CategoryController $categoryController): self
|
||||
{
|
||||
$this->categoryController = $categoryController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\NewsController;
|
||||
use GeorgRinger\News\Domain\Model\News;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsCheckPidOfNewsRecordFailedInDetailActionEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsController
|
||||
*/
|
||||
private $newsController;
|
||||
|
||||
/**
|
||||
* @var News
|
||||
*/
|
||||
private $news;
|
||||
|
||||
public function __construct(NewsController $newsController, News $news)
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
$this->news = $news;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news controller
|
||||
*/
|
||||
public function getNewsController(): NewsController
|
||||
{
|
||||
return $this->newsController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news controller
|
||||
*/
|
||||
public function setNewsController(NewsController $newsController): self
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news
|
||||
*/
|
||||
public function getNews(): News
|
||||
{
|
||||
return $this->news;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news
|
||||
*/
|
||||
public function setNews(News $news): self
|
||||
{
|
||||
$this->news = $news;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
66
typo3conf/ext/news/Classes/Event/NewsDateMenuActionEvent.php
Normal file
66
typo3conf/ext/news/Classes/Event/NewsDateMenuActionEvent.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\NewsController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsDateMenuActionEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsController
|
||||
*/
|
||||
private $newsController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(NewsController $newsController, array $assignedValues)
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news controller
|
||||
*/
|
||||
public function getNewsController(): NewsController
|
||||
{
|
||||
return $this->newsController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news controller
|
||||
*/
|
||||
public function setNewsController(NewsController $newsController): self
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
66
typo3conf/ext/news/Classes/Event/NewsDetailActionEvent.php
Normal file
66
typo3conf/ext/news/Classes/Event/NewsDetailActionEvent.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\NewsController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsDetailActionEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsController
|
||||
*/
|
||||
private $newsController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(NewsController $newsController, array $assignedValues)
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news controller
|
||||
*/
|
||||
public function getNewsController(): NewsController
|
||||
{
|
||||
return $this->newsController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news controller
|
||||
*/
|
||||
public function setNewsController(NewsController $newsController): self
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\News;
|
||||
use GeorgRinger\News\Domain\Service\NewsImportService;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsImportPostHydrateEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsImportService
|
||||
*/
|
||||
private $newsImportService;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $importItem;
|
||||
|
||||
/**
|
||||
* @var News
|
||||
*/
|
||||
private $news;
|
||||
|
||||
public function __construct(NewsImportService $newsImportService, array $importItem, News $news)
|
||||
{
|
||||
$this->newsImportService = $newsImportService;
|
||||
$this->importItem = $importItem;
|
||||
$this->news = $news;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the importer service
|
||||
*/
|
||||
public function getNewsImportService(): NewsImportService
|
||||
{
|
||||
return $this->newsImportService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the importer service
|
||||
*/
|
||||
public function setNewsImportService(NewsImportService $newsImportService): self
|
||||
{
|
||||
$this->newsImportService = $newsImportService;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the importItem
|
||||
*/
|
||||
public function getImportItem(): array
|
||||
{
|
||||
return $this->importItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the importItem
|
||||
*/
|
||||
public function setImportItem(array $importItem): self
|
||||
{
|
||||
$this->importItem = $importItem;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news
|
||||
*/
|
||||
public function getNews(): News
|
||||
{
|
||||
return $this->news;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news
|
||||
*/
|
||||
public function setNews(News $news): self
|
||||
{
|
||||
$this->news = $news;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Domain\Service\NewsImportService;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsImportPreHydrateEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsImportService
|
||||
*/
|
||||
private $newsImportService;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $importItem;
|
||||
|
||||
public function __construct(NewsImportService $newsImportService, array $importItem)
|
||||
{
|
||||
$this->newsImportService = $newsImportService;
|
||||
$this->importItem = $importItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the importer service
|
||||
*/
|
||||
public function getNewsImportService(): NewsImportService
|
||||
{
|
||||
return $this->newsImportService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the importer service
|
||||
*/
|
||||
public function setNewsImportService(NewsImportService $newsImportService): self
|
||||
{
|
||||
$this->newsImportService = $newsImportService;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the importItem
|
||||
*/
|
||||
public function getImportItem(): array
|
||||
{
|
||||
return $this->importItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the importItem
|
||||
*/
|
||||
public function setImportItem(array $importItem): self
|
||||
{
|
||||
$this->importItem = $importItem;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
66
typo3conf/ext/news/Classes/Event/NewsListActionEvent.php
Normal file
66
typo3conf/ext/news/Classes/Event/NewsListActionEvent.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\NewsController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsListActionEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsController
|
||||
*/
|
||||
private $newsController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(NewsController $newsController, array $assignedValues)
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news controller
|
||||
*/
|
||||
public function getNewsController(): NewsController
|
||||
{
|
||||
return $this->newsController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news controller
|
||||
*/
|
||||
public function setNewsController(NewsController $newsController): self
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\NewsController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsListSelectedActionEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsController
|
||||
*/
|
||||
private $newsController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(NewsController $newsController, array $assignedValues)
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news controller
|
||||
*/
|
||||
public function getNewsController(): NewsController
|
||||
{
|
||||
return $this->newsController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news controller
|
||||
*/
|
||||
public function setNewsController(NewsController $newsController): self
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\NewsController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsSearchFormActionEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsController
|
||||
*/
|
||||
private $newsController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(NewsController $newsController, array $assignedValues)
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news controller
|
||||
*/
|
||||
public function getNewsController(): NewsController
|
||||
{
|
||||
return $this->newsController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news controller
|
||||
*/
|
||||
public function setNewsController(NewsController $newsController): self
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\NewsController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class NewsSearchResultActionEvent
|
||||
{
|
||||
/**
|
||||
* @var NewsController
|
||||
*/
|
||||
private $newsController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(NewsController $newsController, array $assignedValues)
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the news controller
|
||||
*/
|
||||
public function getNewsController(): NewsController
|
||||
{
|
||||
return $this->newsController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the news controller
|
||||
*/
|
||||
public function setNewsController(NewsController $newsController): self
|
||||
{
|
||||
$this->newsController = $newsController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
66
typo3conf/ext/news/Classes/Event/TagListActionEvent.php
Normal file
66
typo3conf/ext/news/Classes/Event/TagListActionEvent.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Event;
|
||||
|
||||
use GeorgRinger\News\Controller\TagController;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
final class TagListActionEvent
|
||||
{
|
||||
/**
|
||||
* @var TagController
|
||||
*/
|
||||
private $tagController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $assignedValues;
|
||||
|
||||
public function __construct(TagController $tagController, array $assignedValues)
|
||||
{
|
||||
$this->tagController = $tagController;
|
||||
$this->assignedValues = $assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag controller
|
||||
*/
|
||||
public function getTagController(): TagController
|
||||
{
|
||||
return $this->tagController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tag controller
|
||||
*/
|
||||
public function setTagController(TagController $tagController): self
|
||||
{
|
||||
$this->tagController = $tagController;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedValues
|
||||
*/
|
||||
public function getAssignedValues(): array
|
||||
{
|
||||
return $this->assignedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assignedValues
|
||||
*/
|
||||
public function setAssignedValues(array $assignedValues): self
|
||||
{
|
||||
$this->assignedValues = $assignedValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks\Backend;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Hook into PageLayoutView to hide tt_content elements in page view
|
||||
*/
|
||||
class PageViewQueryHook
|
||||
{
|
||||
protected static $count = 0;
|
||||
|
||||
/**
|
||||
* Prevent inline tt_content elements in news articles from
|
||||
* showing in the page module.
|
||||
*
|
||||
* @param array $parameters
|
||||
* @param string $table
|
||||
* @param int $pageId
|
||||
* @param array $additionalConstraints
|
||||
* @param string[] $fieldList
|
||||
* @param QueryBuilder $queryBuilder
|
||||
*/
|
||||
public function modifyQuery(
|
||||
$parameters,
|
||||
$table,
|
||||
$pageId,
|
||||
$additionalConstraints,
|
||||
$fieldList,
|
||||
QueryBuilder $queryBuilder
|
||||
): void {
|
||||
if ($table === 'tt_content' && $pageId > 0) {
|
||||
|
||||
// Get page record base on page uid
|
||||
$pageRecord = BackendUtility::getRecord('pages', $pageId, 'uid', " AND doktype='254' AND module='news'");
|
||||
|
||||
if (is_array($pageRecord)) {
|
||||
$tsConfig = BackendUtility::getPagesTSconfig($pageId);
|
||||
|
||||
if ((int)($tsConfig['tx_news.']['showContentElementsInNewsSysFolder'] ?? 0) === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only hide elements which are inline, allowing for standard
|
||||
// elements to show
|
||||
$queryBuilder->andWhere(
|
||||
$queryBuilder->expr()->eq('tx_news_related_news', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
|
||||
);
|
||||
|
||||
if (self::$count === 0) {
|
||||
$this->addFlashMessage();
|
||||
}
|
||||
|
||||
self::$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render flash message to inform user
|
||||
* that no elements belonging to news articles
|
||||
* are rendered in the page module
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addFlashMessage(): void
|
||||
{
|
||||
$message = GeneralUtility::makeInstance(
|
||||
FlashMessage::class,
|
||||
$this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:hiddenContentElements.description'),
|
||||
'',
|
||||
FlashMessage::INFO
|
||||
);
|
||||
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
|
||||
$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
|
||||
$defaultFlashMessageQueue->enqueue($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
102
typo3conf/ext/news/Classes/Hooks/Backend/RecordListQueryHook.php
Normal file
102
typo3conf/ext/news/Classes/Hooks/Backend/RecordListQueryHook.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks\Backend;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Backend\RecordList\RecordListConstraint;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Hook into DatabaseRecordList to hide tt_content elements in list view
|
||||
*/
|
||||
class RecordListQueryHook
|
||||
{
|
||||
protected static $count = 0;
|
||||
|
||||
/** @var RecordListConstraint */
|
||||
protected $recordListConstraint;
|
||||
|
||||
/**
|
||||
* RecordListQueryHook constructor.
|
||||
* @param RecordListConstraint $recordListConstraint
|
||||
*/
|
||||
public function __construct(
|
||||
RecordListConstraint $recordListConstraint
|
||||
) {
|
||||
$this->recordListConstraint = $recordListConstraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function modifyQuery(
|
||||
array &$parameters,
|
||||
string $table,
|
||||
int $pageId,
|
||||
array $additionalConstraints,
|
||||
array $fieldList,
|
||||
QueryBuilder $queryBuilder
|
||||
) {
|
||||
if ($table === 'tt_content' && $pageId > 0) {
|
||||
$pageRecord = BackendUtility::getRecord('pages', $pageId, 'uid', " AND doktype='254' AND module='news'");
|
||||
if (is_array($pageRecord)) {
|
||||
$tsConfig = BackendUtility::getPagesTSconfig($pageId);
|
||||
if ((int)($tsConfig['tx_news.']['showContentElementsInNewsSysFolder'] ?? 0) === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$queryBuilder->where(...['1=2']);
|
||||
|
||||
if (self::$count === 0) {
|
||||
$this->addFlashMessage();
|
||||
}
|
||||
self::$count++;
|
||||
}
|
||||
} elseif ($table === 'tx_news_domain_model_news' && $this->recordListConstraint->isInAdministrationModule()) {
|
||||
$vars = GeneralUtility::_GET('tx_news_web_newsadministration');
|
||||
if (is_array($vars) && is_array($vars['demand'])) {
|
||||
$vars = $vars['demand'];
|
||||
$this->recordListConstraint->extendQuery($parameters, $vars);
|
||||
if (isset($parameters['orderBy'][0])) {
|
||||
$queryBuilder->orderBy($parameters['orderBy'][0][0], $parameters['orderBy'][0][1]);
|
||||
unset($parameters['orderBy']);
|
||||
}
|
||||
if (!empty($parameters['whereDoctrine'])) {
|
||||
$queryBuilder->andWhere(...$parameters['whereDoctrine']);
|
||||
unset($parameters['where']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function addFlashMessage(): void
|
||||
{
|
||||
$message = GeneralUtility::makeInstance(
|
||||
FlashMessage::class,
|
||||
$this->getLanguageService()->sL('LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:hiddenContentElements.description'),
|
||||
'',
|
||||
FlashMessage::INFO
|
||||
);
|
||||
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
|
||||
$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
|
||||
$defaultFlashMessageQueue->enqueue($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
291
typo3conf/ext/news/Classes/Hooks/BackendUtility.php
Normal file
291
typo3conf/ext/news/Classes/Hooks/BackendUtility.php
Normal file
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Hook into \TYPO3\CMS\Backend\Utility\BackendUtility to change flexform behaviour
|
||||
* depending on action selection
|
||||
*/
|
||||
class BackendUtility
|
||||
{
|
||||
|
||||
/**
|
||||
* Fields which are removed in detail view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $removedFieldsInDetailView = [
|
||||
'sDEF' => 'orderBy,orderDirection,categories,categoryConjunction,includeSubCategories,
|
||||
archiveRestriction,timeRestriction,timeRestrictionHigh,topNewsRestriction,
|
||||
dateField,selectedList',
|
||||
'additional' => 'limit,offset,hidePagination,topNewsFirst,listPid,list.paginate.itemsPerPage',
|
||||
'template' => 'cropMaxCharacters'
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in list view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $removedFieldsInListView = [
|
||||
'sDEF' => 'dateField,singleNews,previewHiddenRecords,selectedList',
|
||||
'additional' => '',
|
||||
'template' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in selected list view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $removedFieldsInSelectedListView = [
|
||||
'sDEF' => 'categories,categoryConjunction,includeSubCategories,
|
||||
archiveRestriction,timeRestriction,timeRestrictionHigh,topNewsRestriction,
|
||||
startingpoint,recursive,dateField,singleNews,previewHiddenRecords,
|
||||
previewHiddenRecords,startingpoint,recursive',
|
||||
'additional' => 'tags,limit,offset,hidePagination,topNewsFirst,backPid,excludeAlreadyDisplayedNews,
|
||||
list.paginate.itemsPerPage,disableOverrideDemand',
|
||||
'template' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in dateMenu view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $removedFieldsInDateMenuView = [
|
||||
'sDEF' => 'orderBy,singleNews,selectedList',
|
||||
'additional' => 'limit,offset,hidePagination,topNewsFirst,backPid,previewHiddenRecords,excludeAlreadyDisplayedNews,
|
||||
list.paginate.itemsPerPage',
|
||||
'template' => 'cropMaxCharacters,media.maxWidth,media.maxHeight'
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in search form view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $removedFieldsInSearchFormView = [
|
||||
'sDEF' => 'orderBy,orderDirection,categories,categoryConjunction,includeSubCategories,
|
||||
archiveRestriction,timeRestriction,timeRestrictionHigh,topNewsRestriction,
|
||||
startingpoint,recursive,dateField,singleNews,previewHiddenRecords,selectedList',
|
||||
'additional' => 'limit,offset,hidePagination,topNewsFirst,detailPid,backPid,excludeAlreadyDisplayedNews,
|
||||
list.paginate.itemsPerPage',
|
||||
'template' => 'cropMaxCharacters,media.maxWidth,media.maxHeight'
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in category list view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $removedFieldsInCategoryListView = [
|
||||
'sDEF' => 'orderBy,orderDirection,categoryConjunction,includeSubCategories,
|
||||
archiveRestriction,timeRestriction,timeRestrictionHigh,topNewsRestriction,
|
||||
recursive,dateField,singleNews,previewHiddenRecords,selectedList',
|
||||
'additional' => 'limit,offset,hidePagination,topNewsFirst,detailPid,backPid,excludeAlreadyDisplayedNews,
|
||||
list.paginate.itemsPerPage',
|
||||
'template' => 'cropMaxCharacters,media.maxWidth,media.maxHeight'
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields which are removed in tag list view
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $removedFieldsInTagListView = [
|
||||
'sDEF' => 'categories,categoryConjunction,includeSubCategories,
|
||||
archiveRestriction,timeRestriction,timeRestrictionHigh,topNewsRestriction,
|
||||
dateField,singleNews,previewHiddenRecords,selectedList',
|
||||
'additional' => 'limit,offset,hidePagination,topNewsFirst,detailPid,backPid,excludeAlreadyDisplayedNews,
|
||||
list.paginate.itemsPerPage',
|
||||
'template' => 'cropMaxCharacters,media.maxWidth,media.maxHeight'
|
||||
];
|
||||
|
||||
/** @var EmConfiguration */
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* BackendUtility constructor.
|
||||
* @param EmConfiguration $configuration
|
||||
*/
|
||||
public function __construct(
|
||||
) {
|
||||
$this->configuration = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $dataStructure
|
||||
* @param array $identifier
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function parseDataStructureByIdentifierPostProcess(array $dataStructure, array $identifier)
|
||||
{
|
||||
if ($identifier['type'] === 'tca' && $identifier['tableName'] === 'tt_content' && $identifier['dataStructureKey'] === 'news_pi1,list') {
|
||||
$getVars = GeneralUtility::_GET('edit');
|
||||
if (isset($getVars['tt_content']) && is_array($getVars['tt_content'])) {
|
||||
$item = array_keys($getVars['tt_content']);
|
||||
$recordId = (int)$item[0];
|
||||
|
||||
if (($getVars['tt_content'][$recordId] ?? '') === 'new') {
|
||||
$fakeRow = [
|
||||
'uid' => 'NEW123'
|
||||
];
|
||||
$this->updateFlexforms($dataStructure, $fakeRow);
|
||||
} else {
|
||||
$row = BackendUtilityCore::getRecord('tt_content', $recordId);
|
||||
if (is_array($row)) {
|
||||
$this->updateFlexforms($dataStructure, $row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $dataStructure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update flexform configuration if a action is selected
|
||||
*
|
||||
* @param array|string &$dataStructure flexform structure
|
||||
* @param array $row row of current record
|
||||
* @param array $dataStructure
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function updateFlexforms(array &$dataStructure, array $row): void
|
||||
{
|
||||
$selectedView = '';
|
||||
$flexformSelection = [];
|
||||
if (isset($row['pi_flexform'])) {
|
||||
// get the first selected action
|
||||
if (is_string($row['pi_flexform'])) {
|
||||
$flexformSelection = GeneralUtility::xml2array($row['pi_flexform']);
|
||||
} else {
|
||||
$flexformSelection = $row['pi_flexform'];
|
||||
}
|
||||
}
|
||||
if (is_array($flexformSelection) && isset($flexformSelection['data'])) {
|
||||
$selectedView = $flexformSelection['data']['sDEF']['lDEF']['switchableControllerActions']['vDEF'] ?? '';
|
||||
if (!empty($selectedView)) {
|
||||
$actionParts = GeneralUtility::trimExplode(';', $selectedView, true);
|
||||
$selectedView = $actionParts[0];
|
||||
}
|
||||
|
||||
// new plugin element
|
||||
} elseif (str_starts_with((string)$row['uid'], 'NEW')) {
|
||||
// use List as starting view
|
||||
$selectedView = 'News->list';
|
||||
}
|
||||
|
||||
if (!empty($selectedView)) {
|
||||
// Modify the flexform structure depending on the first found action
|
||||
switch ($selectedView) {
|
||||
case 'News->list':
|
||||
case 'News->searchResult':
|
||||
$this->deleteFromStructure($dataStructure, $this->removedFieldsInListView);
|
||||
break;
|
||||
case 'News->detail':
|
||||
$this->deleteFromStructure($dataStructure, $this->removedFieldsInDetailView);
|
||||
break;
|
||||
case 'News->selectedList':
|
||||
$this->deleteFromStructure($dataStructure, $this->removedFieldsInSelectedListView);
|
||||
break;
|
||||
case 'News->searchForm':
|
||||
$this->deleteFromStructure($dataStructure, $this->removedFieldsInSearchFormView);
|
||||
break;
|
||||
case 'News->dateMenu':
|
||||
$this->deleteFromStructure($dataStructure, $this->removedFieldsInDateMenuView);
|
||||
break;
|
||||
case 'Category->list':
|
||||
$this->deleteFromStructure($dataStructure, $this->removedFieldsInCategoryListView);
|
||||
break;
|
||||
case 'Tag->list':
|
||||
$this->deleteFromStructure($dataStructure, $this->removedFieldsInTagListView);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Hooks/BackendUtility.php']['updateFlexforms'] ?? []) {
|
||||
$params = [
|
||||
'selectedView' => $selectedView,
|
||||
'dataStructure' => &$dataStructure,
|
||||
];
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Hooks/BackendUtility.php']['updateFlexforms'] as $reference) {
|
||||
GeneralUtility::callUserFunction($reference, $params, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add category restriction to flexforms
|
||||
*
|
||||
* @param array $structure
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addCategoryConstraints(&$structure): void
|
||||
{
|
||||
$categoryRestrictionSetting = $this->configuration->getCategoryRestriction();
|
||||
$categoryRestriction = '';
|
||||
switch ($categoryRestrictionSetting) {
|
||||
case 'current_pid':
|
||||
$categoryRestriction = ' AND sys_category.pid=###CURRENT_PID### ';
|
||||
break;
|
||||
case 'siteroot':
|
||||
$categoryRestriction = ' AND sys_category.pid IN (###SITEROOT###) ';
|
||||
break;
|
||||
case 'page_tsconfig':
|
||||
$categoryRestriction = ' AND sys_category.pid IN (###PAGE_TSCONFIG_IDLIST###) ';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($categoryRestriction) && isset($structure['sheets']['sDEF']['ROOT']['el']['settings.categories'])) {
|
||||
$structure['sheets']['sDEF']['ROOT']['el']['settings.categories']['TCEforms']['config']['foreign_table_where'] = $categoryRestriction . $structure['sheets']['sDEF']['ROOT']['el']['settings.categories']['TCEforms']['config']['foreign_table_where'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove fields from flexform structure
|
||||
*
|
||||
* @param array &$dataStructure flexform structure
|
||||
* @param array $fieldsToBeRemoved fields which need to be removed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function deleteFromStructure(array &$dataStructure, array $fieldsToBeRemoved): void
|
||||
{
|
||||
foreach ($fieldsToBeRemoved as $sheetName => $sheetFields) {
|
||||
$fieldsInSheet = GeneralUtility::trimExplode(',', $sheetFields, true);
|
||||
|
||||
foreach ($fieldsInSheet as $fieldName) {
|
||||
unset($dataStructure['sheets'][$sheetName]['ROOT']['el']['settings.' . $fieldName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pageId
|
||||
* @return bool
|
||||
*/
|
||||
protected function enabledInTsConfig($pageId): bool
|
||||
{
|
||||
$tsConfig = BackendUtilityCore::getPagesTSconfig($pageId);
|
||||
if (isset($tsConfig['tx_news.']['categoryRestrictionForFlexForms'])) {
|
||||
return (bool)$tsConfig['tx_news.']['categoryRestrictionForFlexForms'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
183
typo3conf/ext/news/Classes/Hooks/DataHandler.php
Normal file
183
typo3conf/ext/news/Classes/Hooks/DataHandler.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Service\AccessControlService;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Hook into tcemain which is used to show preview of news item
|
||||
*/
|
||||
class DataHandler implements SingletonInterface
|
||||
{
|
||||
protected $cacheTagsToFlush = [];
|
||||
|
||||
/**
|
||||
* Flushes the cache if a news record was edited.
|
||||
* This happens on two levels: by UID and by PID.
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearCachePostProc(array $params): void
|
||||
{
|
||||
if (isset($params['table']) && $params['table'] === 'tx_news_domain_model_news') {
|
||||
if (isset($params['uid'])) {
|
||||
$this->cacheTagsToFlush[] = 'tx_news_uid_' . $params['uid'];
|
||||
}
|
||||
if (isset($params['uid_page'])) {
|
||||
$this->cacheTagsToFlush[] = 'tx_news_pid_' . $params['uid_page'];
|
||||
}
|
||||
|
||||
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
|
||||
foreach (array_unique($this->cacheTagsToFlush) as $cacheTag) {
|
||||
$cacheManager->flushCachesInGroupByTag('pages', $cacheTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a different preview link *
|
||||
*
|
||||
* @param string $status status
|
||||
* @param string $table table name
|
||||
* @param int $recordUid id of the record
|
||||
* @param array $fields fieldArray
|
||||
* @param \TYPO3\CMS\Core\DataHandling\DataHandler $parentObject parent Object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function processDatamap_afterDatabaseOperations(
|
||||
$status,
|
||||
$table,
|
||||
$recordUid,
|
||||
array $fields,
|
||||
\TYPO3\CMS\Core\DataHandling\DataHandler $parentObject
|
||||
): void {
|
||||
// Clear category cache
|
||||
if ($table === 'sys_category') {
|
||||
$cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('news_category');
|
||||
$cache->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent saving of a news record if the editor doesn't have access to all categories of the news record
|
||||
*
|
||||
* @param array $fieldArray
|
||||
* @param string $table
|
||||
* @param int $id
|
||||
* @param $parentObject \TYPO3\CMS\Core\DataHandling\DataHandler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function processDatamap_preProcessFieldArray(&$fieldArray, $table, $id, $parentObject): void
|
||||
{
|
||||
if ($table === 'tx_news_domain_model_news') {
|
||||
// check permissions of assigned categories
|
||||
if (is_int($id) && !$this->getBackendUser()->isAdmin()) {
|
||||
$newsRecord = BackendUtilityCore::getRecord($table, $id);
|
||||
if (!AccessControlService::userHasCategoryPermissionsForRecord($newsRecord)) {
|
||||
$parentObject->log(
|
||||
$table,
|
||||
$id,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
"processDatamap: Attempt to modify a record from table '%s' without permission. Reason: the record has one or more categories assigned that are not defined in your BE usergroup.",
|
||||
1,
|
||||
[$table]
|
||||
);
|
||||
// unset fieldArray to prevent saving of the record
|
||||
$fieldArray = [];
|
||||
} else {
|
||||
|
||||
// If the category relation has been modified, no | is found anymore
|
||||
if (isset($fieldArray['categories']) && strpos($fieldArray['categories'], '|') === false) {
|
||||
$deniedCategories = AccessControlService::getAccessDeniedCategories($newsRecord);
|
||||
if (is_array($deniedCategories)) {
|
||||
foreach ($deniedCategories as $deniedCategory) {
|
||||
$fieldArray['categories'] .= ',' . $deniedCategory['uid'];
|
||||
}
|
||||
// Check if the categories are not empty,
|
||||
if (!empty($fieldArray['categories'])) {
|
||||
$fieldArray['categories'] = trim($fieldArray['categories'], ',');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent deleting/moving of a news record if the editor doesn't have access to all categories of the news record
|
||||
*
|
||||
* @param string $command
|
||||
* @param string $table
|
||||
* @param int $id
|
||||
* @param string $value
|
||||
* @param $parentObject \TYPO3\CMS\Core\DataHandling\DataHandler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function processCmdmap_preProcess($command, &$table, $id, $value, $parentObject): void
|
||||
{
|
||||
if ($table === 'tx_news_domain_model_news' && !$this->getBackendUser()->isAdmin() && is_int($id) && $command !== 'undelete') {
|
||||
$newsRecord = BackendUtilityCore::getRecord($table, $id);
|
||||
if (is_array($newsRecord) && !AccessControlService::userHasCategoryPermissionsForRecord($newsRecord)) {
|
||||
$parentObject->log(
|
||||
$table,
|
||||
$id,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
'processCmdmap: Attempt to ' . $command . " a record from table '%s' without permission. Reason: the record has one or more categories assigned that are not defined in the BE usergroup.",
|
||||
1,
|
||||
[$table]
|
||||
);
|
||||
// unset table to prevent saving
|
||||
$table = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush cache for old news pid when moving record
|
||||
*
|
||||
* @param string $table
|
||||
* @param int $uid
|
||||
* @param int $destPid
|
||||
* @param array $propArr
|
||||
* @param array $moveRec
|
||||
* @param int $resolvedPid
|
||||
* @param bool $recordWasMoved
|
||||
* @param \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler
|
||||
*/
|
||||
public function moveRecord($table, $uid, $destPid, $propArr, $moveRec, $resolvedPid, $recordWasMoved, \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler)
|
||||
{
|
||||
if ($table === 'tx_news_domain_model_news') {
|
||||
$this->cacheTagsToFlush[] = 'tx_news_pid_' . $moveRec['pid'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current BE user.
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUser(): \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
99
typo3conf/ext/news/Classes/Hooks/InlineElementHook.php
Normal file
99
typo3conf/ext/news/Classes/Hooks/InlineElementHook.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Backend\Form\Element\InlineElement;
|
||||
use TYPO3\CMS\Backend\Form\Element\InlineElementHookInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Inline Element Hook
|
||||
*/
|
||||
class InlineElementHook implements InlineElementHookInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes this hook object.
|
||||
*
|
||||
* @param InlineElement $parentObject
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(&$parentObject): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-processing to define which control items are enabled or disabled.
|
||||
*
|
||||
* @param string $parentUid The uid of the parent (embedding) record (uid or NEW...)
|
||||
* @param string $foreignTable The table (foreign_table) we create control-icons for
|
||||
* @param array $childRecord The current record of that foreign_table
|
||||
* @param array $childConfig TCA configuration of the current field of the child record
|
||||
* @param bool $isVirtual Defines whether the current records is only virtually shown and not physically part of the parent record
|
||||
* @param array &$enabledControls (reference) Associative array with the enabled control items
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function renderForeignRecordHeaderControl_preProcess(
|
||||
$parentUid,
|
||||
$foreignTable,
|
||||
array $childRecord,
|
||||
array $childConfig,
|
||||
$isVirtual,
|
||||
array &$enabledControls
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-processing to define which control items to show. Possibly own icons can be added here.
|
||||
*
|
||||
* @param string $parentUid The uid of the parent (embedding) record (uid or NEW...)
|
||||
* @param string $foreignTable The table (foreign_table) we create control-icons for
|
||||
* @param array $childRecord The current record of that foreign_table
|
||||
* @param array $childConfig TCA configuration of the current field of the child record
|
||||
* @param bool $isVirtual Defines whether the current records is only virtually shown and not physically part of the parent record
|
||||
* @param array &$controlItems (reference) Associative array with the currently available control items
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function renderForeignRecordHeaderControl_postProcess(
|
||||
$parentUid,
|
||||
$foreignTable,
|
||||
array $childRecord,
|
||||
array $childConfig,
|
||||
$isVirtual,
|
||||
array &$controlItems
|
||||
) {
|
||||
$previewSetting = (int)($childRecord['showinpreview'] ?? 0);
|
||||
if ($foreignTable === 'sys_file_reference' && $previewSetting > 0) {
|
||||
$ll = 'LLL:EXT:news/Resources/Private/Language/locallang_db.xlf:';
|
||||
|
||||
$extensionConfiguration = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
|
||||
if ($extensionConfiguration->isAdvancedMediaPreview()) {
|
||||
if ($previewSetting === 1) {
|
||||
$label = $GLOBALS['LANG']->sL($ll . 'tx_news_domain_model_media.showinviews.1');
|
||||
$extraItem = ['showinpreview' => ' <span class="btn btn-default" title="' . htmlspecialchars($label) . '"><i class="fa fa-check"></i></span>'];
|
||||
$controlItems = $extraItem + $controlItems;
|
||||
} elseif ($previewSetting === 2) {
|
||||
$label = $GLOBALS['LANG']->sL($ll . 'tx_news_domain_model_media.showinviews.2');
|
||||
$extraItem = ['showinpreview' => ' <span class="btn btn-default" title="' . htmlspecialchars($label) . '"><i class="fa fa-check-square"></i></span>'];
|
||||
$controlItems = $extraItem + $controlItems;
|
||||
}
|
||||
} elseif ($previewSetting === 1) {
|
||||
$label = $GLOBALS['LANG']->sL($ll . 'tx_news_domain_model_media.showinpreview');
|
||||
$extraItem = ['showinpreview' => ' <span class="btn btn-default" title="' . htmlspecialchars($label) . '"><i class="fa fa-check"></i></span>'];
|
||||
$controlItems = $extraItem + $controlItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
323
typo3conf/ext/news/Classes/Hooks/ItemsProcFunc.php
Normal file
323
typo3conf/ext/news/Classes/Hooks/ItemsProcFunc.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Utility\TemplateLayout;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\Site\SiteFinder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Userfunc to render alternative label for media elements
|
||||
*/
|
||||
class ItemsProcFunc
|
||||
{
|
||||
|
||||
/** @var TemplateLayout $templateLayoutsUtility */
|
||||
protected $templateLayoutsUtility;
|
||||
|
||||
/**
|
||||
* ItemsProcFunc constructor.
|
||||
* @param TemplateLayout $templateLayout
|
||||
*/
|
||||
public function __construct(
|
||||
TemplateLayout $templateLayout
|
||||
) {
|
||||
$this->templateLayoutsUtility = $templateLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Itemsproc function to extend the selection of templateLayouts in the plugin
|
||||
*
|
||||
* @param array &$config configuration array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function user_templateLayout(array &$config): void
|
||||
{
|
||||
$pageId = 0;
|
||||
|
||||
$currentColPos = $config['flexParentDatabaseRow']['colPos'];
|
||||
$pageId = $this->getPageId($config['flexParentDatabaseRow']['pid']);
|
||||
|
||||
if ($pageId > 0) {
|
||||
$templateLayouts = $this->templateLayoutsUtility->getAvailableTemplateLayouts($pageId);
|
||||
|
||||
$templateLayouts = $this->reduceTemplateLayouts($templateLayouts, $currentColPos);
|
||||
foreach ($templateLayouts as $layout) {
|
||||
$additionalLayout = [
|
||||
htmlspecialchars($this->getLanguageService()->sL($layout[0])),
|
||||
$layout[1]
|
||||
];
|
||||
array_push($config['items'], $additionalLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the template layouts by the ones that are not allowed in given colPos
|
||||
*
|
||||
* @param array $templateLayouts
|
||||
* @param int $currentColPos
|
||||
* @return array
|
||||
*/
|
||||
protected function reduceTemplateLayouts($templateLayouts, $currentColPos): array
|
||||
{
|
||||
$currentColPos = (int)$currentColPos;
|
||||
$restrictions = [];
|
||||
$allLayouts = [];
|
||||
foreach ($templateLayouts as $key => $layout) {
|
||||
if (is_array($layout[0])) {
|
||||
if (isset($layout[0]['allowedColPos']) && str_ends_with((string)$layout[1], '.')) {
|
||||
$layoutKey = substr($layout[1], 0, -1);
|
||||
$restrictions[$layoutKey] = GeneralUtility::intExplode(',', $layout[0]['allowedColPos'], true);
|
||||
}
|
||||
} else {
|
||||
$allLayouts[$key] = $layout;
|
||||
}
|
||||
}
|
||||
if (!empty($restrictions)) {
|
||||
foreach ($restrictions as $restrictedIdentifier => $restrictedColPosList) {
|
||||
if (!in_array($currentColPos, $restrictedColPosList, true)) {
|
||||
unset($allLayouts[$restrictedIdentifier]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $allLayouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the select box of orderBy-options as a category menu
|
||||
* needs different ones then a news action
|
||||
*
|
||||
* @param array &$config configuration array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function user_orderBy(array &$config): void
|
||||
{
|
||||
$row = $this->getContentElementRow($config['row']['uid']);
|
||||
|
||||
// check if the record has been saved once
|
||||
if (is_array($row) && !empty($row['pi_flexform'])) {
|
||||
$flexformConfig = GeneralUtility::xml2array($row['pi_flexform']);
|
||||
|
||||
// check if there is a flexform configuration
|
||||
if (isset($flexformConfig['data']['sDEF']['lDEF']['switchableControllerActions']['vDEF'])) {
|
||||
$selectedActionList = $flexformConfig['data']['sDEF']['lDEF']['switchableControllerActions']['vDEF'] ?? '';
|
||||
// check for selected action
|
||||
if (str_starts_with($selectedActionList, 'Category')) {
|
||||
$newItems = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['orderByCategory'];
|
||||
} elseif (str_starts_with($selectedActionList, 'Tag')) {
|
||||
$this->removeNonValidOrderFields($config, 'tx_news_domain_model_tag');
|
||||
$newItems = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['orderByTag'];
|
||||
} else {
|
||||
$newItems = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['orderByNews'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if a override configuration is found
|
||||
if (!empty($newItems)) {
|
||||
// remove default configuration
|
||||
$config['items'] = [];
|
||||
// empty default line
|
||||
array_push($config['items'], ['', '']);
|
||||
|
||||
$newItemArray = GeneralUtility::trimExplode(',', $newItems, true);
|
||||
$languageKey = 'LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:flexforms_general.orderBy.';
|
||||
foreach ($newItemArray as $item) {
|
||||
// label: if empty, key (=field) is used
|
||||
$label = $this->getLanguageService()->sL($languageKey . $item);
|
||||
if (empty($label)) {
|
||||
$label = $item;
|
||||
}
|
||||
array_push($config['items'], [htmlspecialchars($label), $item]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove not valid fields from ordering
|
||||
*
|
||||
* @param array $config tca items
|
||||
* @param string $tableName table name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeNonValidOrderFields(array &$config, $tableName): void
|
||||
{
|
||||
$allowedFields = array_keys($GLOBALS['TCA'][$tableName]['columns']);
|
||||
|
||||
foreach ($config['items'] as $key => $item) {
|
||||
if ($item[1] != '' && !in_array($item[1], $allowedFields)) {
|
||||
unset($config['items'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the selectbox of available actions
|
||||
*
|
||||
* @param array &$config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function user_switchableControllerActions(array &$config): void
|
||||
{
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['switchableControllerActions']['list'])) {
|
||||
$configuration = (int)$GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['switchableControllerActions']['list'];
|
||||
switch ($configuration) {
|
||||
case 1:
|
||||
$this->removeActionFromList($config, 'News->list');
|
||||
break;
|
||||
case 2:
|
||||
$this->removeActionFromList($config, 'News->list;News->detail');
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Add additional actions
|
||||
if (isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['switchableControllerActions']['newItems'])
|
||||
&& is_array($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['switchableControllerActions']['newItems'])
|
||||
) {
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['switchableControllerActions']['newItems'] as $key => $label) {
|
||||
array_push($config['items'], [$this->getLanguageService()->sL($label), $key, '']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove given action from switchableControllerActions
|
||||
*
|
||||
* @param array $config available items
|
||||
* @param string $action action to be removed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function removeActionFromList(array &$config, $action): void
|
||||
{
|
||||
foreach ($config['items'] as $key => $item) {
|
||||
if ($item[1] === $action) {
|
||||
unset($config['items'][$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a select box of languages to choose an overlay
|
||||
*
|
||||
* @return string select box
|
||||
*/
|
||||
public function user_categoryOverlay(): string
|
||||
{
|
||||
$html = '';
|
||||
|
||||
$languages = $this->getAllLanguages();
|
||||
// if any language is available
|
||||
if (count($languages) > 0) {
|
||||
$html = '<select name="data[newsoverlay]" id="field_newsoverlay" class="form-control">';
|
||||
|
||||
if (!isset($languages[0])) {
|
||||
$html .= '<option value="0">' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.default_value')) . '</option>';
|
||||
}
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$selected = ((int)($GLOBALS['BE_USER']->uc['newsoverlay'] ?? 0) === (int)$language['uid']) ? ' selected="selected" ' : '';
|
||||
$html .= '<option ' . $selected . 'value="' . $language['uid'] . '">' . htmlspecialchars($language['title']) . '</option>';
|
||||
}
|
||||
|
||||
$html .= '</select>';
|
||||
} else {
|
||||
$html .= htmlspecialchars(
|
||||
$this->getLanguageService()->sL(
|
||||
'LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:usersettings.no-languages-available'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all languages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAllLanguages(): array
|
||||
{
|
||||
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class);
|
||||
if ($versionInformation->getMajorVersion() === 10) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_language');
|
||||
return $queryBuilder->select('*')
|
||||
->from('sys_language')
|
||||
->orderBy('sorting')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
}
|
||||
$siteLanguages = [];
|
||||
foreach (GeneralUtility::makeInstance(SiteFinder::class)->getAllSites() as $site) {
|
||||
foreach ($site->getAllLanguages() as $languageId => $language) {
|
||||
if (!isset($siteLanguages[$languageId])) {
|
||||
$siteLanguages[$languageId] = [
|
||||
'uid' => $languageId,
|
||||
'title' => $language->getTitle(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $siteLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tt_content record
|
||||
*
|
||||
* @param int $uid
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function getContentElementRow($uid): ?array
|
||||
{
|
||||
return BackendUtilityCore::getRecord('tt_content', $uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page id, if negative, then it is a "after record"
|
||||
*
|
||||
* @param int $pid
|
||||
* @return int
|
||||
*/
|
||||
protected function getPageId($pid): int
|
||||
{
|
||||
$pid = (int)$pid;
|
||||
|
||||
if ($pid > 0) {
|
||||
return $pid;
|
||||
}
|
||||
|
||||
$row = BackendUtilityCore::getRecord('tt_content', abs($pid), 'uid,pid');
|
||||
return $row['pid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns LanguageService
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Localization\LanguageService
|
||||
*/
|
||||
protected function getLanguageService(): \TYPO3\CMS\Core\Localization\LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
46
typo3conf/ext/news/Classes/Hooks/Labels.php
Normal file
46
typo3conf/ext/news/Classes/Hooks/Labels.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Service\CategoryService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Userfunc to get alternative label
|
||||
*/
|
||||
class Labels
|
||||
{
|
||||
|
||||
/**
|
||||
* Generate additional label for category records
|
||||
* including the title of the parent category
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getUserLabelCategory(array &$params): void
|
||||
{
|
||||
$showTranslationInformation = false;
|
||||
|
||||
$getVars = GeneralUtility::_GET();
|
||||
if (isset($getVars['route']) && $getVars['route'] === '/record/edit'
|
||||
&& isset($getVars['edit']) && is_array($getVars['edit'])
|
||||
&& (isset($getVars['edit']['tt_content']) || isset($getVars['edit']['tx_news_domain_model_news']) || isset($getVars['edit']['sys_category']))
|
||||
) {
|
||||
$showTranslationInformation = true;
|
||||
}
|
||||
|
||||
if ($showTranslationInformation && is_array($params['row'])) {
|
||||
$params['title'] = CategoryService::translateCategoryRecord($params['row']['title'], $params['row']);
|
||||
} else {
|
||||
$params['title'] = $params['row']['title'];
|
||||
}
|
||||
}
|
||||
}
|
||||
770
typo3conf/ext/news/Classes/Hooks/PageLayoutView.php
Normal file
770
typo3conf/ext/news/Classes/Hooks/PageLayoutView.php
Normal file
@@ -0,0 +1,770 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Hooks;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Utility\TemplateLayout;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Template\DocumentTemplate;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
|
||||
/**
|
||||
* Hook to display verbose information about pi1 plugin in Web>Page module
|
||||
*/
|
||||
class PageLayoutView
|
||||
{
|
||||
|
||||
/**
|
||||
* Extension key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const KEY = 'news';
|
||||
|
||||
/**
|
||||
* Path to the locallang file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const LLPATH = 'LLL:EXT:news/Resources/Private/Language/locallang_be.xlf:';
|
||||
|
||||
/**
|
||||
* Max shown settings
|
||||
*/
|
||||
const SETTINGS_IN_PREVIEW = 7;
|
||||
|
||||
/**
|
||||
* Table information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tableData = [];
|
||||
|
||||
/**
|
||||
* Flexform information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $flexformData = [];
|
||||
|
||||
/**
|
||||
* @var IconFactory
|
||||
*/
|
||||
protected $iconFactory;
|
||||
|
||||
/** @var TemplateLayout $templateLayoutsUtility */
|
||||
protected $templateLayoutsUtility;
|
||||
|
||||
/**
|
||||
* PageLayoutView constructor.
|
||||
* @param TemplateLayout $templateLayout
|
||||
* @param IconFactory $iconFactory
|
||||
*/
|
||||
public function __construct(
|
||||
TemplateLayout $templateLayout,
|
||||
IconFactory $iconFactory
|
||||
) {
|
||||
$this->templateLayoutsUtility = $templateLayout;
|
||||
$this->iconFactory = $iconFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about this extension's pi1 plugin
|
||||
*
|
||||
* @param array $params Parameters to the hook
|
||||
* @return string Information about pi1 plugin
|
||||
*/
|
||||
public function getExtensionSummary(array $params): string
|
||||
{
|
||||
$actionTranslationKey = $result = '';
|
||||
|
||||
$header = '<strong>' . htmlspecialchars($this->getLanguageService()->sL(self::LLPATH . 'pi1_title')) . '</strong>';
|
||||
|
||||
if ($params['row']['list_type'] == self::KEY . '_pi1') {
|
||||
$this->tableData = [];
|
||||
$this->flexformData = GeneralUtility::xml2array($params['row']['pi_flexform']);
|
||||
|
||||
// if flexform data is found
|
||||
$actions = $this->getFieldFromFlexform('switchableControllerActions');
|
||||
if (!empty($actions)) {
|
||||
$actionList = GeneralUtility::trimExplode(';', $actions);
|
||||
|
||||
// translate the first action into its translation
|
||||
$actionTranslationKey = strtolower(str_replace('->', '_', $actionList[0]));
|
||||
$actionTranslation = $this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.mode.' . $actionTranslationKey);
|
||||
|
||||
$header .= '<br><strong style="text-transform: uppercase">' . htmlspecialchars($actionTranslation) . '</strong>';
|
||||
} else {
|
||||
$header .= $this->generateCallout($this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.mode.not_configured'));
|
||||
}
|
||||
|
||||
if (is_array($this->flexformData)) {
|
||||
switch ($actionTranslationKey) {
|
||||
case 'news_list':
|
||||
$this->getStartingPoint();
|
||||
$this->getCategorySettings();
|
||||
$this->getDetailPidSetting();
|
||||
$this->getTimeRestrictionSetting();
|
||||
$this->getTemplateLayoutSettings($params['row']['pid']);
|
||||
$this->getArchiveSettings();
|
||||
$this->getTopNewsRestrictionSetting();
|
||||
$this->getOrderSettings();
|
||||
$this->getOffsetLimitSettings();
|
||||
$this->getListPidSetting();
|
||||
$this->getTagRestrictionSetting();
|
||||
break;
|
||||
case 'news_selectedlist':
|
||||
$this->getSelectedListSetting();
|
||||
$this->getOrderSettings();
|
||||
break;
|
||||
case 'news_detail':
|
||||
$this->getSingleNewsSettings();
|
||||
$this->getDetailPidSetting();
|
||||
$this->getTemplateLayoutSettings($params['row']['pid']);
|
||||
break;
|
||||
case 'news_datemenu':
|
||||
$this->getStartingPoint();
|
||||
$this->getTimeRestrictionSetting();
|
||||
$this->getTopNewsRestrictionSetting();
|
||||
$this->getArchiveSettings();
|
||||
$this->getDateMenuSettings();
|
||||
$this->getCategorySettings();
|
||||
$this->getTemplateLayoutSettings($params['row']['pid']);
|
||||
break;
|
||||
case 'category_list':
|
||||
$this->getCategorySettings(false);
|
||||
$this->getTemplateLayoutSettings($params['row']['pid']);
|
||||
break;
|
||||
case 'tag_list':
|
||||
$this->getStartingPoint();
|
||||
$this->getListPidSetting();
|
||||
$this->getOrderSettings();
|
||||
$this->getTemplateLayoutSettings($params['row']['pid']);
|
||||
break;
|
||||
default:
|
||||
$this->getTemplateLayoutSettings($params['row']['pid']);
|
||||
}
|
||||
|
||||
if ($hooks = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['GeorgRinger\\News\\Hooks\\PageLayoutView']['extensionSummary'] ?? []) {
|
||||
$params = [
|
||||
'action' => $actionTranslationKey
|
||||
];
|
||||
foreach ($hooks as $reference) {
|
||||
GeneralUtility::callUserFunction($reference, $params, $this);
|
||||
}
|
||||
}
|
||||
|
||||
// for all views
|
||||
$this->getOverrideDemandSettings();
|
||||
|
||||
$result = $this->renderSettingsAsTable($header, $params['row']['uid'] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render archive settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getArchiveSettings(): void
|
||||
{
|
||||
$archive = $this->getFieldFromFlexform('settings.archiveRestriction');
|
||||
|
||||
if (!empty($archive)) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.archiveRestriction'),
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.archiveRestriction.' . $archive)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render single news settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getSingleNewsSettings(): void
|
||||
{
|
||||
$singleNewsRecord = (int)$this->getFieldFromFlexform('settings.singleNews');
|
||||
|
||||
if ($singleNewsRecord > 0) {
|
||||
$newsRecord = BackendUtilityCore::getRecord('tx_news_domain_model_news', $singleNewsRecord);
|
||||
|
||||
if (is_array($newsRecord)) {
|
||||
$pageRecord = BackendUtilityCore::getRecord('pages', $newsRecord['pid']);
|
||||
|
||||
if (is_array($pageRecord)) {
|
||||
$content = $this->getRecordData($newsRecord['uid'], 'tx_news_domain_model_news');
|
||||
} else {
|
||||
$text = sprintf(
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'pagemodule.pageNotAvailable'),
|
||||
$newsRecord['pid']
|
||||
);
|
||||
$content = $this->generateCallout($text);
|
||||
}
|
||||
} else {
|
||||
$text = sprintf(
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'pagemodule.newsNotAvailable'),
|
||||
$singleNewsRecord
|
||||
);
|
||||
$content = $this->generateCallout($text);
|
||||
}
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.singleNews'),
|
||||
$content
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render single news settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getDetailPidSetting(): void
|
||||
{
|
||||
$detailPid = (int)$this->getFieldFromFlexform('settings.detailPid', 'additional');
|
||||
|
||||
if ($detailPid > 0) {
|
||||
$content = $this->getRecordData($detailPid);
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_additional.detailPid'),
|
||||
$content
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render listPid news settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getListPidSetting(): void
|
||||
{
|
||||
$listPid = (int)$this->getFieldFromFlexform('settings.listPid', 'additional');
|
||||
|
||||
if ($listPid > 0) {
|
||||
$content = $this->getRecordData($listPid);
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_additional.listPid'),
|
||||
$content
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rendered page title including onclick menu
|
||||
*
|
||||
* @param int $detailPid
|
||||
* @return string
|
||||
* @deprecated use getRecordData() instead
|
||||
*/
|
||||
public function getPageRecordData($detailPid): string
|
||||
{
|
||||
return $this->getRecordData($detailPid, 'pages');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
public function getRecordData($id, $table = 'pages'): string
|
||||
{
|
||||
$record = BackendUtilityCore::getRecord($table, $id);
|
||||
|
||||
if (is_array($record)) {
|
||||
$data = '<span data-toggle="tooltip" data-placement="top" data-title="id=' . $record['uid'] . '">'
|
||||
. $this->iconFactory->getIconForRecord($table, $record, Icon::SIZE_SMALL)->render()
|
||||
. '</span> ';
|
||||
$content = BackendUtilityCore::wrapClickMenuOnIcon(
|
||||
$data,
|
||||
$table,
|
||||
$record['uid'],
|
||||
true,
|
||||
'',
|
||||
'+info,edit,history'
|
||||
);
|
||||
|
||||
$linkTitle = htmlspecialchars(BackendUtilityCore::getRecordTitle($table, $record));
|
||||
|
||||
if ($table === 'pages') {
|
||||
$id = $record['uid'];
|
||||
$currentPageId = (int)GeneralUtility::_GET('id');
|
||||
$link = htmlspecialchars($this->getEditLink($record, $currentPageId));
|
||||
$switchLabel = $this->getLanguageService()->sL(self::LLPATH . 'pagemodule.switchToPage');
|
||||
$content .= ' <a href="#" data-toggle="tooltip" data-placement="top" data-title="' . $switchLabel . '" onclick=\'top.jump("' . $link . '", "web_layout", "web", ' . $id . ');return false\'>' . $linkTitle . '</a>';
|
||||
} else {
|
||||
$content .= $linkTitle;
|
||||
}
|
||||
} else {
|
||||
$text = sprintf(
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'pagemodule.recordNotAvailable'),
|
||||
$id
|
||||
);
|
||||
$content = $this->generateCallout($text);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getOrderSettings(): void
|
||||
{
|
||||
$orderField = $this->getFieldFromFlexform('settings.orderBy');
|
||||
if (!empty($orderField)) {
|
||||
$text = $this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.orderBy.' . $orderField);
|
||||
|
||||
// Order direction (asc, desc)
|
||||
$orderDirection = $this->getOrderDirectionSetting();
|
||||
if ($orderDirection) {
|
||||
$text .= ', ' . strtolower($orderDirection);
|
||||
}
|
||||
|
||||
// Top news first
|
||||
$topNews = $this->getTopNewsFirstSetting();
|
||||
if ($topNews) {
|
||||
$text .= '<br />' . $topNews;
|
||||
}
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.orderBy'),
|
||||
$text
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order direction
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrderDirectionSetting(): string
|
||||
{
|
||||
$text = '';
|
||||
|
||||
$orderDirection = $this->getFieldFromFlexform('settings.orderDirection');
|
||||
if (!empty($orderDirection)) {
|
||||
$text = $this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.orderDirection.' . $orderDirection);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get topNewsFirst setting
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTopNewsFirstSetting(): string
|
||||
{
|
||||
$text = '';
|
||||
$topNewsSetting = (int)$this->getFieldFromFlexform('settings.topNewsFirst', 'additional');
|
||||
if ($topNewsSetting === 1) {
|
||||
$text = $this->getLanguageService()->sL(self::LLPATH . 'flexforms_additional.topNewsFirst');
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render category settings
|
||||
*
|
||||
* @param bool $showCategoryMode show the category conjunction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getCategorySettings($showCategoryMode = true): void
|
||||
{
|
||||
$categories = GeneralUtility::intExplode(',', $this->getFieldFromFlexform('settings.categories') ?? '', true);
|
||||
if (count($categories) > 0) {
|
||||
$categoriesOut = [];
|
||||
foreach ($categories as $id) {
|
||||
$categoriesOut[] = $this->getRecordData($id, 'sys_category');
|
||||
}
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.categories'),
|
||||
implode(', ', $categoriesOut)
|
||||
];
|
||||
|
||||
// Category mode
|
||||
if ($showCategoryMode) {
|
||||
$categoryModeSelection = $this->getFieldFromFlexform('settings.categoryConjunction');
|
||||
if (empty($categoryModeSelection)) {
|
||||
$categoryMode = $this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.categoryConjunction.all');
|
||||
} else {
|
||||
$categoryMode = $this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.categoryConjunction.' . $categoryModeSelection);
|
||||
}
|
||||
|
||||
if (count($categories) > 0 && empty($categoryModeSelection)) {
|
||||
$categoryMode = $this->generateCallout($categoryMode);
|
||||
} else {
|
||||
$categoryMode = htmlspecialchars($categoryMode);
|
||||
}
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.categoryConjunction'),
|
||||
$categoryMode
|
||||
];
|
||||
}
|
||||
|
||||
$includeSubcategories = $this->getFieldFromFlexform('settings.includeSubCategories');
|
||||
if ($includeSubcategories) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.includeSubCategories'),
|
||||
'<i class="fa fa-check"></i>'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the restriction for tags
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getTagRestrictionSetting()
|
||||
{
|
||||
$tags = GeneralUtility::intExplode(',', $this->getFieldFromFlexform('settings.tags', 'additional') ?? '', true);
|
||||
if (count($tags) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$categoryTitles = [];
|
||||
foreach ($tags as $id) {
|
||||
$categoryTitles[] = $this->getRecordData($id, 'tx_news_domain_model_tag');
|
||||
}
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_additional.tags'),
|
||||
implode(', ', $categoryTitles)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render offset & limit configuration
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getOffsetLimitSettings(): void
|
||||
{
|
||||
$offset = $this->getFieldFromFlexform('settings.offset', 'additional');
|
||||
$limit = $this->getFieldFromFlexform('settings.limit', 'additional');
|
||||
$hidePagination = $this->getFieldFromFlexform('settings.hidePagination', 'additional');
|
||||
|
||||
if ($offset) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_additional.offset'),
|
||||
$offset
|
||||
];
|
||||
}
|
||||
if ($limit) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_additional.limit'),
|
||||
$limit
|
||||
];
|
||||
}
|
||||
if ($hidePagination) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_additional.hidePagination'),
|
||||
'<i class="fa fa-check"></i>'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render date menu configuration
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getDateMenuSettings(): void
|
||||
{
|
||||
$dateMenuField = $this->getFieldFromFlexform('settings.dateField');
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.dateField'),
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.dateField.' . $dateMenuField)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render time restriction configuration
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getTimeRestrictionSetting(): void
|
||||
{
|
||||
$timeRestriction = $this->getFieldFromFlexform('settings.timeRestriction');
|
||||
|
||||
if (!empty($timeRestriction)) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.timeRestriction'),
|
||||
htmlspecialchars($timeRestriction)
|
||||
];
|
||||
}
|
||||
|
||||
$timeRestrictionHigh = $this->getFieldFromFlexform('settings.timeRestrictionHigh');
|
||||
if (!empty($timeRestrictionHigh)) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.timeRestrictionHigh'),
|
||||
htmlspecialchars($timeRestrictionHigh)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render top news restriction configuration
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getTopNewsRestrictionSetting(): void
|
||||
{
|
||||
$topNewsRestriction = (int)$this->getFieldFromFlexform('settings.topNewsRestriction');
|
||||
if ($topNewsRestriction > 0) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.topNewsRestriction'),
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.topNewsRestriction.' . $topNewsRestriction)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render template layout configuration
|
||||
*
|
||||
* @param int $pageUid
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getTemplateLayoutSettings($pageUid): void
|
||||
{
|
||||
$title = '';
|
||||
$field = $this->getFieldFromFlexform('settings.templateLayout', 'template');
|
||||
|
||||
// Find correct title by looping over all options
|
||||
if (!empty($field)) {
|
||||
$layouts = $this->templateLayoutsUtility->getAvailableTemplateLayouts($pageUid);
|
||||
foreach ($layouts as $layout) {
|
||||
if ((string)$layout[1] === (string)$field) {
|
||||
$title = $layout[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($title)) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_template.templateLayout'),
|
||||
$this->getLanguageService()->sL($title)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information if override demand setting is disabled or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getOverrideDemandSettings(): void
|
||||
{
|
||||
$field = $this->getFieldFromFlexform('settings.disableOverrideDemand', 'additional');
|
||||
|
||||
if ($field == 1) {
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(
|
||||
self::LLPATH . 'flexforms_additional.disableOverrideDemand'
|
||||
),
|
||||
'<i class="fa fa-check"></i>'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the startingpoint
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getStartingPoint(): void
|
||||
{
|
||||
$value = $this->getFieldFromFlexform('settings.startingpoint');
|
||||
|
||||
if (!empty($value)) {
|
||||
$pageIds = GeneralUtility::intExplode(',', $value, true);
|
||||
$pagesOut = [];
|
||||
|
||||
foreach ($pageIds as $id) {
|
||||
$pagesOut[] = $this->getRecordData($id, 'pages');
|
||||
}
|
||||
|
||||
$recursiveLevel = (int)$this->getFieldFromFlexform('settings.recursive');
|
||||
$recursiveLevelText = '';
|
||||
if ($recursiveLevel === 250) {
|
||||
$recursiveLevelText = $this->getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.5');
|
||||
} elseif ($recursiveLevel > 0) {
|
||||
$recursiveLevelText = $this->getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.' . $recursiveLevel);
|
||||
}
|
||||
|
||||
if (!empty($recursiveLevelText)) {
|
||||
$recursiveLevelText = '<br />' .
|
||||
htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.recursive')) . ' ' .
|
||||
$recursiveLevelText;
|
||||
}
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.startingpoint'),
|
||||
implode(', ', $pagesOut) . $recursiveLevelText
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of selected news items
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function getSelectedListSetting(): void
|
||||
{
|
||||
$value = $this->getFieldFromFlexform('settings.selectedList');
|
||||
|
||||
if (!empty($value)) {
|
||||
$idList = GeneralUtility::intExplode(',', $value, true);
|
||||
$out = [];
|
||||
|
||||
foreach ($idList as $id) {
|
||||
$out[] = $this->getRecordData($id, 'tx_news_domain_model_news');
|
||||
}
|
||||
|
||||
$this->tableData[] = [
|
||||
$this->getLanguageService()->sL(self::LLPATH . 'flexforms_general.selectedList'),
|
||||
implode('<br>', $out)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an alert box
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCallout($text): string
|
||||
{
|
||||
return '<div class="alert alert-warning">
|
||||
' . htmlspecialchars($text) . '
|
||||
</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the settings as table for Web>Page module
|
||||
* System settings are displayed in mono font
|
||||
*
|
||||
* @param string $header
|
||||
* @param int $recordUid
|
||||
* @return string
|
||||
*/
|
||||
protected function renderSettingsAsTable($header = '', $recordUid = 0): string
|
||||
{
|
||||
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
|
||||
$pageRenderer->loadRequireJsModule('TYPO3/CMS/News/PageLayout');
|
||||
$pageRenderer->addCssFile('EXT:news/Resources/Public/Css/Backend/PageLayoutView.css');
|
||||
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:news/Resources/Private/Backend/PageLayoutView.html'));
|
||||
$view->assignMultiple([
|
||||
'header' => $header,
|
||||
'rows' => [
|
||||
'above' => array_slice($this->tableData, 0, self::SETTINGS_IN_PREVIEW),
|
||||
'below' => array_slice($this->tableData, self::SETTINGS_IN_PREVIEW)
|
||||
],
|
||||
'id' => $recordUid
|
||||
]);
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field value from flexform configuration,
|
||||
* including checks if flexform configuration is available
|
||||
*
|
||||
* @param string $key name of the key
|
||||
* @param string $sheet name of the sheet
|
||||
* @return string|null if nothing found, value if found
|
||||
*/
|
||||
public function getFieldFromFlexform($key, $sheet = 'sDEF'): ?string
|
||||
{
|
||||
$flexform = $this->flexformData;
|
||||
if (isset($flexform['data'])) {
|
||||
$flexform = $flexform['data'];
|
||||
if (isset($flexform[$sheet]['lDEF'][$key]['vDEF'])
|
||||
) {
|
||||
return $flexform[$sheet]['lDEF'][$key]['vDEF'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a backend edit link based on given record.
|
||||
*
|
||||
* @param array $row Current record row from database.
|
||||
* @param int $currentPageUid current page uid
|
||||
* @return string Link to open an edit window for record.
|
||||
* @see \TYPO3\CMS\Backend\Utility\BackendUtilityCore::readPageAccess()
|
||||
*/
|
||||
protected function getEditLink($row, $currentPageUid): string
|
||||
{
|
||||
$editLink = '';
|
||||
$localCalcPerms = $GLOBALS['BE_USER']->calcPerms(BackendUtilityCore::getRecord('pages', $row['uid']));
|
||||
$permsEdit = $localCalcPerms & Permission::PAGE_EDIT;
|
||||
if ($permsEdit) {
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
$returnUrl = $uriBuilder->buildUriFromRoute('web_layout', ['id' => $currentPageUid]);
|
||||
$editLink = $uriBuilder->buildUriFromRoute('web_layout', [
|
||||
'id' => $row['uid'],
|
||||
'returnUrl' => $returnUrl
|
||||
]);
|
||||
}
|
||||
return $editLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return language service instance
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Localization\LanguageService
|
||||
*/
|
||||
public function getLanguageService(): \TYPO3\CMS\Core\Localization\LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DocumentTemplate
|
||||
*
|
||||
* @return DocumentTemplate
|
||||
*/
|
||||
protected function getDocumentTemplate(): DocumentTemplate
|
||||
{
|
||||
return $GLOBALS['TBE_TEMPLATE'];
|
||||
}
|
||||
}
|
||||
102
typo3conf/ext/news/Classes/Jobs/AbstractImportJob.php
Normal file
102
typo3conf/ext/news/Classes/Jobs/AbstractImportJob.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Jobs;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract Import job
|
||||
*/
|
||||
abstract class AbstractImportJob implements ImportJobInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \GeorgRinger\News\Service\Import\DataProviderServiceInterface
|
||||
*/
|
||||
protected $importDataProviderService;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\SingletonInterface
|
||||
*/
|
||||
protected $importService;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $importServiceSettings = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $importItemOverwrite = [];
|
||||
|
||||
/*
|
||||
* @var int
|
||||
*/
|
||||
protected $numberOfRecordsPerRun;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $increaseOffsetPerRunBy;
|
||||
|
||||
/**
|
||||
* Get number of runs
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumberOfRecordsPerRun(): int
|
||||
{
|
||||
// If not explicit defined by the job we import all records at once.
|
||||
if ($this->numberOfRecordsPerRun === null) {
|
||||
$this->numberOfRecordsPerRun = $this->importDataProviderService->getTotalRecordCount();
|
||||
}
|
||||
return $this->numberOfRecordsPerRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this job is enabled. Do perform some checks her if you need to.
|
||||
* E.g. check if a certain extension is loaded or similar.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get job info.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInfo(): array
|
||||
{
|
||||
$totalRecordCount = (int)$this->importDataProviderService->getTotalRecordCount();
|
||||
$info = [
|
||||
'totalRecordCount' => $totalRecordCount,
|
||||
'runsToComplete' => $totalRecordCount > 0 ? (ceil($totalRecordCount / $this->getNumberOfRecordsPerRun())) : 0,
|
||||
'increaseOffsetPerRunBy' => $this->getNumberOfRecordsPerRun(),
|
||||
];
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual run method.
|
||||
*
|
||||
* @param int $offset
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run($offset)
|
||||
{
|
||||
$importData = $this->importDataProviderService->getImportData($offset, $this->getNumberOfRecordsPerRun());
|
||||
$this->importService->import($importData, $this->importItemOverwrite, $this->importServiceSettings);
|
||||
}
|
||||
}
|
||||
24
typo3conf/ext/news/Classes/Jobs/ImportJobInterface.php
Normal file
24
typo3conf/ext/news/Classes/Jobs/ImportJobInterface.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Jobs;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Import job interface
|
||||
*/
|
||||
interface ImportJobInterface
|
||||
{
|
||||
public function getNumberOfRecordsPerRun();
|
||||
|
||||
public function getInfo();
|
||||
|
||||
public function isEnabled();
|
||||
|
||||
public function run($offset);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace GeorgRinger\News\Pagination;
|
||||
|
||||
use TYPO3\CMS\Core\Pagination\AbstractPaginator;
|
||||
|
||||
abstract class CustomAbstractPaginator extends AbstractPaginator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $currentPageNumber = 1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $itemsPerPage = 10;
|
||||
|
||||
protected $initialOffset = 0;
|
||||
protected $initialLimit = 0;
|
||||
|
||||
/**
|
||||
* This method is the heart of the pagination. It updates all internal params and then calls the
|
||||
* {@see updatePaginatedItems} method which must update the set of paginated items.
|
||||
*/
|
||||
protected function updateInternalState(): void
|
||||
{
|
||||
// offset
|
||||
if ($this->currentPageNumber > 1) {
|
||||
$offset = ($this->itemsPerPage * ($this->currentPageNumber - 1));
|
||||
$offset += $this->initialOffset;
|
||||
} elseif ($this->initialOffset > 0) {
|
||||
$offset = $this->initialOffset;
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
// limit
|
||||
$limit = $this->itemsPerPage;
|
||||
|
||||
$totalAmountOfItems = $this->getTotalAmountOfItems();
|
||||
|
||||
/*
|
||||
* If the total amount of items is zero, then the number of pages is mathematically zero as
|
||||
* well. As that looks strange in the frontend, the number of pages is forced to be at least
|
||||
* one.
|
||||
*/
|
||||
$this->numberOfPages = max(1, (int)ceil($totalAmountOfItems / $this->itemsPerPage));
|
||||
|
||||
/*
|
||||
* To prevent empty results in case the given current page number exceeds the maximum number
|
||||
* of pages, we set the current page number to the last page and update the internal state
|
||||
* with this value again. Such situation should in the first place be prevented by not allowing
|
||||
* those values to be passed, e.g. by using the "max" attribute in the view. However there are
|
||||
* valid cases. For example when a user deletes a record while the pagination is already visible
|
||||
* to another user with, until then, a valid "max" value. Passing invalid values unintentionally
|
||||
* should therefore just silently be resolved.
|
||||
*/
|
||||
if ($this->currentPageNumber > $this->numberOfPages) {
|
||||
$this->currentPageNumber = $this->numberOfPages;
|
||||
$this->updateInternalState();
|
||||
return;
|
||||
}
|
||||
|
||||
$isUpdated = false;
|
||||
if ($this->currentPageNumber === $this->numberOfPages && $this->initialLimit > 0) {
|
||||
$difference = $this->initialLimit - ((integer)($this->itemsPerPage * ($this->currentPageNumber - 1)));
|
||||
if ($difference > 0) {
|
||||
$this->updatePaginatedItems($difference, $offset);
|
||||
$isUpdated = true;
|
||||
$totalAmountOfItems = $this->getTotalAmountOfItems();
|
||||
$this->numberOfPages = max(1, (int)ceil($totalAmountOfItems / $this->itemsPerPage));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isUpdated) {
|
||||
$this->updatePaginatedItems($limit, $offset);
|
||||
}
|
||||
|
||||
if (!$this->hasItemsOnCurrentPage()) {
|
||||
$this->keyOfFirstPaginatedItem = 0;
|
||||
$this->keyOfLastPaginatedItem = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
$indexOfLastPaginatedItem = min($offset + $this->itemsPerPage, $totalAmountOfItems);
|
||||
|
||||
$this->keyOfFirstPaginatedItem = $offset;
|
||||
$this->keyOfLastPaginatedItem = $indexOfLastPaginatedItem - 1;
|
||||
}
|
||||
|
||||
public function getCurrentPageNumber(): int
|
||||
{
|
||||
return $this->currentPageNumber;
|
||||
}
|
||||
|
||||
protected function setItemsPerPage(int $itemsPerPage): void
|
||||
{
|
||||
if ($itemsPerPage < 1) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Argument $itemsPerPage must be greater than 0',
|
||||
1573061766
|
||||
);
|
||||
}
|
||||
|
||||
$this->itemsPerPage = $itemsPerPage;
|
||||
}
|
||||
|
||||
protected function setCurrentPageNumber(int $currentPageNumber): void
|
||||
{
|
||||
if ($currentPageNumber < 1) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Argument $currentPageNumber must be greater than 0',
|
||||
1573047338
|
||||
);
|
||||
}
|
||||
|
||||
$this->currentPageNumber = $currentPageNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace GeorgRinger\News\Pagination;
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
||||
|
||||
final class QueryResultPaginator extends CustomAbstractPaginator
|
||||
{
|
||||
/**
|
||||
* @var QueryResultInterface
|
||||
*/
|
||||
private $queryResult;
|
||||
|
||||
/**
|
||||
* @var QueryResultInterface
|
||||
*/
|
||||
private $paginatedQueryResult;
|
||||
|
||||
public function __construct(
|
||||
QueryResultInterface $queryResult,
|
||||
int $currentPageNumber = 1,
|
||||
int $itemsPerPage = 10,
|
||||
int $initialLimit = 0,
|
||||
int $initialOffset = 0
|
||||
) {
|
||||
$this->queryResult = $queryResult;
|
||||
$this->setCurrentPageNumber($currentPageNumber);
|
||||
$this->setItemsPerPage($itemsPerPage);
|
||||
$this->initialLimit = $initialLimit;
|
||||
$this->initialOffset = $initialOffset;
|
||||
$this->updateInternalState();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable|QueryResultInterface
|
||||
*/
|
||||
public function getPaginatedItems(): iterable
|
||||
{
|
||||
return $this->paginatedQueryResult;
|
||||
}
|
||||
|
||||
protected function updatePaginatedItems(int $limit, int $offset): void
|
||||
{
|
||||
$this->paginatedQueryResult = $this->queryResult
|
||||
->getQuery()
|
||||
->setLimit($limit)
|
||||
->setOffset($offset)
|
||||
->execute();
|
||||
}
|
||||
|
||||
protected function getTotalAmountOfItems(): int
|
||||
{
|
||||
return count($this->queryResult);
|
||||
}
|
||||
|
||||
protected function getAmountOfItemsOnCurrentPage(): int
|
||||
{
|
||||
return count($this->paginatedQueryResult);
|
||||
}
|
||||
}
|
||||
70
typo3conf/ext/news/Classes/Seo/HrefLangEvent.php
Normal file
70
typo3conf/ext/news/Classes/Seo/HrefLangEvent.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Seo;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor;
|
||||
use TYPO3\CMS\Frontend\Event\ModifyHrefLangTagsEvent;
|
||||
|
||||
/**
|
||||
* Remove the hreflang for news in strict mode with no translations
|
||||
*/
|
||||
class HrefLangEvent
|
||||
{
|
||||
/** @var ContentObjectRenderer */
|
||||
public $cObj;
|
||||
|
||||
/** @var LanguageMenuProcessor */
|
||||
protected $languageMenuProcessor;
|
||||
|
||||
public function __construct(ContentObjectRenderer $cObj, LanguageMenuProcessor $languageMenuProcessor)
|
||||
{
|
||||
$this->cObj = $cObj;
|
||||
$this->languageMenuProcessor = $languageMenuProcessor;
|
||||
}
|
||||
|
||||
public function __invoke(ModifyHrefLangTagsEvent $event): void
|
||||
{
|
||||
$newsAvailabilityChecker = GeneralUtility::makeInstance(NewsAvailability::class);
|
||||
if ($newsAvailabilityChecker->getNewsIdFromRequest() > 0) {
|
||||
$allHrefLangs = $event->getHrefLangs();
|
||||
|
||||
$languages = $this->languageMenuProcessor->process($this->cObj, [], [], []);
|
||||
$errorTriggered = false;
|
||||
foreach ($languages['languagemenu'] as $language) {
|
||||
$hreflangKey = $language['hreflang'];
|
||||
// skip all languages which are not used in hreflang
|
||||
if (!isset($allHrefLangs[$hreflangKey]) || $hreflangKey === 'x-default') {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$check = $newsAvailabilityChecker->check($language['languageId']);
|
||||
|
||||
if (!$check) {
|
||||
unset($allHrefLangs[$hreflangKey]);
|
||||
}
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
$errorTriggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$errorTriggered) {
|
||||
if (count($allHrefLangs) <= 2) {
|
||||
unset($allHrefLangs['x-default']);
|
||||
}
|
||||
$event->setHrefLangs($allHrefLangs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
140
typo3conf/ext/news/Classes/Seo/NewsAvailability.php
Normal file
140
typo3conf/ext/news/Classes/Seo/NewsAvailability.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Seo;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteInterface;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if a news record is available
|
||||
*/
|
||||
class NewsAvailability
|
||||
{
|
||||
|
||||
/**
|
||||
* @param int $languageId
|
||||
* @param int $newsId
|
||||
* @return bool
|
||||
*/
|
||||
public function check(int $languageId, int $newsId = 0): bool
|
||||
{
|
||||
// get it from current request
|
||||
if ($newsId === 0) {
|
||||
$newsId = $this->getNewsIdFromRequest();
|
||||
}
|
||||
if ($newsId === 0) {
|
||||
throw new \UnexpectedValueException('No news id provided', 1586431984);
|
||||
}
|
||||
|
||||
/** @var SiteInterface $site */
|
||||
$site = $this->getRequest()->getAttribute('site');
|
||||
$allAvailableLanguagesOfSite = $site->getAllLanguages();
|
||||
|
||||
$targetLanguage = $this->getLanguageFromAllLanguages($allAvailableLanguagesOfSite, $languageId);
|
||||
if (!$targetLanguage) {
|
||||
throw new \UnexpectedValueException('Target language could not be found', 1586431985);
|
||||
}
|
||||
return $this->mustBeIncluded($newsId, $targetLanguage);
|
||||
}
|
||||
|
||||
protected function mustBeIncluded(int $newsId, SiteLanguage $language): bool
|
||||
{
|
||||
if ($language->getFallbackType() === 'strict') {
|
||||
$newsRecord = $this->getNewsRecord($newsId, $language->getLanguageId());
|
||||
|
||||
if (!is_array($newsRecord) || empty($newsRecord)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SiteLanguage[] $allLanguages
|
||||
* @param int $languageId
|
||||
*/
|
||||
protected function getLanguageFromAllLanguages(array $allLanguages, int $languageId): ?SiteLanguage
|
||||
{
|
||||
foreach ($allLanguages as $siteLanguage) {
|
||||
if ($siteLanguage->getLanguageId() === $languageId) {
|
||||
return $siteLanguage;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getNewsRecord(int $newsId, int $language)
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_news_domain_model_news');
|
||||
if ($language === 0) {
|
||||
$where = [
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT))
|
||||
),
|
||||
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT))
|
||||
];
|
||||
} else {
|
||||
$where = [
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT))
|
||||
),
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq('l10n_parent', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT))
|
||||
),
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('l10n_parent', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT))
|
||||
)
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
$row = $queryBuilder
|
||||
->select('uid', 'l10n_parent', 'sys_language_uid')
|
||||
->from('tx_news_domain_model_news')
|
||||
->where(...$where)
|
||||
->execute()
|
||||
->fetch();
|
||||
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
protected function getRequest(): ServerRequestInterface
|
||||
{
|
||||
return $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
|
||||
public function getNewsIdFromRequest(): int
|
||||
{
|
||||
$newsId = 0;
|
||||
/** @var PageArguments $pageArguments */
|
||||
$pageArguments = $this->getRequest()->getAttribute('routing');
|
||||
if (isset($pageArguments->getRouteArguments()['tx_news_pi1']['news'])) {
|
||||
$newsId = (int)$pageArguments->getRouteArguments()['tx_news_pi1']['news'];
|
||||
} elseif (isset($this->getRequest()->getQueryParams()['tx_news_pi1']['news'])) {
|
||||
$newsId = (int)$this->getRequest()->getQueryParams()['tx_news_pi1']['news'];
|
||||
}
|
||||
return $newsId;
|
||||
}
|
||||
}
|
||||
52
typo3conf/ext/news/Classes/Seo/NewsTitleProvider.php
Normal file
52
typo3conf/ext/news/Classes/Seo/NewsTitleProvider.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Seo;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use GeorgRinger\News\Domain\Model\News;
|
||||
use TYPO3\CMS\Core\PageTitle\AbstractPageTitleProvider;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Generate page title based on properties of the news model
|
||||
*/
|
||||
class NewsTitleProvider extends AbstractPageTitleProvider
|
||||
{
|
||||
private const DEFAULT_PROPERTIES = 'title';
|
||||
private const DEFAULT_GLUE = '" "';
|
||||
|
||||
/**
|
||||
* @param News $news
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function setTitleByNews(News $news, array $configuration = []): void
|
||||
{
|
||||
$title = '';
|
||||
$fields = GeneralUtility::trimExplode(',', $configuration['properties'] ?? self::DEFAULT_PROPERTIES, true);
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$getter = 'get' . ucfirst($field);
|
||||
$value = $news->$getter();
|
||||
if ($value) {
|
||||
$title = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($title) {
|
||||
$this->title = $title;
|
||||
}
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
}
|
||||
259
typo3conf/ext/news/Classes/Seo/NewsXmlSitemapDataProvider.php
Normal file
259
typo3conf/ext/news/Classes/Seo/NewsXmlSitemapDataProvider.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Seo;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Context\WorkspaceAspect;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Seo\XmlSitemap\AbstractXmlSitemapDataProvider;
|
||||
use TYPO3\CMS\Seo\XmlSitemap\Exception\MissingConfigurationException;
|
||||
|
||||
/**
|
||||
* Generate sitemap for news records
|
||||
*/
|
||||
class NewsXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string $key
|
||||
* @param array $config
|
||||
* @param ContentObjectRenderer|null $cObj
|
||||
* @throws MissingConfigurationException
|
||||
*/
|
||||
public function __construct(ServerRequestInterface $request, string $key, array $config = [], ContentObjectRenderer $cObj = null)
|
||||
{
|
||||
parent::__construct($request, $key, $config, $cObj);
|
||||
|
||||
$this->generateItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MissingConfigurationException
|
||||
*/
|
||||
public function generateItems(): void
|
||||
{
|
||||
$table = 'tx_news_domain_model_news';
|
||||
|
||||
$pids = !empty($this->config['pid']) ? GeneralUtility::intExplode(',', $this->config['pid']) : [];
|
||||
$lastModifiedField = $this->config['lastModifiedField'] ?? 'tstamp';
|
||||
$sortField = $this->config['sortField'] ?? 'datetime';
|
||||
$forGoogleNews = $this->config['googleNews'] ?? false;
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable($table);
|
||||
|
||||
$constraints = [];
|
||||
if (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField'])) {
|
||||
$constraints[] = $queryBuilder->expr()->in(
|
||||
$GLOBALS['TCA'][$table]['ctrl']['languageField'],
|
||||
[
|
||||
-1, // All languages
|
||||
$this->getLanguageId() // Current language
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($pids)) {
|
||||
$recursiveLevel = isset($this->config['recursive']) ? (int)$this->config['recursive'] : 0;
|
||||
if ($recursiveLevel) {
|
||||
$newList = [];
|
||||
foreach ($pids as $pid) {
|
||||
$list = $this->cObj->getTreeList($pid, $recursiveLevel);
|
||||
if ($list) {
|
||||
$newList = array_merge($newList, explode(',', $list));
|
||||
}
|
||||
}
|
||||
$pids = array_merge($pids, $newList);
|
||||
}
|
||||
|
||||
$constraints[] = $queryBuilder->expr()->in('pid', $pids);
|
||||
}
|
||||
|
||||
if ($forGoogleNews) {
|
||||
$constraints[] = $queryBuilder->expr()->gt($sortField, (new \DateTime('-2 days'))->getTimestamp());
|
||||
}
|
||||
|
||||
if (!empty($this->config['excludedTypes'])) {
|
||||
$excludedTypes = GeneralUtility::trimExplode(',', $this->config['excludedTypes'], true);
|
||||
if (!empty($excludedTypes)) {
|
||||
$constraints[] = $queryBuilder->expr()->notIn(
|
||||
'type',
|
||||
$queryBuilder->createNamedParameter($excludedTypes, Connection::PARAM_STR_ARRAY)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->config['additionalWhere'])) {
|
||||
$constraints[] = $this->config['additionalWhere'];
|
||||
}
|
||||
|
||||
$queryBuilder->getRestrictions()->add(
|
||||
GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getCurrentWorkspaceAspect()->getId())
|
||||
);
|
||||
|
||||
$queryBuilder->select('*')
|
||||
->from($table);
|
||||
|
||||
if (!empty($constraints)) {
|
||||
$queryBuilder->where(
|
||||
...$constraints
|
||||
);
|
||||
}
|
||||
|
||||
$rows = $queryBuilder->orderBy($sortField, $forGoogleNews ? 'DESC' : 'ASC')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$this->items[] = [
|
||||
'data' => $row,
|
||||
'lastMod' => (int)$row[$lastModifiedField],
|
||||
'priority' => 0.5
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function defineUrl(array $data): array
|
||||
{
|
||||
$pageId = $this->config['url']['pageId'] ?? $GLOBALS['TSFE']->id;
|
||||
if ($this->config['url']['useCategorySinglePid'] && $pageIdFromCategory = $this->getSinglePidFromCategory($data['data']['uid'])) {
|
||||
$pageId = $pageIdFromCategory;
|
||||
}
|
||||
|
||||
$additionalParams = [];
|
||||
$additionalParams = $this->getUrlFieldParameterMap($additionalParams, $data['data']);
|
||||
$additionalParams = $this->getUrlAdditionalParams($additionalParams);
|
||||
|
||||
if (!empty($this->config['url']['hrDate']) && !empty($data['data']['datetime'])) {
|
||||
// adjust timezone (database field is UTC)
|
||||
$timezoneCorrectedDatetime = (int)$data['data']['datetime'] + date('Z', (int)$data['data']['datetime']);
|
||||
$dateTime = \DateTime::createFromFormat('U', (string)$timezoneCorrectedDatetime);
|
||||
if (!empty($this->config['url']['hrDate']['day'])) {
|
||||
$additionalParams['tx_news_pi1[day]'] = $dateTime->format($this->config['url']['hrDate']['day']);
|
||||
}
|
||||
if (!empty($this->config['url']['hrDate']['month'])) {
|
||||
$additionalParams['tx_news_pi1[month]'] = $dateTime->format($this->config['url']['hrDate']['month']);
|
||||
}
|
||||
if (!empty($this->config['url']['hrDate']['year'])) {
|
||||
$additionalParams['tx_news_pi1[year]'] = $dateTime->format($this->config['url']['hrDate']['year']);
|
||||
}
|
||||
}
|
||||
|
||||
$additionalParamsString = http_build_query(
|
||||
$additionalParams,
|
||||
'',
|
||||
'&',
|
||||
PHP_QUERY_RFC3986
|
||||
);
|
||||
|
||||
$typoLinkConfig = [
|
||||
'parameter' => $pageId,
|
||||
'additionalParams' => $additionalParamsString ? '&' . $additionalParamsString : '',
|
||||
'forceAbsoluteUrl' => 1,
|
||||
];
|
||||
|
||||
$data['loc'] = $this->cObj->typoLink_URL($typoLinkConfig);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a pid for the single view from the category.
|
||||
*
|
||||
* @param int $newsId
|
||||
* @return int
|
||||
*/
|
||||
protected function getSinglePidFromCategory(int $newsId): int
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category');
|
||||
$categoryRecord = $queryBuilder
|
||||
->select('title', 'single_pid')
|
||||
->from('sys_category')
|
||||
->leftJoin(
|
||||
'sys_category',
|
||||
'sys_category_record_mm',
|
||||
'sys_category_record_mm',
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.uid_local', $queryBuilder->quoteIdentifier('sys_category.uid'))
|
||||
)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->gt('sys_category.single_pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.uid_foreign', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT))
|
||||
)
|
||||
->setMaxResults(1)
|
||||
->execute()->fetch();
|
||||
return (int)$categoryRecord['single_pid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $additionalParams
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function getUrlFieldParameterMap(array $additionalParams, array $data): array
|
||||
{
|
||||
if (!empty($this->config['url']['fieldToParameterMap']) &&
|
||||
\is_array($this->config['url']['fieldToParameterMap'])) {
|
||||
foreach ($this->config['url']['fieldToParameterMap'] as $field => $urlPart) {
|
||||
$additionalParams[$urlPart] = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
return $additionalParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $additionalParams
|
||||
* @return array
|
||||
*/
|
||||
protected function getUrlAdditionalParams(array $additionalParams): array
|
||||
{
|
||||
if (!empty($this->config['url']['additionalGetParameters']) &&
|
||||
is_array($this->config['url']['additionalGetParameters'])) {
|
||||
foreach ($this->config['url']['additionalGetParameters'] as $extension => $extensionConfig) {
|
||||
foreach ($extensionConfig as $key => $value) {
|
||||
$additionalParams[$extension . '[' . $key . ']'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $additionalParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
|
||||
*/
|
||||
protected function getLanguageId(): int
|
||||
{
|
||||
$context = GeneralUtility::makeInstance(Context::class);
|
||||
return (int)$context->getPropertyFromAspect('language', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WorkspaceAspect
|
||||
*/
|
||||
protected function getCurrentWorkspaceAspect(): WorkspaceAspect
|
||||
{
|
||||
return GeneralUtility::makeInstance(Context::class)->getAspect('workspace');
|
||||
}
|
||||
}
|
||||
171
typo3conf/ext/news/Classes/Service/AccessControlService.php
Normal file
171
typo3conf/ext/news/Classes/Service/AccessControlService.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Service;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Service for access control related stuff
|
||||
*/
|
||||
class AccessControlService
|
||||
{
|
||||
|
||||
/**
|
||||
* Check if a user has access to all categories of a news record
|
||||
*
|
||||
* @param array $newsRecord
|
||||
* @return bool
|
||||
*/
|
||||
public static function userHasCategoryPermissionsForRecord(array $newsRecord): bool
|
||||
{
|
||||
$settings = GeneralUtility::makeInstance(EmConfiguration::class);
|
||||
if (!$settings->getCategoryBeGroupTceFormsRestriction()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self::getBackendUser()->isAdmin()) {
|
||||
// an admin may edit all news
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there are any categories with denied access, the user has no permission
|
||||
if (count(self::getAccessDeniedCategories($newsRecord))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array with the uid and title of all categories the user doesn't have access to
|
||||
*
|
||||
* @param array $newsRecord
|
||||
* @return array
|
||||
*/
|
||||
public static function getAccessDeniedCategories(array $newsRecord): array
|
||||
{
|
||||
if (self::getBackendUser()->isAdmin()) {
|
||||
// an admin may edit all news so no categories without access
|
||||
return [];
|
||||
}
|
||||
|
||||
// no category mounts set means access to all
|
||||
$backendUserCategories = self::getBackendUser()->getCategoryMountPoints();
|
||||
if ($backendUserCategories === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$catService = GeneralUtility::makeInstance(CategoryService::class);
|
||||
$subCategories = $catService::getChildrenCategories(implode(',', $backendUserCategories));
|
||||
if (!empty($subCategories)) {
|
||||
$backendUserCategories = explode(',', $subCategories);
|
||||
}
|
||||
|
||||
$newsRecordCategories = self::getCategoriesForNewsRecord($newsRecord);
|
||||
|
||||
// Remove categories the user has access to
|
||||
foreach ($newsRecordCategories as $key => $newsRecordCategory) {
|
||||
if (in_array($newsRecordCategory['uid'], $backendUserCategories)) {
|
||||
unset($newsRecordCategories[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $newsRecordCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all categories for a news record respecting l10n_mode
|
||||
*
|
||||
* @param array $newsRecord
|
||||
* @return array
|
||||
*/
|
||||
public static function getCategoriesForNewsRecord($newsRecord): array
|
||||
{
|
||||
// determine localization overlay mode to select categories either from parent or localized record
|
||||
if ($newsRecord['sys_language_uid'] > 0 && $newsRecord['l10n_parent'] > 0) {
|
||||
// localized version of a news record
|
||||
$categoryL10nMode = $GLOBALS['TCA']['tx_news_domain_model_news']['columns']['categories']['l10n_mode'];
|
||||
if ($categoryL10nMode === 'mergeIfNotBlank') {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category_record_mm');
|
||||
$newsRecordCategoriesCount = $queryBuilder->count('*')
|
||||
->from('sys_category_record_mm')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($newsRecord['uid'], \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('categories', \PDO::PARAM_STR))
|
||||
)
|
||||
->execute()
|
||||
->fetchColumn(0);
|
||||
if ($newsRecordCategoriesCount > 0) {
|
||||
// take categories from localized version
|
||||
$newsRecordUid = $newsRecord['uid'];
|
||||
} else {
|
||||
// inherit categories from parent
|
||||
$newsRecordUid = $newsRecord['l10n_parent'];
|
||||
}
|
||||
} elseif ($categoryL10nMode === 'exclude') {
|
||||
// exclude: The localized version inherits the categories of the parent
|
||||
$newsRecordUid = $newsRecord['l10n_parent'];
|
||||
} else {
|
||||
// noCopy/prefixLangTitle: no inheritance
|
||||
$newsRecordUid = $newsRecord['uid'];
|
||||
}
|
||||
} else {
|
||||
$newsRecordUid = $newsRecord['uid'];
|
||||
}
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category');
|
||||
$queryBuilder->getRestrictions()
|
||||
->removeByType(StartTimeRestriction::class)
|
||||
->removeByType(HiddenRestriction::class)
|
||||
->removeByType(EndTimeRestriction::class);
|
||||
$res = $queryBuilder
|
||||
->select('sys_category_record_mm.uid_local', 'sys_category.title')
|
||||
->from('sys_category')
|
||||
->leftJoin(
|
||||
'sys_category',
|
||||
'sys_category_record_mm',
|
||||
'sys_category_record_mm',
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.uid_local', $queryBuilder->quoteIdentifier('sys_category.uid'))
|
||||
)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.fieldname', $queryBuilder->createNamedParameter('categories', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.uid_foreign', $queryBuilder->createNamedParameter($newsRecordUid, \PDO::PARAM_INT))
|
||||
)
|
||||
->execute();
|
||||
|
||||
$categories = [];
|
||||
while ($row =$res->fetch()) {
|
||||
$categories[] = [
|
||||
'uid' => $row['uid_local'],
|
||||
'title' => $row['title']
|
||||
];
|
||||
}
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current BE user.
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
*/
|
||||
protected static function getBackendUser(): \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
172
typo3conf/ext/news/Classes/Service/CategoryService.php
Normal file
172
typo3conf/ext/news/Classes/Service/CategoryService.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Service;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Service for category related stuff
|
||||
*/
|
||||
class CategoryService
|
||||
{
|
||||
|
||||
/**
|
||||
* Get child categories by calling recursive function
|
||||
* and using the caching framework to save some queries
|
||||
*
|
||||
* @param string $idList list of category ids to start
|
||||
* @param int $counter
|
||||
* @param string $additionalWhere additional where clause
|
||||
* @param bool $removeGivenIdListFromResult remove the given id list from result
|
||||
* @return string comma separated list of category ids
|
||||
*/
|
||||
public static function getChildrenCategories(
|
||||
$idList,
|
||||
$counter = 0,
|
||||
$additionalWhere = '',
|
||||
$removeGivenIdListFromResult = false
|
||||
): string {
|
||||
if ($additionalWhere !== '') {
|
||||
throw new \UnexpectedValueException('The argument $additionalWhere is not supported anymore');
|
||||
}
|
||||
$cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('news_category');
|
||||
$cacheIdentifier = sha1('children' . $idList);
|
||||
|
||||
$entry = $cache->get($cacheIdentifier);
|
||||
if (!$entry) {
|
||||
$entry = self::getChildrenCategoriesRecursive($idList, $counter, $additionalWhere);
|
||||
$cache->set($cacheIdentifier, $entry);
|
||||
}
|
||||
|
||||
if ($removeGivenIdListFromResult) {
|
||||
$entry = self::removeValuesFromString($entry, $idList);
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove values of a comma separated list from another comma separated list
|
||||
*
|
||||
* @param string $result string comma separated list
|
||||
* @param $toBeRemoved string comma separated list
|
||||
* @return string
|
||||
*/
|
||||
public static function removeValuesFromString($result, string $toBeRemoved): string
|
||||
{
|
||||
$resultAsArray = GeneralUtility::trimExplode(',', $result, true);
|
||||
$idListAsArray = GeneralUtility::trimExplode(',', $toBeRemoved, true);
|
||||
|
||||
$result = implode(',', array_diff($resultAsArray, $idListAsArray));
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get child categories
|
||||
*
|
||||
* @param string $idList list of category ids to start
|
||||
* @param int $counter
|
||||
* @param string $additionalWhere additional where clause
|
||||
* @return string comma separated list of category ids
|
||||
*/
|
||||
private static function getChildrenCategoriesRecursive($idList, $counter = 0, $additionalWhere = ''): string
|
||||
{
|
||||
$result = [];
|
||||
|
||||
// add idlist to the output too
|
||||
if ($counter === 0) {
|
||||
$result[] = self::cleanIntList($idList);
|
||||
}
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category');
|
||||
$res = $queryBuilder
|
||||
->select('uid')
|
||||
->from('sys_category')
|
||||
->where(
|
||||
$queryBuilder->expr()->in('parent', $queryBuilder->createNamedParameter(array_map('intval', explode(',', $idList)), Connection::PARAM_INT_ARRAY))
|
||||
)
|
||||
->execute();
|
||||
|
||||
while (($row = $res->fetch())) {
|
||||
$counter++;
|
||||
if ($counter > 10000) {
|
||||
GeneralUtility::makeInstance(TimeTracker::class)->setTSlogMessage('EXT:news: one or more recursive categories where found');
|
||||
return implode(',', $result);
|
||||
}
|
||||
$subcategories = self::getChildrenCategoriesRecursive($row['uid'], $counter);
|
||||
$result[] = $row['uid'] . ($subcategories ? ',' . $subcategories : '');
|
||||
}
|
||||
|
||||
$result = implode(',', $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a category record in the backend
|
||||
*
|
||||
* @param string $default default label
|
||||
* @param array $row category record
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public static function translateCategoryRecord($default, array $row = []): string
|
||||
{
|
||||
if (TYPO3_MODE !== 'BE') {
|
||||
throw new \UnexpectedValueException('TYPO3 Mode must be BE');
|
||||
}
|
||||
|
||||
$overlayLanguage = (int)($GLOBALS['BE_USER']->uc['newsoverlay'] ?? 0);
|
||||
|
||||
$title = '';
|
||||
|
||||
if ($row['uid'] > 0 && $overlayLanguage > 0 && !isset($row['sys_language_uid'])) {
|
||||
$row = BackendUtility::getRecord('sys_category', $row['uid']);
|
||||
}
|
||||
|
||||
if ($row['uid'] > 0 && $overlayLanguage > 0 && $row['sys_language_uid'] === 0) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category');
|
||||
$overlayRecord = $queryBuilder
|
||||
->select('title')
|
||||
->from('sys_category')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($overlayLanguage, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('l10n_parent', $queryBuilder->createNamedParameter($row['uid'], \PDO::PARAM_INT))
|
||||
)
|
||||
->setMaxResults(1)
|
||||
->execute()->fetch();
|
||||
|
||||
if (is_array($overlayRecord) && !empty($overlayRecord)) {
|
||||
$title = $overlayRecord['title'] . ' (' . $row['title'] . ')';
|
||||
}
|
||||
}
|
||||
|
||||
$title = $title ?: $default ?: '';
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean list of integers
|
||||
*
|
||||
* @param string $list
|
||||
* @return string
|
||||
*/
|
||||
private static function cleanIntList($list): string
|
||||
{
|
||||
return implode(',', GeneralUtility::intExplode(',', $list));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Service\Import;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Import Service interface
|
||||
*/
|
||||
interface DataProviderServiceInterface
|
||||
{
|
||||
public function getTotalRecordCount();
|
||||
|
||||
public function getImportData();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Service;
|
||||
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
class LinkHandlerTargetPageService
|
||||
{
|
||||
|
||||
/** @var ContentObjectRenderer */
|
||||
public $cObj;
|
||||
|
||||
public function process(string $content = '', array $configuration = []): int
|
||||
{
|
||||
$fallbackPageId = (int)($configuration['fallback'] ?? 0);
|
||||
|
||||
$newsId = (int)$this->cObj->stdWrapValue('news', $configuration, null);
|
||||
if ($newsId === 0) {
|
||||
return $fallbackPageId;
|
||||
}
|
||||
|
||||
$singlePid = $this->getSinglePidFromCategory($newsId);
|
||||
return $singlePid ?: $fallbackPageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a pid for the single view from the category.
|
||||
*
|
||||
* @param int $newsId
|
||||
* @return int
|
||||
*/
|
||||
protected function getSinglePidFromCategory(int $newsId): int
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category');
|
||||
$categoryRecord = $queryBuilder
|
||||
->select('title', 'single_pid')
|
||||
->from('sys_category')
|
||||
->leftJoin(
|
||||
'sys_category',
|
||||
'sys_category_record_mm',
|
||||
'sys_category_record_mm',
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.uid_local', $queryBuilder->quoteIdentifier('sys_category.uid'))
|
||||
)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->gt('sys_category.single_pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('sys_category_record_mm.sorting_foreign', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT))
|
||||
)
|
||||
->orderBy('sys_category_record_mm.sorting')
|
||||
->setMaxResults(1)
|
||||
->execute()->fetch();
|
||||
return (int)$categoryRecord['single_pid'];
|
||||
}
|
||||
}
|
||||
83
typo3conf/ext/news/Classes/Service/SettingsService.php
Normal file
83
typo3conf/ext/news/Classes/Service/SettingsService.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Service;
|
||||
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
/**
|
||||
* Provide a way to get the configuration just everywhere
|
||||
*
|
||||
* Example
|
||||
* $pluginSettingsService =
|
||||
* $this->objectManager->get('GeorgRinger\\News\\Service\\SettingsService');
|
||||
* \TYPO3\CMS\Core\Utility\GeneralUtility::print_array($pluginSettingsService->getSettings());
|
||||
*
|
||||
* If objectManager is not available:
|
||||
* http://forge.typo3.org/projects/typo3v4-mvc/wiki/
|
||||
* Dependency_Injection_%28DI%29#Creating-Prototype-Objects-through-the-Object-Manager
|
||||
*/
|
||||
class SettingsService
|
||||
{
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
|
||||
*/
|
||||
protected $configurationManager;
|
||||
|
||||
/**
|
||||
* Injects the Configuration Manager and loads the settings
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager An instance of the Configuration Manager
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function injectConfigurationManager(
|
||||
ConfigurationManagerInterface $configurationManager
|
||||
): void {
|
||||
$this->configurationManager = $configurationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSettings(): array
|
||||
{
|
||||
if ($this->settings === null) {
|
||||
$this->settings = $this->configurationManager->getConfiguration(
|
||||
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
|
||||
'News',
|
||||
'Pi1'
|
||||
);
|
||||
}
|
||||
return $this->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the settings at path $path, which is separated by ".",
|
||||
* e.g. "pages.uid".
|
||||
* "pages.uid" would return $this->settings['pages']['uid'].
|
||||
*
|
||||
* If the path is invalid or no entry is found, false is returned.
|
||||
*
|
||||
* @param string $path
|
||||
* @return mixed
|
||||
*/
|
||||
public function getByPath($path)
|
||||
{
|
||||
return ObjectAccess::getPropertyPath($this->getSettings(), $path);
|
||||
}
|
||||
}
|
||||
305
typo3conf/ext/news/Classes/Service/SlugService.php
Normal file
305
typo3conf/ext/news/Classes/Service/SlugService.php
Normal file
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Service;
|
||||
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
|
||||
use TYPO3\CMS\Core\DataHandling\SlugHelper;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
class SlugService
|
||||
{
|
||||
|
||||
/** @var NewsSlugHelper */
|
||||
protected $slugService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$fieldConfig = $GLOBALS['TCA']['tx_news_domain_model_news']['columns']['path_segment']['config'];
|
||||
$this->slugService = GeneralUtility::makeInstance(SlugHelper::class, 'tx_news_domain_model_news', 'path_segment', $fieldConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function countOfSlugUpdates(): int
|
||||
{
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_news_domain_model_news');
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$elementCount = $queryBuilder->count('uid')
|
||||
->from('tx_news_domain_model_news')
|
||||
->where(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq('path_segment', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->isNull('path_segment')
|
||||
)
|
||||
)
|
||||
->execute()->fetchColumn(0);
|
||||
|
||||
return $elementCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function performUpdates(): array
|
||||
{
|
||||
$databaseQueries = [];
|
||||
|
||||
/** @var Connection $connection */
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_news_domain_model_news');
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$statement = $queryBuilder->select('*')
|
||||
->from('tx_news_domain_model_news')
|
||||
->where(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq('path_segment', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)),
|
||||
$queryBuilder->expr()->isNull('path_segment')
|
||||
)
|
||||
)
|
||||
->execute();
|
||||
while ($record = $statement->fetch()) {
|
||||
if ((string)$record['title'] !== '') {
|
||||
$slug = $this->slugService->generate($record, $record['pid']);
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$queryBuilder->update('tx_news_domain_model_news')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($record['uid'], \PDO::PARAM_INT)
|
||||
)
|
||||
)
|
||||
->set('path_segment', $this->getUniqueValue($record['uid'], $record['sys_language_uid'], $slug));
|
||||
$databaseQueries[] = $queryBuilder->getSQL();
|
||||
$queryBuilder->execute();
|
||||
}
|
||||
}
|
||||
|
||||
return $databaseQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $uid
|
||||
* @param string $slug
|
||||
* @return string
|
||||
*/
|
||||
protected function getUniqueValue(int $uid, int $languageId, string $slug): string
|
||||
{
|
||||
$statement = $this->getUniqueCountStatement($uid, $languageId, $slug);
|
||||
if ($statement->fetchColumn()) {
|
||||
for ($counter = 1; $counter <= 100; $counter++) {
|
||||
$newSlug = $slug . '-' . $counter;
|
||||
$statement->bindValue(1, $newSlug);
|
||||
$statement->execute();
|
||||
if (!$statement->fetchColumn()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $newSlug ?? $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $uid
|
||||
* @param int $languageId
|
||||
* @param string $slug
|
||||
* @return \Doctrine\DBAL\Driver\Statement|int
|
||||
*/
|
||||
protected function getUniqueCountStatement(int $uid, int $languageId, string $slug)
|
||||
{
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_news_domain_model_news');
|
||||
/** @var DeletedRestriction $deleteRestriction */
|
||||
$deleteRestriction = GeneralUtility::makeInstance(DeletedRestriction::class);
|
||||
$queryBuilder->getRestrictions()->removeAll()->add($deleteRestriction);
|
||||
|
||||
return $queryBuilder
|
||||
->count('uid')
|
||||
->from('tx_news_domain_model_news')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'path_segment',
|
||||
$queryBuilder->createPositionalParameter($slug, \PDO::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'sys_language_uid',
|
||||
$queryBuilder->createPositionalParameter($languageId, \PDO::PARAM_INT)
|
||||
),
|
||||
$queryBuilder->expr()->neq('uid', $queryBuilder->createPositionalParameter($uid, \PDO::PARAM_INT))
|
||||
)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count valid entries from EXT:realurl table tx_realurl_uniqalias which can be migrated
|
||||
* Checks also for existance of third party extension table 'tx_realurl_uniqalias'
|
||||
* EXT:realurl requires not to be installed
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countOfRealurlAliasMigrations(): int
|
||||
{
|
||||
$elementCount = 0;
|
||||
// Check if table 'tx_realurl_uniqalias' exists
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_realurl_uniqalias');
|
||||
$schemaManager = $queryBuilder->getConnection()->getSchemaManager();
|
||||
if ($schemaManager->tablesExist(['tx_realurl_uniqalias']) === true) {
|
||||
// Count valid aliases for news
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$elementCount = $queryBuilder->selectLiteral('COUNT(DISTINCT tx_news_domain_model_news.uid)')
|
||||
->from('tx_realurl_uniqalias')
|
||||
->join(
|
||||
'tx_realurl_uniqalias',
|
||||
'tx_news_domain_model_news',
|
||||
'tx_news_domain_model_news',
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_realurl_uniqalias.value_id',
|
||||
$queryBuilder->quoteIdentifier('tx_news_domain_model_news.uid')
|
||||
)
|
||||
)
|
||||
->where(
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_news_domain_model_news.path_segment',
|
||||
$queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->isNull('tx_news_domain_model_news.path_segment')
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_news_domain_model_news.sys_language_uid',
|
||||
'tx_realurl_uniqalias.lang'
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_realurl_uniqalias.tablename',
|
||||
$queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_realurl_uniqalias.expire',
|
||||
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
|
||||
),
|
||||
$queryBuilder->expr()->gte(
|
||||
'tx_realurl_uniqalias.expire',
|
||||
$queryBuilder->createNamedParameter($GLOBALS['ACCESS_TIME'], \PDO::PARAM_INT)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->execute()->fetchColumn(0);
|
||||
}
|
||||
return $elementCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform migration of EXT:realurl unique alias into empty news slugs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function performRealurlAliasMigration(): array
|
||||
{
|
||||
$databaseQueries = [];
|
||||
|
||||
// Check if table 'tx_realurl_uniqalias' exists
|
||||
/** @var QueryBuilder $queryBuilderForRealurl */
|
||||
$queryBuilderForRealurl = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('tx_realurl_uniqalias');
|
||||
$schemaManager = $queryBuilderForRealurl->getConnection()->getSchemaManager();
|
||||
if ($schemaManager->tablesExist(['tx_realurl_uniqalias']) === true) {
|
||||
/** @var Connection $connection */
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getConnectionForTable('tx_news_domain_model_news');
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
|
||||
// Get entries to update
|
||||
$statement = $queryBuilder
|
||||
->selectLiteral(
|
||||
'DISTINCT tx_news_domain_model_news.uid, tx_realurl_uniqalias.value_alias, tx_news_domain_model_news.uid, tx_news_domain_model_news.l10n_parent,tx_news_domain_model_news.sys_language_uid'
|
||||
)
|
||||
->from('tx_news_domain_model_news')
|
||||
->join(
|
||||
'tx_news_domain_model_news',
|
||||
'tx_realurl_uniqalias',
|
||||
'tx_realurl_uniqalias',
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_news_domain_model_news.uid',
|
||||
$queryBuilder->quoteIdentifier('tx_realurl_uniqalias.value_id')
|
||||
),
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_news_domain_model_news.l10n_parent',
|
||||
$queryBuilder->quoteIdentifier('tx_realurl_uniqalias.value_id')
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_news_domain_model_news.sys_language_uid',
|
||||
$queryBuilder->quoteIdentifier('tx_realurl_uniqalias.lang')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->where(
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_news_domain_model_news.path_segment',
|
||||
$queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->isNull('tx_news_domain_model_news.path_segment')
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_news_domain_model_news.sys_language_uid',
|
||||
'tx_realurl_uniqalias.lang'
|
||||
),
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_realurl_uniqalias.tablename',
|
||||
$queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)
|
||||
),
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq(
|
||||
'tx_realurl_uniqalias.expire',
|
||||
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
|
||||
),
|
||||
$queryBuilder->expr()->gte(
|
||||
'tx_realurl_uniqalias.expire',
|
||||
$queryBuilder->createNamedParameter($GLOBALS['ACCESS_TIME'], \PDO::PARAM_INT)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->execute();
|
||||
|
||||
// Update entries
|
||||
while ($record = $statement->fetch()) {
|
||||
$slug = $this->slugService->sanitize((string)$record['value_alias']);
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$queryBuilder->update('tx_news_domain_model_news')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($record['uid'], \PDO::PARAM_INT)
|
||||
)
|
||||
)
|
||||
->set('path_segment', $this->getUniqueValue($record['uid'], $record['sys_language_uid'], $slug));
|
||||
$databaseQueries[] = $queryBuilder->getSQL();
|
||||
$queryBuilder->execute();
|
||||
}
|
||||
}
|
||||
|
||||
return $databaseQueries;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\TreeProvider;
|
||||
|
||||
use GeorgRinger\News\Service\CategoryService;
|
||||
use TYPO3\CMS\Backend\Tree\SortedTreeNodeCollection;
|
||||
use TYPO3\CMS\Backend\Tree\TreeNode;
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* TCA tree data provider which considers
|
||||
*/
|
||||
class DatabaseTreeDataProvider extends \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeDataProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Builds a complete node including children
|
||||
*
|
||||
* @param \TYPO3\CMS\Backend\Tree\TreeNode|\TYPO3\CMS\Backend\Tree\TreeNode $basicNode
|
||||
* @param \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode|null $parent
|
||||
* @param int $level
|
||||
* @param bool $restriction
|
||||
*
|
||||
* @return null|object node
|
||||
*/
|
||||
protected function buildRepresentationForNode(
|
||||
TreeNode $basicNode,
|
||||
DatabaseTreeNode $parent = null,
|
||||
$level = 0,
|
||||
$restriction = false
|
||||
): ?object {
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
/**@param $node \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode */
|
||||
$node = GeneralUtility::makeInstance(DatabaseTreeNode::class);
|
||||
$row = [];
|
||||
if ($basicNode->getId() == 0) {
|
||||
$node->setSelected(false);
|
||||
$node->setExpanded(true);
|
||||
$node->setLabel($GLOBALS['LANG']->sL($GLOBALS['TCA'][$this->tableName]['ctrl']['title']));
|
||||
} else {
|
||||
$row = BackendUtility::getRecordWSOL($this->tableName, $basicNode->getId(), '*', '', false);
|
||||
|
||||
if ($this->getLabelField() !== '') {
|
||||
$label = CategoryService::translateCategoryRecord($row[$this->getLabelField()], $row);
|
||||
$node->setLabel($label);
|
||||
} else {
|
||||
$node->setLabel($basicNode->getId());
|
||||
}
|
||||
$node->setSelected(GeneralUtility::inList($this->getSelectedList(), $basicNode->getId()));
|
||||
$node->setExpanded($this->isExpanded($basicNode));
|
||||
$node->setLabel($node->getLabel());
|
||||
}
|
||||
|
||||
$node->setId($basicNode->getId());
|
||||
|
||||
// Break to force single category activation
|
||||
if ($parent != null && $level != 0 && $this->isSingleCategoryAclActivated() && !$this->isCategoryAllowed($node)) {
|
||||
return null;
|
||||
}
|
||||
$node->setSelectable(!GeneralUtility::inList(
|
||||
$this->getNonSelectableLevelList(),
|
||||
$level
|
||||
) && !in_array($basicNode->getId(), $this->getItemUnselectableList()));
|
||||
if (!empty($this->nodeSortValues)) {
|
||||
$node->setSortValue($this->nodeSortValues[$basicNode->getId()]);
|
||||
}
|
||||
$node->setIcon($iconFactory->getIconForRecord($this->tableName, $row, Icon::SIZE_SMALL));
|
||||
$node->setParentNode($parent);
|
||||
if ($basicNode->hasChildNodes()) {
|
||||
$node->setHasChildren(true);
|
||||
$childNodes = GeneralUtility::makeInstance(SortedTreeNodeCollection::class);
|
||||
$foundSomeChild = false;
|
||||
foreach ($basicNode->getChildNodes() as $child) {
|
||||
// Change in custom TreeDataProvider by adding the if clause
|
||||
if ($restriction || $this->isCategoryAllowed($child)) {
|
||||
$returnedChild = $this->buildRepresentationForNode($child, $node, $level + 1, true);
|
||||
|
||||
if (!is_null($returnedChild)) {
|
||||
$foundSomeChild = true;
|
||||
$childNodes->append($returnedChild);
|
||||
} else {
|
||||
$node->setParentNode(null);
|
||||
$node->setHasChildren(false);
|
||||
}
|
||||
}
|
||||
// Change in custom TreeDataProvider end
|
||||
}
|
||||
|
||||
if ($foundSomeChild) {
|
||||
$node->setChildNodes($childNodes);
|
||||
}
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given category is allowed by the access rights
|
||||
*
|
||||
* @param \TYPO3\CMS\Backend\Tree\TreeNode $child
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCategoryAllowed($child): bool
|
||||
{
|
||||
$mounts = $GLOBALS['BE_USER']->getCategoryMountPoints();
|
||||
if (empty($mounts)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array($child->getId(), $mounts);
|
||||
}
|
||||
|
||||
/**
|
||||
* By setting "tx_news.singleCategoryAcl = 1" in UserTsConfig
|
||||
* every category needs to be activated, no recursive enabling
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSingleCategoryAclActivated(): bool
|
||||
{
|
||||
$userTsConfig = $GLOBALS['BE_USER']->getTSConfig();
|
||||
if (is_array($userTsConfig['tx_news.'])
|
||||
&& $userTsConfig['tx_news.']['singleCategoryAcl'] === '1'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
84
typo3conf/ext/news/Classes/Updates/NewsSlugUpdater.php
Normal file
84
typo3conf/ext/news/Classes/Updates/NewsSlugUpdater.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Updates;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use GeorgRinger\News\Service\SlugService;
|
||||
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
|
||||
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
|
||||
|
||||
/**
|
||||
* Migrate empty slugs
|
||||
*/
|
||||
class NewsSlugUpdater implements UpgradeWizardInterface
|
||||
{
|
||||
const TABLE = 'tx_news_domain_model_news';
|
||||
|
||||
/** @var SlugService */
|
||||
protected $slugService;
|
||||
|
||||
/**
|
||||
* NewsSlugUpdater constructor.
|
||||
* @param SlugService $slugService
|
||||
*/
|
||||
public function __construct(
|
||||
SlugService $slugService
|
||||
) {
|
||||
$this->slugService = $slugService;
|
||||
}
|
||||
|
||||
public function executeUpdate(): bool
|
||||
{
|
||||
$this->slugService->performUpdates();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updateNecessary(): bool
|
||||
{
|
||||
$elementCount = $this->slugService->countOfSlugUpdates();
|
||||
|
||||
return $elementCount > 0;
|
||||
}
|
||||
|
||||
public function getPrerequisites(): array
|
||||
{
|
||||
return [
|
||||
DatabaseUpdatedPrerequisite::class
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Updates slug field "path_segment" of EXT:news records';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description
|
||||
*
|
||||
* @return string Longer description of this updater
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Fills empty slug field "path_segment" of EXT:news records with urlized title.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Unique identifier of this updater
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'newsSlug';
|
||||
}
|
||||
}
|
||||
187
typo3conf/ext/news/Classes/Updates/PopulateCategorySlugs.php
Normal file
187
typo3conf/ext/news/Classes/Updates/PopulateCategorySlugs.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace GeorgRinger\News\Updates;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
|
||||
use TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory;
|
||||
use TYPO3\CMS\Core\DataHandling\SlugHelper;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
|
||||
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
|
||||
|
||||
/**
|
||||
* Fills sys_category.slug with a proper value
|
||||
*/
|
||||
class PopulateCategorySlugs implements UpgradeWizardInterface
|
||||
{
|
||||
protected $table = 'sys_category';
|
||||
|
||||
protected $fieldName = 'slug';
|
||||
|
||||
/**
|
||||
* @return string Unique identifier of this updater
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'sysCategorySlugs';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Title of this updater
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Introduce URL parts ("slugs") to all sys_categories';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Longer description of this updater
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Slugs for sys_category records';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether updates are required.
|
||||
*
|
||||
* @return bool Whether an update is required (TRUE) or not (FALSE)
|
||||
*/
|
||||
public function updateNecessary(): bool
|
||||
{
|
||||
$updateNeeded = false;
|
||||
// Check if the database table even exists
|
||||
if ($this->checkIfWizardIsRequired()) {
|
||||
$updateNeeded = true;
|
||||
}
|
||||
return $updateNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] All new fields and tables must exist
|
||||
*/
|
||||
public function getPrerequisites(): array
|
||||
{
|
||||
return [
|
||||
DatabaseUpdatedPrerequisite::class
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the accordant updates.
|
||||
*
|
||||
* @return bool Whether everything went smoothly or not
|
||||
*/
|
||||
public function executeUpdate(): bool
|
||||
{
|
||||
$this->populateSlugs();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the database table with slugs based on the page title and its configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function populateSlugs(): void
|
||||
{
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
$statement = $queryBuilder
|
||||
->select('*')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
|
||||
$queryBuilder->expr()->isNull($this->fieldName)
|
||||
)
|
||||
)
|
||||
// Ensure that live workspace records are handled first
|
||||
->addOrderBy('t3ver_wsid', 'asc')
|
||||
->addOrderBy('pid', 'asc')
|
||||
->addOrderBy('sorting', 'asc')
|
||||
->execute();
|
||||
|
||||
// Check for existing slugs from realurl
|
||||
$suggestedSlugs = [];
|
||||
|
||||
$fieldConfig = $GLOBALS['TCA'][$this->table]['columns'][$this->fieldName]['config'];
|
||||
$evalInfo = !empty($fieldConfig['eval']) ? GeneralUtility::trimExplode(',', $fieldConfig['eval'], true) : [];
|
||||
$hasToBeUniqueInSite = in_array('uniqueInSite', $evalInfo, true);
|
||||
$hasToBeUniqueInPid = in_array('uniqueInPid', $evalInfo, true);
|
||||
$slugHelper = GeneralUtility::makeInstance(SlugHelper::class, $this->table, $this->fieldName, $fieldConfig);
|
||||
|
||||
while ($record = $statement->fetch()) {
|
||||
$recordId = (int)$record['uid'];
|
||||
$pid = (int)$record['pid'];
|
||||
$languageId = (int)$record['sys_language_uid'];
|
||||
$recordInDefaultLanguage = $languageId > 0 ? (int)$record['l10n_parent'] : $recordId;
|
||||
$slug = $suggestedSlugs[$recordInDefaultLanguage][$languageId] ?? '';
|
||||
|
||||
if (empty($slug)) {
|
||||
if ($pid === -1) {
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
$liveVersion = $queryBuilder
|
||||
->select('pid')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($record['t3ver_oid'], \PDO::PARAM_INT))
|
||||
)->execute()->fetch();
|
||||
$pid = (int)$liveVersion['pid'];
|
||||
}
|
||||
$slug = $slugHelper->generate($record, $pid);
|
||||
}
|
||||
|
||||
$state = RecordStateFactory::forName($this->table)
|
||||
->fromArray($record, $pid, $recordId);
|
||||
if ($hasToBeUniqueInSite && !$slugHelper->isUniqueInSite($slug, $state)) {
|
||||
$slug = $slugHelper->buildSlugForUniqueInSite($slug, $state);
|
||||
}
|
||||
if ($hasToBeUniqueInPid && !$slugHelper->isUniqueInPid($slug, $state)) {
|
||||
$slug = $slugHelper->buildSlugForUniqueInPid($slug, $state);
|
||||
}
|
||||
|
||||
$connection->update(
|
||||
$this->table,
|
||||
[$this->fieldName => $slug],
|
||||
['uid' => $recordId]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are record within database table with an empty "slug" field.
|
||||
*
|
||||
* @return bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function checkIfWizardIsRequired(): bool
|
||||
{
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
|
||||
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
|
||||
$numberOfEntries = $queryBuilder
|
||||
->count('uid')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
|
||||
$queryBuilder->expr()->isNull($this->fieldName)
|
||||
)
|
||||
)
|
||||
->execute()
|
||||
->fetchColumn();
|
||||
return $numberOfEntries > 0;
|
||||
}
|
||||
}
|
||||
187
typo3conf/ext/news/Classes/Updates/PopulateTagSlugs.php
Normal file
187
typo3conf/ext/news/Classes/Updates/PopulateTagSlugs.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace GeorgRinger\News\Updates;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
|
||||
use TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory;
|
||||
use TYPO3\CMS\Core\DataHandling\SlugHelper;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
|
||||
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
|
||||
|
||||
/**
|
||||
* Fills tx_news_domain_model_tag.slug with a proper value
|
||||
*/
|
||||
class PopulateTagSlugs implements UpgradeWizardInterface
|
||||
{
|
||||
protected $table = 'tx_news_domain_model_tag';
|
||||
|
||||
protected $fieldName = 'slug';
|
||||
|
||||
/**
|
||||
* @return string Unique identifier of this updater
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'txNewsTagSlugs';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Title of this updater
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Introduce URL parts ("slugs") to all tags of EXT:news';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Longer description of this updater
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Slugs for EXT:news tag records';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether updates are required.
|
||||
*
|
||||
* @return bool Whether an update is required (TRUE) or not (FALSE)
|
||||
*/
|
||||
public function updateNecessary(): bool
|
||||
{
|
||||
$updateNeeded = false;
|
||||
// Check if the database table even exists
|
||||
if ($this->checkIfWizardIsRequired()) {
|
||||
$updateNeeded = true;
|
||||
}
|
||||
return $updateNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] All new fields and tables must exist
|
||||
*/
|
||||
public function getPrerequisites(): array
|
||||
{
|
||||
return [
|
||||
DatabaseUpdatedPrerequisite::class
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the accordant updates.
|
||||
*
|
||||
* @return bool Whether everything went smoothly or not
|
||||
*/
|
||||
public function executeUpdate(): bool
|
||||
{
|
||||
$this->populateSlugs();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the database table with slugs based on the page title and its configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function populateSlugs(): void
|
||||
{
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
$statement = $queryBuilder
|
||||
->select('*')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
|
||||
$queryBuilder->expr()->isNull($this->fieldName)
|
||||
)
|
||||
)
|
||||
// Ensure that live workspace records are handled first
|
||||
->addOrderBy('t3ver_wsid', 'asc')
|
||||
->addOrderBy('pid', 'asc')
|
||||
->addOrderBy('sorting', 'asc')
|
||||
->execute();
|
||||
|
||||
// Check for existing slugs from realurl
|
||||
$suggestedSlugs = [];
|
||||
|
||||
$fieldConfig = $GLOBALS['TCA'][$this->table]['columns'][$this->fieldName]['config'];
|
||||
$evalInfo = !empty($fieldConfig['eval']) ? GeneralUtility::trimExplode(',', $fieldConfig['eval'], true) : [];
|
||||
$hasToBeUniqueInSite = in_array('uniqueInSite', $evalInfo, true);
|
||||
$hasToBeUniqueInPid = in_array('uniqueInPid', $evalInfo, true);
|
||||
$slugHelper = GeneralUtility::makeInstance(SlugHelper::class, $this->table, $this->fieldName, $fieldConfig);
|
||||
|
||||
while ($record = $statement->fetch()) {
|
||||
$recordId = (int)$record['uid'];
|
||||
$pid = (int)$record['pid'];
|
||||
$languageId = (int)$record['sys_language_uid'];
|
||||
$recordInDefaultLanguage = $languageId > 0 ? (int)$record['l10n_parent'] : $recordId;
|
||||
$slug = $suggestedSlugs[$recordInDefaultLanguage][$languageId] ?? '';
|
||||
|
||||
if (empty($slug)) {
|
||||
if ($pid === -1) {
|
||||
$queryBuilder = $connection->createQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
$liveVersion = $queryBuilder
|
||||
->select('pid')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($record['t3ver_oid'], \PDO::PARAM_INT))
|
||||
)->execute()->fetch();
|
||||
$pid = (int)$liveVersion['pid'];
|
||||
}
|
||||
$slug = $slugHelper->generate($record, $pid);
|
||||
}
|
||||
|
||||
$state = RecordStateFactory::forName($this->table)
|
||||
->fromArray($record, $pid, $recordId);
|
||||
if ($hasToBeUniqueInSite && !$slugHelper->isUniqueInSite($slug, $state)) {
|
||||
$slug = $slugHelper->buildSlugForUniqueInSite($slug, $state);
|
||||
}
|
||||
if ($hasToBeUniqueInPid && !$slugHelper->isUniqueInPid($slug, $state)) {
|
||||
$slug = $slugHelper->buildSlugForUniqueInPid($slug, $state);
|
||||
}
|
||||
|
||||
$connection->update(
|
||||
$this->table,
|
||||
[$this->fieldName => $slug],
|
||||
['uid' => $recordId]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are record within database table with an empty "slug" field.
|
||||
*
|
||||
* @return bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function checkIfWizardIsRequired(): bool
|
||||
{
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
|
||||
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
|
||||
$numberOfEntries = $queryBuilder
|
||||
->count('uid')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
|
||||
$queryBuilder->expr()->isNull($this->fieldName)
|
||||
)
|
||||
)
|
||||
->execute()
|
||||
->fetchColumn();
|
||||
return $numberOfEntries > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeorgRinger\News\Updates;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use GeorgRinger\News\Service\SlugService;
|
||||
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
|
||||
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
|
||||
|
||||
/**
|
||||
* Migrate EXT:realurl unique alias into empty news slugs
|
||||
*
|
||||
* If a lot of similar titles are used it might be a good a idea
|
||||
* to migrate the unique aliases from realurl to be sure that the same alias is used
|
||||
*
|
||||
* Requires existence of DB table tx_realurl_uniqalias, but EXT:realurl requires not to be installed
|
||||
* Will only appear if missing slugs found between realurl and news, respecting language and expire date from realurl
|
||||
* Copies values from 'tx_realurl_uniqalias.value_alias' to 'tx_news_domain_model_news.path_segment'
|
||||
*/
|
||||
class RealurlAliasNewsSlugUpdater implements UpgradeWizardInterface
|
||||
{
|
||||
const TABLE = 'tx_news_domain_model_news';
|
||||
|
||||
/** @var SlugService */
|
||||
protected $slugService;
|
||||
|
||||
/**
|
||||
* RealurlAliasNewsSlugUpdater constructor.
|
||||
* @param SlugService $slugService
|
||||
*/
|
||||
public function __construct(
|
||||
SlugService $slugService
|
||||
) {
|
||||
$this->slugService = $slugService;
|
||||
}
|
||||
|
||||
public function executeUpdate(): bool
|
||||
{
|
||||
// user decided to migrate, migrate and mark wizard as done
|
||||
$queries = $this->slugService->performRealurlAliasMigration();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updateNecessary(): bool
|
||||
{
|
||||
$updateNeeded = false;
|
||||
$elementCount = $this->slugService->countOfRealurlAliasMigrations();
|
||||
if ($elementCount > 0) {
|
||||
$updateNeeded = true;
|
||||
}
|
||||
return $updateNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] All new fields and tables must exist
|
||||
*/
|
||||
public function getPrerequisites(): array
|
||||
{
|
||||
return [
|
||||
DatabaseUpdatedPrerequisite::class
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return '[Optional] Migrate realurl alias to slug field "path_segment" of EXT:news records';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description
|
||||
*
|
||||
* @return string Longer description of this updater
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Migrates EXT:realurl unique alias values into empty slug field "path_segment" of EXT:news records.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Unique identifier of this updater
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'realurlAliasNewsSlug';
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step: Ask user to install the extensions
|
||||
*
|
||||
* @param string $inputPrefix input prefix, all names of form fields have to start with this. Append custom name in [ ... ]
|
||||
* @return string HTML output
|
||||
*/
|
||||
public function getUserInput($inputPrefix): string
|
||||
{
|
||||
return '
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading">Are you really sure?</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
You can migrate EXT:realurl unique alias into news slugs,
|
||||
to ensure that the same alias is used if similar news titles are used.
|
||||
</p>
|
||||
<p>
|
||||
This wizard migrates only matching realurl alias for news entries, where path_segment is empty.
|
||||
Requires database table "tx_realurl_uniqalias" from EXT:realurl, but EXT:realurl requires not to be installed.
|
||||
</p>
|
||||
<p>
|
||||
Cause only empty news slugs will be generated within this migration,
|
||||
you may decide to empty all news slugs before.
|
||||
</p>
|
||||
<p>
|
||||
The result of this migration can still left empty slugs fields for news entries.
|
||||
Therfore you should generate these slugs afterwards using the news slug upater wizard.
|
||||
</p>
|
||||
<div class="btn-group clearfix" data-toggle="buttons">
|
||||
<label class="btn btn-default active">
|
||||
<input type="radio" name="' . $inputPrefix . '[install]" value="0" checked="checked" /> No, don\'t migrate
|
||||
</label>
|
||||
<label class="btn btn-default">
|
||||
<input type="radio" name="' . $inputPrefix . '[install]" value="1" /> Yes, please migrate
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
';
|
||||
}
|
||||
}
|
||||
101
typo3conf/ext/news/Classes/Utility/Cache.php
Normal file
101
typo3conf/ext/news/Classes/Utility/Cache.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
use GeorgRinger\News\Domain\Model\Dto\NewsDemand;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Cache Utility class
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
|
||||
/**
|
||||
* Stack for processed cObjs which has added news relevant cache tags.
|
||||
* @var array
|
||||
*/
|
||||
protected static $processedContentRecords = [];
|
||||
|
||||
/**
|
||||
* Marks as cObj as processed.
|
||||
*
|
||||
* @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $cObj
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function markContentRecordAsProcessed(ContentObjectRenderer $cObj): void
|
||||
{
|
||||
$key = 'tt_content_' . $cObj->data['uid'];
|
||||
self::$processedContentRecords[$key] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a cObj has already added cache tags.
|
||||
*
|
||||
* @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $cObj
|
||||
* @return bool
|
||||
*/
|
||||
public function isContentRecordAlreadyProcessed(ContentObjectRenderer $cObj): bool
|
||||
{
|
||||
$key = 'tt_content_' . $cObj->data['uid'];
|
||||
return array_key_exists($key, self::$processedContentRecords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds cache tags to page cache by news-records.
|
||||
*
|
||||
* Following cache tags will be added to tsfe:
|
||||
* "tx_news_uid_[news:uid]"
|
||||
*
|
||||
* @param array|\TYPO3\CMS\Extbase\Persistence\QueryResult $newsRecords with news records
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function addCacheTagsByNewsRecords($newsRecords): void
|
||||
{
|
||||
$cacheTags = [];
|
||||
foreach ($newsRecords as $news) {
|
||||
// cache tag for each news record
|
||||
$cacheTags[] = 'tx_news_uid_' . $news->getUid();
|
||||
|
||||
if ($news->_getProperty('_localizedUid')) {
|
||||
$cacheTags[] = 'tx_news_uid_' . $news->_getProperty('_localizedUid');
|
||||
}
|
||||
}
|
||||
if (count($cacheTags) > 0) {
|
||||
$GLOBALS['TSFE']->addCacheTags($cacheTags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds page cache tags by used storagePages.
|
||||
* This adds tags with the scheme tx_news_pid_[news:pid]
|
||||
*
|
||||
* @param \GeorgRinger\News\Domain\Model\Dto\NewsDemand $demand
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function addPageCacheTagsByDemandObject(NewsDemand $demand): void
|
||||
{
|
||||
$cacheTags = [];
|
||||
if ($demand->getStoragePage()) {
|
||||
// Add cache tags for each storage page
|
||||
foreach (GeneralUtility::trimExplode(',', $demand->getStoragePage()) as $pageId) {
|
||||
$cacheTags[] = 'tx_news_pid_' . $pageId;
|
||||
}
|
||||
} else {
|
||||
$cacheTags[] = 'tx_news_domain_model_news';
|
||||
}
|
||||
if (count($cacheTags) > 0) {
|
||||
$GLOBALS['TSFE']->addCacheTags($cacheTags);
|
||||
}
|
||||
}
|
||||
}
|
||||
227
typo3conf/ext/news/Classes/Utility/ClassCacheManager.php
Normal file
227
typo3conf/ext/news/Classes/Utility/ClassCacheManager.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class ClassCacheManager
|
||||
*/
|
||||
class ClassCacheManager
|
||||
{
|
||||
/**
|
||||
* Cache instance
|
||||
*
|
||||
* @var PhpFrontend
|
||||
*/
|
||||
protected $classCache;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $constructorLines = [];
|
||||
|
||||
/**
|
||||
* @param PhpFrontend $classCache
|
||||
*/
|
||||
public function __construct(PhpFrontend $classCache = null)
|
||||
{
|
||||
if ($classCache === null) {
|
||||
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
|
||||
if (!$cacheManager->hasCache('news')) {
|
||||
$cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
|
||||
}
|
||||
$this->classCache = $cacheManager->getCache('news');
|
||||
} else {
|
||||
$this->classCache = $classCache;
|
||||
}
|
||||
}
|
||||
|
||||
public function reBuildSimple()
|
||||
{
|
||||
$classPath = 'Classes/';
|
||||
|
||||
if (!function_exists('token_get_all')) {
|
||||
throw new \Exception(('The function token_get_all must exist. Please install the module PHP Module Tokenizer'));
|
||||
}
|
||||
|
||||
if (!isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['classes'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$files = GeneralUtility::getFilesInDir(Environment::getVarPath() . '/cache/code/news/', 'php');
|
||||
if (empty($files)) {
|
||||
$this->reBuild();
|
||||
}
|
||||
}
|
||||
|
||||
public function reBuild()
|
||||
{
|
||||
$classPath = 'Classes/';
|
||||
|
||||
if (!function_exists('token_get_all')) {
|
||||
throw new \Exception(('The function token_get_all must exist. Please install the module PHP Module Tokenizer'));
|
||||
}
|
||||
|
||||
if (!isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['classes'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['classes'] as $key => $extensionsWithThisClass) {
|
||||
$this->constructorLines = [];
|
||||
$extendingClassFound = false;
|
||||
|
||||
$path = ExtensionManagementUtility::extPath('news') . $classPath . $key . '.php';
|
||||
if (!is_file($path)) {
|
||||
throw new \Exception('Given file "' . $path . '" does not exist');
|
||||
}
|
||||
$code = $this->parseSingleFile($path, true);
|
||||
|
||||
// Get the files from all other extensions
|
||||
foreach (array_unique($extensionsWithThisClass) as $extensionKey) {
|
||||
$path = ExtensionManagementUtility::extPath($extensionKey) . $classPath . $key . '.php';
|
||||
if (is_file($path)) {
|
||||
$extendingClassFound = true;
|
||||
$code .= $this->parseSingleFile($path, false);
|
||||
}
|
||||
}
|
||||
if (isset($this->constructorLines['code']) && count($this->constructorLines['code'])) {
|
||||
$code .= LF . implode("\n", $this->constructorLines['doc']);
|
||||
$code .= LF . ' public function __construct(' . implode(',', $this->constructorLines['parameters'] ?? []) . ')' . LF . ' {' . LF . implode(LF, $this->constructorLines['code'] ?? []) . LF . ' }' . LF;
|
||||
}
|
||||
$code = $this->closeClassDefinition($code);
|
||||
|
||||
// If an extending class is found, the file is written and
|
||||
// added to the autoloader info
|
||||
if ($extendingClassFound) {
|
||||
$cacheEntryIdentifier = 'tx_news_' . strtolower(str_replace('/', '_', $key));
|
||||
try {
|
||||
$this->classCache->set($cacheEntryIdentifier, $code);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single file and does some magic
|
||||
* - Remove the <?php tags
|
||||
* - Remove the class definition (if set)
|
||||
*
|
||||
* @param string $filePath path of the file
|
||||
* @param bool $baseClass If class definition should be removed
|
||||
* @return string path of the saved file
|
||||
* @throws \Exception
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function parseSingleFile($filePath, $baseClass = false): string
|
||||
{
|
||||
if (!is_file($filePath)) {
|
||||
throw new \InvalidArgumentException(sprintf('File "%s" could not be found', $filePath));
|
||||
}
|
||||
$code = GeneralUtility::getUrl($filePath);
|
||||
|
||||
$classParser = GeneralUtility::makeInstance(ClassParser::class);
|
||||
$classParser->parse($filePath);
|
||||
$classParserInformation = $classParser->getFirstClass();
|
||||
|
||||
$code = str_replace('<?php', '', $code);
|
||||
$codeInLines = explode(LF, str_replace(CR, '', $code));
|
||||
$offsetForInnerPart = 0;
|
||||
|
||||
if ($baseClass) {
|
||||
$innerPart = $codeInLines;
|
||||
} else {
|
||||
$offsetForInnerPart = $classParserInformation['start'];
|
||||
if (isset($classParserInformation['eol'])) {
|
||||
$innerPart = array_slice(
|
||||
$codeInLines,
|
||||
$classParserInformation['start'],
|
||||
($classParserInformation['eol'] - $classParserInformation['start'] - 1)
|
||||
);
|
||||
} else {
|
||||
$innerPart = array_slice($codeInLines, $classParserInformation['start']);
|
||||
}
|
||||
}
|
||||
|
||||
if (trim($innerPart[0]) === '{') {
|
||||
unset($innerPart[0]);
|
||||
}
|
||||
|
||||
$innerPartLine = function ($line) use ($offsetForInnerPart) {
|
||||
return $line - $offsetForInnerPart;
|
||||
};
|
||||
|
||||
// unset the constructor and save it's lines
|
||||
if (isset($classParserInformation['functions']['__construct'])) {
|
||||
$constructorInfo = $classParserInformation['functions']['__construct'];
|
||||
$constructorInfo['inner_start'] = $constructorInfo['start'] - $offsetForInnerPart;
|
||||
$constructorInfo['inner_end'] = $constructorInfo['end'] - $offsetForInnerPart;
|
||||
if ($baseClass) {
|
||||
$this->constructorLines['doc'] = explode("\n", $constructorInfo['doc']);
|
||||
} else {
|
||||
array_splice(
|
||||
$this->constructorLines['doc'],
|
||||
-1,
|
||||
0,
|
||||
array_filter(explode("\n", $constructorInfo['doc'] ?? ''), function ($value) {
|
||||
return strpos($value, '@param') !== false;
|
||||
})
|
||||
);
|
||||
}
|
||||
$codePart = false;
|
||||
for ($i = $constructorInfo['inner_start']; $i < $constructorInfo['inner_end']; $i++) {
|
||||
if ($codePart) {
|
||||
$this->constructorLines['code'][] = $innerPart[$i];
|
||||
} elseif (trim($innerPart[$i]) === ') {' || trim($innerPart[$i]) === '{') {
|
||||
$codePart = true;
|
||||
} elseif (trim($innerPart[$i]) !== ')' && $i >= ($constructorInfo['inner_start'])) {
|
||||
$this->constructorLines['parameters'][] = LF . rtrim($innerPart[$i], ',');
|
||||
}
|
||||
unset($innerPart[$i]);
|
||||
}
|
||||
unset($innerPart[$constructorInfo['inner_start'] - 1]);
|
||||
unset($innerPart[$constructorInfo['inner_end']]);
|
||||
}
|
||||
|
||||
$codePart = implode(LF, $innerPart);
|
||||
$closingBracket = strrpos($codePart, '}');
|
||||
$codePart = substr($codePart, 0, $closingBracket);
|
||||
|
||||
return $this->getPartialInfo($filePath) . $codePart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath
|
||||
* @return string
|
||||
*/
|
||||
protected function getPartialInfo($filePath): string
|
||||
{
|
||||
return LF . '/*' . str_repeat('*', 70) . LF . "\t" .
|
||||
'this is partial from: ' . LF . "\t" . str_replace(Environment::getPublicPath(), '', $filePath) . LF . str_repeat(
|
||||
'*',
|
||||
70
|
||||
) . '*/' . LF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
* @return string
|
||||
*/
|
||||
protected function closeClassDefinition($code): string
|
||||
{
|
||||
return $code . LF . '}';
|
||||
}
|
||||
}
|
||||
153
typo3conf/ext/news/Classes/Utility/ClassLoader.php
Normal file
153
typo3conf/ext/news/Classes/Utility/ClassLoader.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
|
||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ClassLoader
|
||||
*/
|
||||
class ClassLoader implements SingletonInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var PhpFrontend
|
||||
*/
|
||||
protected $classCache;
|
||||
|
||||
/** @var ClassCacheManager */
|
||||
protected $classCacheManager;
|
||||
|
||||
/** @var bool */
|
||||
protected $isValidInstance = false;
|
||||
|
||||
/**
|
||||
* ClassLoader constructor.
|
||||
*
|
||||
* @param PhpFrontend $classCache
|
||||
*/
|
||||
public function __construct(PhpFrontend $classCache = null, ClassCacheManager $classCacheManager = null)
|
||||
{
|
||||
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class);
|
||||
if ($versionInformation->getMajorVersion() === 10) {
|
||||
// Use DI
|
||||
// something might fail, e.g loading checks in Install Tool
|
||||
if ($classCacheManager !== null) {
|
||||
$this->classCacheManager = $classCacheManager;
|
||||
$this->isValidInstance = true;
|
||||
}
|
||||
} else {
|
||||
$this->classCacheManager = GeneralUtility::makeInstance(ClassCacheManager::class);
|
||||
$this->isValidInstance = true;
|
||||
}
|
||||
|
||||
if ($this->isValidInstance) {
|
||||
if ($classCache === null) {
|
||||
$this->classCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('news');
|
||||
} else {
|
||||
$this->classCache = $classCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register instance of this class as spl autoloader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function registerAutoloader(): void
|
||||
{
|
||||
spl_autoload_register([GeneralUtility::makeInstance(self::class), 'loadClass'], true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads php files containing classes or interfaces part of the
|
||||
* classes directory of an extension.
|
||||
*
|
||||
* @param string $className Name of the class/interface to load
|
||||
* @return bool
|
||||
*/
|
||||
public function loadClass($className): bool
|
||||
{
|
||||
if (!$this->isValidInstance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$className = ltrim($className, '\\');
|
||||
|
||||
if (!$this->isValidClassName($className)) {
|
||||
return false;
|
||||
}
|
||||
$cacheEntryIdentifier = 'tx_news_' . strtolower(str_replace('/', '_', $this->changeClassName($className)));
|
||||
|
||||
if (!$this->classCache->has($cacheEntryIdentifier)) {
|
||||
$this->classCacheManager->reBuild();
|
||||
}
|
||||
|
||||
$this->classCache->requireOnce($cacheEntryIdentifier);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension key from namespaced classname
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getExtensionKey($className): ?string
|
||||
{
|
||||
$extensionKey = null;
|
||||
|
||||
if (strpos($className, '\\') !== false) {
|
||||
$namespaceParts = GeneralUtility::trimExplode(
|
||||
'\\',
|
||||
$className,
|
||||
0,
|
||||
(substr($className, 0, 9) === 'TYPO3\\CMS' ? 4 : 3)
|
||||
);
|
||||
array_pop($namespaceParts);
|
||||
$extensionKey = GeneralUtility::camelCaseToLowerCaseUnderscored(array_pop($namespaceParts));
|
||||
}
|
||||
|
||||
return $extensionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if a class name is valid
|
||||
*
|
||||
* @param string $className
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidClassName($className): bool
|
||||
{
|
||||
if ($this->isFirstPartOfStr($className, 'GeorgRinger\\News\\Domain\\') || $this->isFirstPartOfStr($className, 'GeorgRinger\\News\\Controller\\')) {
|
||||
$modifiedClassName = $this->changeClassName($className);
|
||||
if (isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['classes'][$modifiedClassName])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isFirstPartOfStr(string $str, string $partStr): bool
|
||||
{
|
||||
return $partStr !== '' && strpos($str, $partStr) === 0;
|
||||
}
|
||||
|
||||
protected function changeClassName(string $className): string
|
||||
{
|
||||
return str_replace('\\', '/', str_replace('GeorgRinger\\News\\', '', $className));
|
||||
}
|
||||
}
|
||||
175
typo3conf/ext/news/Classes/Utility/ClassParser.php
Normal file
175
typo3conf/ext/news/Classes/Utility/ClassParser.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
class ClassParser
|
||||
{
|
||||
private $classes = [];
|
||||
private $extends = [];
|
||||
private $implements = [];
|
||||
|
||||
const STATE_CLASS_HEAD = 100001;
|
||||
const STATE_FUNCTION_HEAD = 100002;
|
||||
|
||||
public function getClasses()
|
||||
{
|
||||
return $this->classes;
|
||||
}
|
||||
|
||||
public function getFirstClass()
|
||||
{
|
||||
return array_shift($this->classes);
|
||||
}
|
||||
|
||||
public function getClassesImplementing($interface): array
|
||||
{
|
||||
$implementers = [];
|
||||
if (isset($this->implements[$interface])) {
|
||||
foreach ($this->implements[$interface] as $name) {
|
||||
$implementers[$name] = $this->classes[$name];
|
||||
}
|
||||
}
|
||||
return $implementers;
|
||||
}
|
||||
|
||||
public function getClassesExtending($class): array
|
||||
{
|
||||
$extenders = [];
|
||||
if (isset($this->extends[$class])) {
|
||||
foreach ($this->extends[$class] as $name) {
|
||||
$extenders[$name] = $this->classes[$name];
|
||||
}
|
||||
}
|
||||
return $extenders;
|
||||
}
|
||||
|
||||
public function parse($file): void
|
||||
{
|
||||
$file = realpath($file);
|
||||
$tokens = token_get_all(file_get_contents($file));
|
||||
$classes = [];
|
||||
$clsc = 0;
|
||||
|
||||
$si = null;
|
||||
$depth = 0;
|
||||
$mod = [];
|
||||
$doc = null;
|
||||
$state = null;
|
||||
$inFunction = false;
|
||||
$functionName = '';
|
||||
$lastLine = 0;
|
||||
foreach ($tokens as $idx => &$token) {
|
||||
if (is_array($token)) {
|
||||
switch ($token[0]) {
|
||||
case T_DOC_COMMENT:
|
||||
$doc = $token[1];
|
||||
break;
|
||||
case T_PUBLIC:
|
||||
case T_PRIVATE:
|
||||
case T_ABSTRACT:
|
||||
case T_PROTECTED:
|
||||
$mod[] = $token[1];
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_FUNCTION:
|
||||
$state = $token[0];
|
||||
break;
|
||||
case T_EXTENDS:
|
||||
case T_IMPLEMENTS:
|
||||
switch ($state) {
|
||||
case self::STATE_CLASS_HEAD:
|
||||
case T_EXTENDS:
|
||||
$state = $token[0];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case T_CLOSE_TAG:
|
||||
$classes[$depth]['eol'] = $token[2];
|
||||
break;
|
||||
case T_STRING:
|
||||
switch ($state) {
|
||||
case T_CLASS:
|
||||
$state = self::STATE_CLASS_HEAD;
|
||||
$si = $token[1];
|
||||
$classes[] = [
|
||||
'name' => $token[1],
|
||||
'modifiers' => $mod,
|
||||
'doc' => $doc,
|
||||
'start' => $token[2]
|
||||
];
|
||||
break;
|
||||
case T_FUNCTION:
|
||||
$state = self::STATE_FUNCTION_HEAD;
|
||||
$clsc = count($classes);
|
||||
if ($depth > 0 && $clsc) {
|
||||
$inFunction = true;
|
||||
$functionName = $token[1];
|
||||
$classes[$clsc - 1]['functions'][$token[1]] = [
|
||||
'modifiers' => $mod,
|
||||
'doc' => $doc,
|
||||
'start' => $token[2]
|
||||
];
|
||||
}
|
||||
break;
|
||||
case T_IMPLEMENTS:
|
||||
case T_EXTENDS:
|
||||
$clsc = count($classes);
|
||||
$classes[$clsc - 1][$state == T_IMPLEMENTS ? 'implements' : 'extends'][] = $token[1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$lastLine = $token[2];
|
||||
} else {
|
||||
switch ($token) {
|
||||
case '{':
|
||||
$depth++;
|
||||
break;
|
||||
case '}':
|
||||
$depth--;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token === '}') {
|
||||
if ($inFunction) {
|
||||
$classes[$clsc - 1]['functions'][$functionName]['end'] = $lastLine;
|
||||
$inFunction = false;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($token) {
|
||||
case '{':
|
||||
case '}':
|
||||
case ';':
|
||||
$state = 0;
|
||||
$doc = null;
|
||||
$mod = [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$class['file'] = $file;
|
||||
$this->classes[$class['name']] = $class;
|
||||
|
||||
if (!empty($class['implements'])) {
|
||||
foreach ($class['implements'] as $name) {
|
||||
$this->implements[$name][] = $class['name'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($class['extends'])) {
|
||||
foreach ($class['extends'] as $name) {
|
||||
$this->extends[$name][] = $class['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
typo3conf/ext/news/Classes/Utility/ConstraintHelper.php
Normal file
72
typo3conf/ext/news/Classes/Utility/ConstraintHelper.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
|
||||
/**
|
||||
* Helper class for building constraints
|
||||
*/
|
||||
class ConstraintHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string|int $timeInput
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getTimeRestrictionLow($timeInput): int
|
||||
{
|
||||
$timeLimit = 0;
|
||||
// integer = timestamp
|
||||
if (MathUtility::canBeInterpretedAsInteger($timeInput)) {
|
||||
$timeLimit = $GLOBALS['SIM_EXEC_TIME'] - $timeInput;
|
||||
} else {
|
||||
$timeByFormat = DateTime::createFromFormat('HH:mm DD-MM-YYYY', $timeInput);
|
||||
if ($timeByFormat) {
|
||||
$timeLimit = $timeByFormat->getTimestamp();
|
||||
} else {
|
||||
// try to check strtotime
|
||||
$timeFromString = strtotime($timeInput);
|
||||
|
||||
if ($timeFromString) {
|
||||
$timeLimit = $timeFromString;
|
||||
} else {
|
||||
throw new Exception('Time limit Low could not be resolved to an integer. Given was: ' . htmlspecialchars($timeLimit));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $timeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $timeInput
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getTimeRestrictionHigh($timeInput): int
|
||||
{
|
||||
$timeLimit = 0;
|
||||
// integer = timestamp
|
||||
if (MathUtility::canBeInterpretedAsInteger($timeInput)) {
|
||||
$timeLimit = $GLOBALS['SIM_EXEC_TIME'] + $timeInput;
|
||||
return $timeLimit;
|
||||
}
|
||||
// try to check strtotime
|
||||
$timeFromStringHigh = strtotime($timeInput);
|
||||
|
||||
if ($timeFromStringHigh) {
|
||||
$timeLimit = $timeFromStringHigh;
|
||||
return $timeLimit;
|
||||
}
|
||||
throw new Exception('Time limit High could not be resolved to an integer. Given was: ' . htmlspecialchars($timeLimit));
|
||||
}
|
||||
}
|
||||
53
typo3conf/ext/news/Classes/Utility/ImportJob.php
Normal file
53
typo3conf/ext/news/Classes/Utility/ImportJob.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility class for import jobs
|
||||
*/
|
||||
class ImportJob
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $registeredJobs = [];
|
||||
|
||||
/**
|
||||
* Register an import job.
|
||||
*
|
||||
* @param string $className class name
|
||||
* @param string $title title
|
||||
* @param string $description description
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register($className, $title, $description): void
|
||||
{
|
||||
self::$registeredJobs[] = [
|
||||
'className' => $className,
|
||||
'title' => $title,
|
||||
'description' => $description
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered import jobs
|
||||
*
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function getRegisteredJobs(): array
|
||||
{
|
||||
return self::$registeredJobs;
|
||||
}
|
||||
}
|
||||
143
typo3conf/ext/news/Classes/Utility/Page.php
Normal file
143
typo3conf/ext/news/Classes/Utility/Page.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Backend\Tree\View\PageTreeView;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Database\QueryGenerator;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Log\LogManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Page Utility class
|
||||
*/
|
||||
class Page
|
||||
{
|
||||
|
||||
/**
|
||||
* Find all ids from given ids and level
|
||||
*
|
||||
* @param string $pidList comma separated list of ids
|
||||
* @param int $recursive recursive levels
|
||||
* @return string comma separated list of ids
|
||||
*/
|
||||
public static function extendPidListByChildren($pidList = '', $recursive = 0): string
|
||||
{
|
||||
$recursive = (int)$recursive;
|
||||
if ($recursive <= 0) {
|
||||
return $pidList ?? '';
|
||||
}
|
||||
|
||||
$queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
|
||||
$recursiveStoragePids = $pidList;
|
||||
$storagePids = GeneralUtility::intExplode(',', $pidList);
|
||||
foreach ($storagePids as $startPid) {
|
||||
if ($startPid >= 0) {
|
||||
$pids = $queryGenerator->getTreeList($startPid, $recursive);
|
||||
if (strlen($pids) > 0) {
|
||||
$recursiveStoragePids .= ',' . $pids;
|
||||
}
|
||||
}
|
||||
}
|
||||
return GeneralUtility::uniqueList($recursiveStoragePids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties of an object/array in cobj->LOAD_REGISTER which can then
|
||||
* be used to be loaded via TS with register:name
|
||||
*
|
||||
* @param string $properties comma separated list of properties
|
||||
* @param mixed $object object or array to get the properties
|
||||
* @param string $prefix optional prefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setRegisterProperties($properties, $object, $prefix = 'news'): void
|
||||
{
|
||||
if (!empty($properties) && $object !== null) {
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
$items = GeneralUtility::trimExplode(',', $properties, true);
|
||||
|
||||
$register = [];
|
||||
foreach ($items as $item) {
|
||||
$key = $prefix . ucfirst($item);
|
||||
if (is_object($object)) {
|
||||
$getter = 'get' . ucfirst($item);
|
||||
try {
|
||||
$value = $object->$getter();
|
||||
if ($value instanceof \DateTime) {
|
||||
$value = $value->getTimestamp();
|
||||
}
|
||||
$register[$key] = $value;
|
||||
} catch (\Exception $e) {
|
||||
$logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
|
||||
$logger->warning($e->getMessage());
|
||||
}
|
||||
}
|
||||
if (is_array($object)) {
|
||||
$value = $object[$item];
|
||||
$register[$key] = $value;
|
||||
}
|
||||
}
|
||||
$cObj->cObjGetSingle('LOAD_REGISTER', $register);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a page tree
|
||||
*
|
||||
* @param int $pageUid page to start with
|
||||
* @param int $treeLevel count of levels
|
||||
* @return PageTreeView
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function pageTree($pageUid, $treeLevel): PageTreeView
|
||||
{
|
||||
if (TYPO3_MODE !== 'BE') {
|
||||
throw new \Exception('Page::pageTree does only work in the backend!');
|
||||
}
|
||||
|
||||
$pageUid = (int)$pageUid;
|
||||
if ($pageUid === 0 && !self::getBackendUser()->isAdmin()) {
|
||||
$mounts = self::getBackendUser()->returnWebmounts();
|
||||
$pageUid = array_shift($mounts);
|
||||
}
|
||||
|
||||
/* @var $tree PageTreeView */
|
||||
$tree = GeneralUtility::makeInstance(PageTreeView::class);
|
||||
$tree->init('AND ' . self::getBackendUser()->getPagePermsClause(1));
|
||||
|
||||
$treeStartingRecord = BackendUtility::getRecord('pages', $pageUid);
|
||||
BackendUtility::workspaceOL('pages', $treeStartingRecord);
|
||||
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
|
||||
// Creating top icon; the current page
|
||||
$tree->tree[] = [
|
||||
'row' => $treeStartingRecord,
|
||||
'HTML' => is_array($treeStartingRecord) ? $iconFactory->getIconForRecord('pages', $treeStartingRecord, Icon::SIZE_SMALL)->render() : ''
|
||||
];
|
||||
|
||||
$tree->getTree($pageUid, $treeLevel, '');
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get backend user
|
||||
*
|
||||
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
*/
|
||||
protected static function getBackendUser(): \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
66
typo3conf/ext/news/Classes/Utility/TemplateLayout.php
Normal file
66
typo3conf/ext/news/Classes/Utility/TemplateLayout.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* TemplateLayout utility class
|
||||
*/
|
||||
class TemplateLayout implements SingletonInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Get available template layouts for a certain page
|
||||
*
|
||||
* @param int $pageUid
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableTemplateLayouts($pageUid): array
|
||||
{
|
||||
$templateLayouts = [];
|
||||
|
||||
// Check if the layouts are extended by ext_tables
|
||||
if (isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['templateLayouts'])
|
||||
&& is_array($GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['templateLayouts'])
|
||||
) {
|
||||
$templateLayouts = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['templateLayouts'];
|
||||
}
|
||||
|
||||
// Add TsConfig values
|
||||
foreach ($this->getTemplateLayoutsFromTsConfig($pageUid) as $templateKey => $title) {
|
||||
if (str_starts_with($title, '--div--')) {
|
||||
$optGroupParts = GeneralUtility::trimExplode(',', $title, true, 2);
|
||||
$title = $optGroupParts[1];
|
||||
$templateKey = $optGroupParts[0];
|
||||
}
|
||||
$templateLayouts[] = [$title, $templateKey];
|
||||
}
|
||||
|
||||
return $templateLayouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template layouts defined in TsConfig
|
||||
*
|
||||
* @param $pageUid
|
||||
* @return array
|
||||
*/
|
||||
protected function getTemplateLayoutsFromTsConfig(int $pageUid): array
|
||||
{
|
||||
$templateLayouts = [];
|
||||
$pagesTsConfig = BackendUtility::getPagesTSconfig($pageUid);
|
||||
if (isset($pagesTsConfig['tx_news.']['templateLayouts.']) && is_array($pagesTsConfig['tx_news.']['templateLayouts.'])) {
|
||||
$templateLayouts = $pagesTsConfig['tx_news.']['templateLayouts.'];
|
||||
}
|
||||
return $templateLayouts;
|
||||
}
|
||||
}
|
||||
121
typo3conf/ext/news/Classes/Utility/TypoScript.php
Normal file
121
typo3conf/ext/news/Classes/Utility/TypoScript.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* TypoScript Utility class
|
||||
*/
|
||||
class TypoScript
|
||||
{
|
||||
|
||||
/**
|
||||
* @param array $base
|
||||
* @param array $overload
|
||||
* @return array
|
||||
*/
|
||||
public function override(array $base, array $overload): array
|
||||
{
|
||||
$configuration = $overload['settings']['overrideFlexformSettingsIfEmpty'] ?? '';
|
||||
$validFields = GeneralUtility::trimExplode(',', $configuration, true);
|
||||
foreach ($validFields as $fieldName) {
|
||||
|
||||
// Multilevel field
|
||||
if (strpos($fieldName, '.') !== false) {
|
||||
$keyAsArray = explode('.', $fieldName);
|
||||
|
||||
$foundInCurrentTs = $this->getValue($base, $keyAsArray);
|
||||
|
||||
if (is_string($foundInCurrentTs) && strlen($foundInCurrentTs) === 0) {
|
||||
$foundInOriginal = $this->getValue($overload['settings'], $keyAsArray);
|
||||
if ($foundInOriginal) {
|
||||
$base = $this->setValue($base, $keyAsArray, $foundInOriginal);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if flexform setting is empty and value is available in TS
|
||||
if (((!isset($base[$fieldName]) || $base[$fieldName] === '0') || (strlen($base[$fieldName]) === 0))
|
||||
&& isset($overload['settings'][$fieldName])
|
||||
) {
|
||||
$base[$fieldName] = $overload['settings'][$fieldName];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from array by path
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param mixed $path
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getValue($data, $path)
|
||||
{
|
||||
$found = true;
|
||||
|
||||
for ($x = 0; ($x < count($path) && $found); $x++) {
|
||||
$key = $path[$x];
|
||||
|
||||
if (isset($data[$key])) {
|
||||
$data = $data[$key];
|
||||
} else {
|
||||
$found = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
return $data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value in array by path
|
||||
*
|
||||
* @param array $array
|
||||
* @param array $path
|
||||
* @param mixed $value
|
||||
* @param string[] $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function setValue(array $array, array $path, $value): array
|
||||
{
|
||||
$this->setValueByReference($array, $path, $value);
|
||||
|
||||
$final = array_merge_recursive([], $array);
|
||||
return $final;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value by reference
|
||||
*
|
||||
* @param array $array
|
||||
* @param array $path
|
||||
* @param $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setValueByReference(array &$array, array $path, $value): void
|
||||
{
|
||||
while (count($path) > 1) {
|
||||
$key = array_shift($path);
|
||||
if (!isset($array[$key])) {
|
||||
$array[$key] = [];
|
||||
}
|
||||
$array = &$array[$key];
|
||||
}
|
||||
|
||||
$key = reset($path);
|
||||
$array[$key] = $value;
|
||||
}
|
||||
}
|
||||
34
typo3conf/ext/news/Classes/Utility/Url.php
Normal file
34
typo3conf/ext/news/Classes/Utility/Url.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Url Utility class
|
||||
*/
|
||||
class Url
|
||||
{
|
||||
|
||||
/**
|
||||
* Prepend current url if url is relative
|
||||
*
|
||||
* @param string $url given url
|
||||
* @return string
|
||||
*/
|
||||
public static function prependDomain(string $url): string
|
||||
{
|
||||
if (!str_starts_with($url, GeneralUtility::getIndpEnv('TYPO3_SITE_URL'))) {
|
||||
$url = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
63
typo3conf/ext/news/Classes/Utility/Validation.php
Normal file
63
typo3conf/ext/news/Classes/Utility/Validation.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace GeorgRinger\News\Utility;
|
||||
|
||||
/**
|
||||
* This file is part of the "news" Extension for TYPO3 CMS.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Validation
|
||||
*/
|
||||
class Validation
|
||||
{
|
||||
|
||||
/**
|
||||
* Validate ordering as extbase can't handle that currently
|
||||
*
|
||||
* @param string $fieldToCheck
|
||||
* @param string $allowedSettings
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidOrdering($fieldToCheck, $allowedSettings): bool
|
||||
{
|
||||
$isValid = true;
|
||||
|
||||
if (empty($fieldToCheck)) {
|
||||
return $isValid;
|
||||
}
|
||||
if (empty($allowedSettings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields = GeneralUtility::trimExplode(',', $fieldToCheck, true);
|
||||
foreach ($fields as $field) {
|
||||
if ($isValid === true) {
|
||||
$split = GeneralUtility::trimExplode(' ', $field, true);
|
||||
$count = count($split);
|
||||
switch ($count) {
|
||||
case 1:
|
||||
if (!GeneralUtility::inList($allowedSettings, $split[0])) {
|
||||
$isValid = false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if ((strtolower($split[1]) !== 'desc' && strtolower($split[1]) !== 'asc') ||
|
||||
!GeneralUtility::inList($allowedSettings, $split[0])
|
||||
) {
|
||||
$isValid = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$isValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user