diff --git a/.gitignore b/.gitignore index b066e0c..d4ebdb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -# OS .DS_Store -Thumbs.db \ No newline at end of file +Thumbs.db +/composer.lock +/vendor/ +/node_modules/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index f69a1be..0610274 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -1,9 +1,11 @@ =7.1", - "contao/core-bundle": "^4.9 || ^5.0" + "php": ">=7.4", + "contao/core-bundle": "^4.9 || ^5.0", + "friendsofphp/php-cs-fixer": "^3.0", + "inspiredminds/contao-news-filter-event": "^1.1" }, "require-dev": { - "contao/manager-plugin": "^2.0" + "contao/manager-plugin": "^2.0", + "rector/rector": "^0.14.5" }, "autoload": { "psr-4": { - "ContaoNewsRelatedBundle\\": "src/" + "InspiredMinds\\ContaoNewsRelated\\": "src/" } }, "extra": { - "contao-manager-plugin": "ContaoNewsRelatedBundle\\ContaoManager\\Plugin" + "contao-manager-plugin": "InspiredMinds\\ContaoNewsRelated\\ContaoManager\\Plugin" + }, + "config": { + "allow-plugins": { + "contao-components/installer": true, + "php-http/discovery": false, + "contao/manager-plugin": true + } } } diff --git a/config/services.yaml b/config/services.yaml new file mode 100644 index 0000000..450d6bd --- /dev/null +++ b/config/services.yaml @@ -0,0 +1,13 @@ +services: + _defaults: + autowire: true + autoconfigure: true + + InspiredMinds\ContaoNewsRelated\: + resource: ../src + + InspiredMinds\ContaoNewsRelated\EventListener\DataContainer\NewsOrderOptionsCallback: + arguments: ['@?InspiredMinds\ContaoNewsSorting\EventListener\ModuleDataContainerListener'] + + InspiredMinds\ContaoNewsRelated\EventListener\NewsFilterEventListener: + tags: ['kernel.event_listener'] diff --git a/src/Resources/contao/dca/tl_module.php b/contao/dca/tl_module.php similarity index 65% rename from src/Resources/contao/dca/tl_module.php rename to contao/dca/tl_module.php index 8d34b4e..7bc6e3c 100644 --- a/src/Resources/contao/dca/tl_module.php +++ b/contao/dca/tl_module.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use Contao\CoreBundle\DataContainer\PaletteManipulator; + /* * This file is part of the ContaoNewsRelated bundle. * @@ -32,12 +34,12 @@ 'sql' => "char(1) NOT NULL default ''", ]; -$GLOBALS['TL_DCA']['tl_module']['fields']['news_order']['options_callback'] = ['contao_newsrelated.listener.news', 'newsOrderOptionsCallback']; $GLOBALS['TL_DCA']['tl_module']['fields']['news_order']['reference'] = &$GLOBALS['TL_LANG']['tl_module']; -\Contao\CoreBundle\DataContainer\PaletteManipulator::create() - ->addField('relatedOnly', 'config_legend', \Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_APPEND) - ->addField('includeCurrent', 'config_legend', \Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_APPEND) - ->addField('disableEmpty', 'config_legend', \Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_APPEND) +PaletteManipulator::create() + ->addLegend('news_related_legend', 'config_legend', PaletteManipulator::POSITION_AFTER, true) + ->addField('relatedOnly', 'news_related_legend', PaletteManipulator::POSITION_APPEND) + ->addField('includeCurrent', 'news_related_legend', PaletteManipulator::POSITION_APPEND) + ->addField('disableEmpty', 'news_related_legend', PaletteManipulator::POSITION_APPEND) ->applyToPalette('newslist', 'tl_module') ; diff --git a/src/Resources/contao/dca/tl_news.php b/contao/dca/tl_news.php similarity index 100% rename from src/Resources/contao/dca/tl_news.php rename to contao/dca/tl_news.php diff --git a/src/Resources/contao/languages/de/tl_module.php b/contao/languages/de/tl_module.php similarity index 53% rename from src/Resources/contao/languages/de/tl_module.php rename to contao/languages/de/tl_module.php index a7d330b..087a9b5 100644 --- a/src/Resources/contao/languages/de/tl_module.php +++ b/contao/languages/de/tl_module.php @@ -8,15 +8,8 @@ * (c) fritzmg */ +$GLOBALS['TL_LANG']['tl_module']['news_related_legend'] = 'Verwandte Nachrichten'; $GLOBALS['TL_LANG']['tl_module']['relatedOnly'] = ['Nur verwandte Einträge anzeigen', 'Beschränkt die Ausgabe auf verwandte Artikel.']; $GLOBALS['TL_LANG']['tl_module']['includeCurrent'] = ['Aktuellen Artikel miteinbeziehen', 'In der Liste erscheint auch der aktuelle Artikel an erster Stelle.']; $GLOBALS['TL_LANG']['tl_module']['disableEmpty'] = ['Keine leere Ausgabe', 'Zeigt alle Nachrichtenartikel an, wenn keine verwandten Artikel gefunden wurden.']; - -$GLOBALS['TL_LANG']['tl_module']['order_date_asc'] = 'Datum (aufsteigend)'; -$GLOBALS['TL_LANG']['tl_module']['order_date_desc'] = 'Datum (absteigend)'; -$GLOBALS['TL_LANG']['tl_module']['order_headline_asc'] = 'Titel (aufsteigend)'; -$GLOBALS['TL_LANG']['tl_module']['order_headline_desc'] = 'Titel (absteigend)'; -$GLOBALS['TL_LANG']['tl_module']['order_random'] = 'Zufällig'; -$GLOBALS['TL_LANG']['tl_module']['order_random_date_desc'] = 'Zufällig (Datum absteigend)'; -$GLOBALS['TL_LANG']['tl_module']['order_featured_desc'] = 'Hervorgehoben (Datum absteigend)'; $GLOBALS['TL_LANG']['tl_module']['order_related'] = 'Verwandte Liste'; diff --git a/src/Resources/contao/languages/de/tl_news.php b/contao/languages/de/tl_news.php similarity index 100% rename from src/Resources/contao/languages/de/tl_news.php rename to contao/languages/de/tl_news.php diff --git a/src/Resources/contao/languages/en/tl_module.php b/contao/languages/en/tl_module.php similarity index 52% rename from src/Resources/contao/languages/en/tl_module.php rename to contao/languages/en/tl_module.php index 388acc8..b61022c 100644 --- a/src/Resources/contao/languages/en/tl_module.php +++ b/contao/languages/en/tl_module.php @@ -8,15 +8,8 @@ * (c) fritzmg */ +$GLOBALS['TL_LANG']['tl_module']['news_related_legend'] = 'Related news'; $GLOBALS['TL_LANG']['tl_module']['relatedOnly'] = ['Show only related entries', 'Limits the output to related entries.']; $GLOBALS['TL_LANG']['tl_module']['includeCurrent'] = ['Include current article', 'The current article will be shown in the list as well as the first element.']; $GLOBALS['TL_LANG']['tl_module']['disableEmpty'] = ['Disable empty output', 'Shows all news entries, if no related entries have been found.']; - -$GLOBALS['TL_LANG']['tl_module']['order_date_asc'] = 'Date (ascending)'; -$GLOBALS['TL_LANG']['tl_module']['order_date_desc'] = 'Date (descending)'; -$GLOBALS['TL_LANG']['tl_module']['order_headline_asc'] = 'Headline (ascending)'; -$GLOBALS['TL_LANG']['tl_module']['order_headline_desc'] = 'Headline (descending)'; -$GLOBALS['TL_LANG']['tl_module']['order_random'] = 'Random'; -$GLOBALS['TL_LANG']['tl_module']['order_random_date_desc'] = 'Random (Date descending)'; -$GLOBALS['TL_LANG']['tl_module']['order_featured_desc'] = 'Featured (Date descending)'; $GLOBALS['TL_LANG']['tl_module']['order_related'] = 'Related list'; diff --git a/src/Resources/contao/languages/en/tl_news.php b/contao/languages/en/tl_news.php similarity index 100% rename from src/Resources/contao/languages/en/tl_news.php rename to contao/languages/en/tl_news.php diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..c422406 --- /dev/null +++ b/rector.php @@ -0,0 +1,17 @@ +paths([ + __DIR__ . '/src', + ]); + + // define sets of rules + $rectorConfig->sets([ + LevelSetList::UP_TO_PHP_74 + ]); +}; diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 6f1633c..780669d 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -3,18 +3,20 @@ declare(strict_types=1); /* - * This file is part of the ContaoNewsRelated bundle. + * This file is part of the Contao News Related extension. * - * (c) fritzmg + * (c) inspiredminds + * + * @license LGPL-3.0-or-later */ -namespace ContaoNewsRelatedBundle\ContaoManager; +namespace InspiredMinds\ContaoNewsRelated\ContaoManager; use Contao\ManagerPlugin\Bundle\BundlePluginInterface; use Contao\ManagerPlugin\Bundle\Config\BundleConfig; use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; use Contao\NewsBundle\ContaoNewsBundle; -use ContaoNewsRelatedBundle\ContaoNewsRelatedBundle; +use InspiredMinds\ContaoNewsRelated\ContaoNewsRelatedBundle; /** * Plugin for the Contao Manager. @@ -28,7 +30,7 @@ public function getBundles(ParserInterface $parser) { return [ BundleConfig::create(ContaoNewsRelatedBundle::class) - ->setLoadAfter([ContaoNewsBundle::class, 'news_sorting']), + ->setLoadAfter([ContaoNewsBundle::class]), ]; } } diff --git a/src/ContaoNewsRelatedBundle.php b/src/ContaoNewsRelatedBundle.php index bde1e1e..72dc352 100644 --- a/src/ContaoNewsRelatedBundle.php +++ b/src/ContaoNewsRelatedBundle.php @@ -3,18 +3,21 @@ declare(strict_types=1); /* - * This file is part of the ContaoNewsRelated bundle. + * This file is part of the Contao News Related extension. * - * (c) fritzmg + * (c) inspiredminds + * + * @license LGPL-3.0-or-later */ -namespace ContaoNewsRelatedBundle; +namespace InspiredMinds\ContaoNewsRelated; use Symfony\Component\HttpKernel\Bundle\Bundle; -/** - * Configures the ContaoNewsRelated Bundle. - */ class ContaoNewsRelatedBundle extends Bundle { + public function getPath(): string + { + return \dirname(__DIR__); + } } diff --git a/src/DependencyInjection/ContaoNewsRelatedExtension.php b/src/DependencyInjection/ContaoNewsRelatedExtension.php index 2e2e7b6..f1876d5 100644 --- a/src/DependencyInjection/ContaoNewsRelatedExtension.php +++ b/src/DependencyInjection/ContaoNewsRelatedExtension.php @@ -3,12 +3,14 @@ declare(strict_types=1); /* - * This file is part of the ContaoNewsRelated bundle. + * This file is part of the Contao News Related extension. * - * (c) fritzmg + * (c) inspiredminds + * + * @license LGPL-3.0-or-later */ -namespace ContaoNewsRelatedBundle\DependencyInjection; +namespace InspiredMinds\ContaoNewsRelated\DependencyInjection; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -22,7 +24,7 @@ class ContaoNewsRelatedExtension extends Extension */ public function load(array $configs, ContainerBuilder $container): void { - $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('listener.yml'); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config')); + $loader->load('services.yaml'); } } diff --git a/src/EventListener/DataContainer/NewsOrderOptionsCallback.php b/src/EventListener/DataContainer/NewsOrderOptionsCallback.php new file mode 100644 index 0000000..2fbd57f --- /dev/null +++ b/src/EventListener/DataContainer/NewsOrderOptionsCallback.php @@ -0,0 +1,45 @@ +newsSortingOptionsListener = $newsSortingOptionsListener; + } + + public function __invoke(DataContainer $dc): array + { + $defaultOptions = (new \tl_module_news())->getSortingOptions($dc); + + if ($dc->activeRecord && 'newsmenu' === $dc->activeRecord->type) { + return $defaultOptions; + } + + if (null !== $this->newsSortingOptionsListener) { + return array_merge($this->newsSortingOptionsListener->getSortingOptions($dc), ['order_related']); + } + + return array_merge($defaultOptions, ['order_related']); + } +} diff --git a/src/EventListener/NewsFilterEventListener.php b/src/EventListener/NewsFilterEventListener.php new file mode 100644 index 0000000..5dba363 --- /dev/null +++ b/src/EventListener/NewsFilterEventListener.php @@ -0,0 +1,86 @@ +getModule(); + + if (!$module->relatedOnly) { + return; + } + + $archives = $event->getArchives(); + + if (empty($archives)) { + $this->forceEmptyResultIfApplicable($event); + + return; + } + + $newsAlias = Input::get('auto_item', false, true); + + if (empty($newsAlias)) { + $this->forceEmptyResultIfApplicable($event); + + return; + } + + $news = NewsModel::findByAlias($newsAlias); + + if (null === $news) { + $this->forceEmptyResultIfApplicable($event); + + return; + } + + $related = array_map('intval', StringUtil::deserialize($news->relatedNews, true)); + + if (empty($related)) { + $this->forceEmptyResultIfApplicable($event); + + return; + } + + // Add current element + if ($module->includeCurrent) { + array_unshift($related, (int) $news->id); + } + + $event->addColumn('tl_news.id IN ('.implode(',', $related).')'); + + // Set sorting + if ('order_related' === $module->news_order) { + $event->addOption('order', 'FIELD(tl_news.id,'.implode(',', $related).')', true); + } + } + + private function forceEmptyResultIfApplicable(NewsFilterEvent $event): void + { + if ($event->getModule()->disableEmpty) { + return; + } + + $event + ->setForceEmptyResult(true) + ->stopPropagation() + ; + } +} diff --git a/src/EventListener/NewsListener.php b/src/EventListener/NewsListener.php deleted file mode 100644 index dc8db5f..0000000 --- a/src/EventListener/NewsListener.php +++ /dev/null @@ -1,59 +0,0 @@ - "$t.headline ASC"]); - - $arrOptions = []; - foreach ($objNews as $news) { - // skip self - if ('tl_news' === $dc->table && $dc->activeRecord && isset($dc->activeRecord->id) && $dc->activeRecord->id === $news->id) { - continue; - } - if ('tl_news' === $dc->parentTable && $dc->activeRecord->pid === $news->id) { - continue; - } - - $arrOptions[$news->getRelated('pid')->title][$news->id] = $news->headline; - } - - ksort($arrOptions); - - return $arrOptions; - } - - /** - * Options callback for the news order selection. - */ - public function newsOrderOptionsCallback(DataContainer $dc): array - { - if ($dc->activeRecord && 'newsmenu' === $dc->activeRecord->type) { - return ['order_date_asc', 'order_date_desc']; - } - - if (class_exists(\NewsSorting::class)) { - return array_merge((new \NewsSorting())->getSortingOptions($dc), ['order_related']); - } - - return ['order_date_asc', 'order_date_desc', 'order_headline_asc', 'order_headline_desc', 'order_random', 'order_related']; - } -} diff --git a/src/Models/NewsRelatedModel.php b/src/Models/NewsRelatedModel.php deleted file mode 100644 index 8268985..0000000 --- a/src/Models/NewsRelatedModel.php +++ /dev/null @@ -1,168 +0,0 @@ -relatedOnly) { - return false; - } - - // check if news archives are defined - if (!$newsArchives) { - return null; - } - - // define the return value for no item - $retNoItem = $objModule->disableEmpty ? false : null; - - // get the table and prepare columns, values and options - $t = self::getTable(); - $arrColumns = ["$t.pid IN(".implode(',', array_map('intval', $newsArchives)).')']; - $arrValues = []; - $arrOptions = []; - - // get the active item - $item = Config::get('useAutoItem') ? Input::get('auto_item') : !Input::get('items'); - - // check if there is an active tem - if (!$item) { - return $retNoItem; - } - - // get the news - $objNews = self::findByAlias($item); - - // check if news was found - if (!$objNews) { - return $retNoItem; - } - - // Get the related news - $arrRelated = StringUtil::deserialize($objNews->relatedNews); - - // Check if any related news are defined - if (!$arrRelated) { - return $retNoItem; - } - - // Add current element - if ($objModule->includeCurrent) { - array_unshift($arrRelated, $objNews->id); - } - - // add related news - $arrColumns[] = "$t.id IN(".implode(',', array_map('intval', $arrRelated)).')'; - - // regular news list stuff - if (true === $blnFeatured) { - $arrColumns[] = "$t.featured='1'"; - } elseif (false === $blnFeatured) { - $arrColumns[] = "$t.featured=''"; - } - - if (!System::getContainer()->get('contao.security.token_checker')->isPreviewMode()) { - $time = Date::floorToMinute(); - $arrColumns[] = "($t.start='' OR $t.start<='$time') AND ($t.stop='' OR $t.stop>'".($time + 60)."') AND $t.published='1'"; - } - - $arrOptions['limit'] = $limit; - $arrOptions['offset'] = $offset; - - // fallback to news_sorting (used by news_sorted and news_sorting extension) - $objModule->news_order = $objModule->news_order ?: $objModule->news_sorting; - - // support for news_sorted and news_sorting - switch ($objModule->news_order) { - case 'list_date_asc': - case 'sort_date_asc': - case 'order_date_asc': - $arrOptions['order'] = "$t.date ASC"; - break; - - case 'list_headline_asc': - case 'sort_headline_asc': - case 'order_headline_asc': - $arrOptions['order'] = "$t.headline ASC"; - break; - - case 'list_headline_desc': - case 'sort_headline_desc': - case 'order_headline_desc': - $arrOptions['order'] = "$t.headline DESC"; - break; - - case 'list_random': - case 'sort_random': - case 'order_random': - $arrOptions['order'] = 'RAND()'; - break; - - case 'order_related': - $arrOptions['order'] = "FIELD($t.id,".implode(',', array_map('intval', $arrRelated)).')'; - break; - - default: - $arrOptions['order'] = "$t.date DESC"; - } - - // support for news_categories - if (class_exists('\NewsCategories\NewsModel')) { - $GLOBALS['NEWS_FILTER_CATEGORIES'] = $objModule->news_filterCategories ? true : false; - $GLOBALS['NEWS_FILTER_DEFAULT'] = StringUtil::deserialize($objModule->news_filterDefault, true); - $GLOBALS['NEWS_FILTER_PRESERVE'] = $objModule->news_filterPreserve; - - $arrColumns = self::filterByCategories($arrColumns); - - unset($GLOBALS['NEWS_FILTER_CATEGORIES'], $GLOBALS['NEWS_FILTER_DEFAULT'], $GLOBALS['NEWS_FILTER_PRESERVE']); - } - - // get the result - return self::findBy($arrColumns, $arrValues, $arrOptions) ?: $retNoItem; - } -} diff --git a/src/Resources/config/listener.yml b/src/Resources/config/listener.yml deleted file mode 100644 index a43f4b5..0000000 --- a/src/Resources/config/listener.yml +++ /dev/null @@ -1,4 +0,0 @@ -services: - contao_newsrelated.listener.news: - public: true - class: ContaoNewsRelatedBundle\EventListener\NewsListener diff --git a/src/Resources/contao/config/config.php b/src/Resources/contao/config/config.php deleted file mode 100644 index f8f812d..0000000 --- a/src/Resources/contao/config/config.php +++ /dev/null @@ -1,18 +0,0 @@ -