From 7590e7f1789ff7a8be48d85a25ce87cdd8b1809c Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 13:06:12 +0200 Subject: [PATCH 01/19] [Update] Drop Contao 4.9 support --- composer.json | 6 +-- src/Resources/contao/config/config.php | 52 +++++++++++++------------- src/Resources/public/style.css | 3 -- 3 files changed, 28 insertions(+), 33 deletions(-) delete mode 100644 src/Resources/public/style.css diff --git a/composer.json b/composer.json index 1944f7d..77ba86c 100644 --- a/composer.json +++ b/composer.json @@ -13,8 +13,8 @@ } ], "require": { - "php": "^7.4 || ^8.0", - "contao/core-bundle":"^4.9" + "php": "^8.0", + "contao/core-bundle":"^4.13" }, "require-dev": { "contao/manager-plugin": "^2.0" @@ -39,7 +39,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" }, "contao-manager-plugin": "Oveleon\\ContaoRecommendationBundle\\ContaoManager\\Plugin" } diff --git a/src/Resources/contao/config/config.php b/src/Resources/contao/config/config.php index 5aa2f67..d55f768 100644 --- a/src/Resources/contao/config/config.php +++ b/src/Resources/contao/config/config.php @@ -2,33 +2,37 @@ declare(strict_types=1); +use Contao\ArrayUtil; + + + // Back end modules -array_insert($GLOBALS['BE_MOD']['content'], 5, array -( - 'recommendation' => array - ( - 'tables' => array('tl_recommendation_archive', 'tl_recommendation') - ) -)); -array_insert($GLOBALS['BE_MOD']['system'], 3, array -( - 'recommendation_settings' => array - ( - 'tables' => array('tl_recommendation_settings'), - 'hideInNavigation' => true - ) -)); +ArrayUtil::arrayInsert($GLOBALS['BE_MOD']['content'], 5, [ + 'recommendation' => [ + 'tables' => [ + 'tl_recommendation_archive', + 'tl_recommendation' + ] + ] +]); + +ArrayUtil::arrayInsert($GLOBALS['BE_MOD']['system'], 3, [ + 'recommendation_settings' => [ + 'tables' => [ + 'tl_recommendation_settings' + ], + 'hideInNavigation' => true + ] +]); // Front end modules -array_insert($GLOBALS['FE_MOD'], 2, array -( - 'recommendation' => array - ( +ArrayUtil::arrayInsert($GLOBALS['FE_MOD'], 2, [ + 'recommendation' => [ 'recommendationlist' => 'Oveleon\ContaoRecommendationBundle\ModuleRecommendationList', 'recommendationreader' => 'Oveleon\ContaoRecommendationBundle\ModuleRecommendationReader', 'recommendationform' => 'Oveleon\ContaoRecommendationBundle\ModuleRecommendationForm', - ) -)); + ] +]); // Models $GLOBALS['TL_MODELS']['tl_recommendation'] = 'Oveleon\ContaoRecommendationBundle\RecommendationModel'; @@ -43,9 +47,3 @@ // Register hooks $GLOBALS['TL_HOOKS']['getSearchablePages'][] = array('Oveleon\ContaoRecommendationBundle\Recommendation', 'getSearchablePages'); - -// Style sheet -if (TL_MODE == 'BE') -{ - $GLOBALS['TL_CSS'][] = 'bundles/contaorecommendation/style.css|static'; -} diff --git a/src/Resources/public/style.css b/src/Resources/public/style.css deleted file mode 100644 index ec6318c..0000000 --- a/src/Resources/public/style.css +++ /dev/null @@ -1,3 +0,0 @@ -.navigation.recommendation_settings { - display: none !important; -} From e8fb8c56a49bff0d88f5b9311fdcdd87449383c4 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 14:02:40 +0200 Subject: [PATCH 02/19] [Update] Change bundle structure --- composer.json | 87 ++--- .../classes/Recommendation.php | 0 .../contao => contao}/config/config.php | 6 +- .../contao => contao}/dca/tl_module.php | 179 ++++----- .../dca/tl_recommendation.php | 342 +++++++----------- .../dca/tl_recommendation_archive.php | 204 +++++------ contao/dca/tl_recommendation_settings.php | 33 ++ contao/dca/tl_user.php | 35 ++ .../contao => contao}/dca/tl_user_group.php | 26 +- .../languages/de/default.xlf | 2 +- .../languages/de/modules.xlf | 2 +- .../languages/de/tl_module.xlf | 2 +- .../languages/de/tl_recommendation.xlf | 2 +- .../de/tl_recommendation_archive.xlf | 2 +- .../languages/de/tl_recommendation_list.xlf | 2 +- .../de/tl_recommendation_notification.xlf | 2 +- .../de/tl_recommendation_settings.xlf | 2 +- .../languages/de/tl_user.xlf | 2 +- contao/languages/de/tl_user_group.xlf | 26 ++ .../languages/en/default.xlf | 2 +- .../languages/en/modules.xlf | 2 +- .../languages/en/tl_module.xlf | 2 +- .../languages/en/tl_recommendation.xlf | 2 +- .../en/tl_recommendation_archive.xlf | 2 +- .../languages/en/tl_recommendation_list.xlf | 2 +- .../en/tl_recommendation_notification.xlf | 2 +- .../en/tl_recommendation_settings.xlf | 2 +- .../languages/en/tl_user.xlf | 2 +- contao/languages/en/tl_user_group.xlf | 21 ++ .../models/RecommendationArchiveModel.php | 0 .../models/RecommendationModel.php | 8 +- .../modules/ModuleRecommendation.php | 12 +- .../modules/ModuleRecommendationForm.php | 84 ++--- .../modules/ModuleRecommendationList.php | 4 +- .../modules/ModuleRecommendationReader.php | 0 .../modules/mod_recommendationform.html5 | 0 .../modules/mod_recommendationlist.html5 | 0 .../modules/mod_recommendationreader.html5 | 0 .../recommendation_default.html5 | 0 .../recommendation/recommendation_full.html5 | 0 .../recommendation_latest.html5 | 0 src/ContaoManager/Plugin.php | 4 +- src/ContaoRecommendationBundle.php | 4 + .../contao/dca/tl_recommendation_settings.php | 41 --- src/Resources/contao/dca/tl_user.php | 37 -- .../contao/languages/de/tl_user_group.xlf | 10 - .../contao/languages/en/tl_user_group.xlf | 9 - 47 files changed, 539 insertions(+), 669 deletions(-) rename {src/Resources/contao => contao}/classes/Recommendation.php (100%) rename {src/Resources/contao => contao}/config/config.php (82%) rename {src/Resources/contao => contao}/dca/tl_module.php (67%) rename {src/Resources/contao => contao}/dca/tl_recommendation.php (75%) rename {src/Resources/contao => contao}/dca/tl_recommendation_archive.php (73%) create mode 100644 contao/dca/tl_recommendation_settings.php create mode 100644 contao/dca/tl_user.php rename {src/Resources/contao => contao}/dca/tl_user_group.php (50%) rename {src/Resources/contao => contao}/languages/de/default.xlf (70%) rename {src/Resources/contao => contao}/languages/de/modules.xlf (94%) rename {src/Resources/contao => contao}/languages/de/tl_module.xlf (98%) rename {src/Resources/contao => contao}/languages/de/tl_recommendation.xlf (99%) rename {src/Resources/contao => contao}/languages/de/tl_recommendation_archive.xlf (97%) rename {src/Resources/contao => contao}/languages/de/tl_recommendation_list.xlf (94%) rename {src/Resources/contao => contao}/languages/de/tl_recommendation_notification.xlf (95%) rename {src/Resources/contao => contao}/languages/de/tl_recommendation_settings.xlf (89%) rename {src/Resources/contao => contao}/languages/de/tl_user.xlf (89%) create mode 100644 contao/languages/de/tl_user_group.xlf rename {src/Resources/contao => contao}/languages/en/default.xlf (68%) rename {src/Resources/contao => contao}/languages/en/modules.xlf (93%) rename {src/Resources/contao => contao}/languages/en/tl_module.xlf (98%) rename {src/Resources/contao => contao}/languages/en/tl_recommendation.xlf (98%) rename {src/Resources/contao => contao}/languages/en/tl_recommendation_archive.xlf (96%) rename {src/Resources/contao => contao}/languages/en/tl_recommendation_list.xlf (93%) rename {src/Resources/contao => contao}/languages/en/tl_recommendation_notification.xlf (93%) rename {src/Resources/contao => contao}/languages/en/tl_recommendation_settings.xlf (88%) rename {src/Resources/contao => contao}/languages/en/tl_user.xlf (88%) create mode 100644 contao/languages/en/tl_user_group.xlf rename {src/Resources/contao => contao}/models/RecommendationArchiveModel.php (100%) rename {src/Resources/contao => contao}/models/RecommendationModel.php (97%) rename {src/Resources/contao => contao}/modules/ModuleRecommendation.php (97%) rename {src/Resources/contao => contao}/modules/ModuleRecommendationForm.php (89%) rename {src/Resources/contao => contao}/modules/ModuleRecommendationList.php (98%) rename {src/Resources/contao => contao}/modules/ModuleRecommendationReader.php (100%) rename {src/Resources/contao => contao}/templates/modules/mod_recommendationform.html5 (100%) rename {src/Resources/contao => contao}/templates/modules/mod_recommendationlist.html5 (100%) rename {src/Resources/contao => contao}/templates/modules/mod_recommendationreader.html5 (100%) rename {src/Resources/contao => contao}/templates/recommendation/recommendation_default.html5 (100%) rename {src/Resources/contao => contao}/templates/recommendation/recommendation_full.html5 (100%) rename {src/Resources/contao => contao}/templates/recommendation/recommendation_latest.html5 (100%) delete mode 100644 src/Resources/contao/dca/tl_recommendation_settings.php delete mode 100644 src/Resources/contao/dca/tl_user.php delete mode 100644 src/Resources/contao/languages/de/tl_user_group.xlf delete mode 100644 src/Resources/contao/languages/en/tl_user_group.xlf diff --git a/composer.json b/composer.json index 77ba86c..e390bef 100644 --- a/composer.json +++ b/composer.json @@ -1,46 +1,49 @@ { - "name": "oveleon/contao-recommendation-bundle", - "type": "contao-bundle", - "description": "Recommendation integration for Contao 4 Open Source CMS", - "keywords": ["contao","recommendation-bundle"], - "homepage": "https://www.oveleon.de/", - "license": "MIT", - "authors": [ - { - "name": "Oveleon", - "homepage": "https://oveleon.de/", - "role": "Developer" - } - ], - "require": { - "php": "^8.0", - "contao/core-bundle":"^4.13" - }, - "require-dev": { - "contao/manager-plugin": "^2.0" - }, - "conflict": { - "contao/core": "*", - "contao/manager-plugin": "<2.0 || >=3.0" + "name": "oveleon/contao-recommendation-bundle", + "type": "contao-bundle", + "description": "Recommendation integration for Contao 4 Open Source CMS", + "keywords": [ + "contao", + "recommendation-bundle" + ], + "homepage": "https://www.oveleon.de/", + "license": "MIT", + "authors": [ + { + "name": "Oveleon", + "homepage": "https://oveleon.de/", + "role": "Developer" + } + ], + "require": { + "php": "^8.1", + "contao/core-bundle": "^4.13" + }, + "require-dev": { + "contao/manager-plugin": "^2.0" + }, + "conflict": { + "contao/core": "*", + "contao/manager-plugin": "<2.0 || >=3.0" + }, + "autoload": { + "psr-4": { + "Oveleon\\ContaoRecommendationBundle\\": "src/" }, - "autoload": { - "psr-4": { - "Oveleon\\ContaoRecommendationBundle\\": "src/" - }, - "classmap": [ - "src/Resources/contao/" - ], - "exclude-from-classmap": [ - "src/Resources/contao/config/", - "src/Resources/contao/dca/", - "src/Resources/contao/languages/", - "src/Resources/contao/templates/" - ] + "classmap": [ + "contao/" + ], + "exclude-from-classmap": [ + "contao/config/", + "contao/dca/", + "contao/languages/", + "contao/templates/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" }, - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - }, - "contao-manager-plugin": "Oveleon\\ContaoRecommendationBundle\\ContaoManager\\Plugin" - } + "contao-manager-plugin": "Oveleon\\ContaoRecommendationBundle\\ContaoManager\\Plugin" + } } diff --git a/src/Resources/contao/classes/Recommendation.php b/contao/classes/Recommendation.php similarity index 100% rename from src/Resources/contao/classes/Recommendation.php rename to contao/classes/Recommendation.php diff --git a/src/Resources/contao/config/config.php b/contao/config/config.php similarity index 82% rename from src/Resources/contao/config/config.php rename to contao/config/config.php index d55f768..a69b991 100644 --- a/src/Resources/contao/config/config.php +++ b/contao/config/config.php @@ -4,8 +4,6 @@ use Contao\ArrayUtil; - - // Back end modules ArrayUtil::arrayInsert($GLOBALS['BE_MOD']['content'], 5, [ 'recommendation' => [ @@ -43,7 +41,7 @@ $GLOBALS['TL_PERMISSIONS'][] = 'recommendationp'; // Cron jobs -$GLOBALS['TL_CRON']['daily']['purgeRecommendations'] = array('Oveleon\ContaoRecommendationBundle\Recommendation', 'purgeRecommendations'); +$GLOBALS['TL_CRON']['daily']['purgeRecommendations'] = ['Oveleon\ContaoRecommendationBundle\Recommendation', 'purgeRecommendations']; // Register hooks -$GLOBALS['TL_HOOKS']['getSearchablePages'][] = array('Oveleon\ContaoRecommendationBundle\Recommendation', 'getSearchablePages'); +$GLOBALS['TL_HOOKS']['getSearchablePages'][] = ['Oveleon\ContaoRecommendationBundle\Recommendation', 'getSearchablePages']; diff --git a/src/Resources/contao/dca/tl_module.php b/contao/dca/tl_module.php similarity index 67% rename from src/Resources/contao/dca/tl_module.php rename to contao/dca/tl_module.php index a4571ee..ef4df7f 100644 --- a/src/Resources/contao/dca/tl_module.php +++ b/contao/dca/tl_module.php @@ -6,9 +6,11 @@ * (c) https://www.oveleon.de/ */ -Contao\System::loadLanguageFile('tl_recommendation'); -Contao\System::loadLanguageFile('tl_recommendation_notification'); -Contao\System::loadLanguageFile('tl_recommendation_list'); +use Contao\System; + +System::loadLanguageFile('tl_recommendation'); +System::loadLanguageFile('tl_recommendation_notification'); +System::loadLanguageFile('tl_recommendation_list'); // Add a palette selector $GLOBALS['TL_DCA']['tl_module']['palettes']['__selector__'][] = 'recommendation_activate'; @@ -21,185 +23,156 @@ $GLOBALS['TL_DCA']['tl_module']['subpalettes']['recommendation_activate'] = 'recommendation_activateJumpTo,recommendation_activateText'; // Add fields to tl_module -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_archives'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_archives'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_archives'] = [ 'exclude' => true, 'inputType' => 'checkbox', - 'options_callback' => array('tl_module_recommendation', 'getRecommendationArchives'), - 'eval' => array('multiple'=>true, 'mandatory'=>true), + 'options_callback' => ['tl_module_recommendation', 'getRecommendationArchives'], + 'eval' => ['multiple'=>true, 'mandatory'=>true], 'sql' => "blob NULL" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_archive'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_archive'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_archive'] = [ 'exclude' => true, 'inputType' => 'select', - 'options_callback' => array('tl_module_recommendation', 'getRecommendationArchives'), - 'eval' => array('mandatory'=>true, 'includeBlankOption'=>true, 'tl_class'=>'w50 clr'), + 'options_callback' => ['tl_module_recommendation', 'getRecommendationArchives'], + 'eval' => ['mandatory'=>true, 'includeBlankOption'=>true, 'tl_class'=>'w50 clr'], 'sql' => "int(10) unsigned NOT NULL default 0" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_featured'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_featured'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_featured'] = [ 'default' => 'all_items', 'exclude' => true, 'inputType' => 'select', - 'options' => array('all_items', 'featured', 'unfeatured', 'featured_first'), + 'options' => ['all_items', 'featured', 'unfeatured', 'featured_first'], 'reference' => &$GLOBALS['TL_LANG']['tl_recommendation_list'], - 'eval' => array('tl_class'=>'w50'), + 'eval' => ['tl_class'=>'w50'], 'sql' => "varchar(16) NOT NULL default ''" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_readerModule'] = array -( +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_readerModule'] = [ 'exclude' => true, 'inputType' => 'select', - 'options_callback' => array('tl_module_recommendation', 'getReaderModules'), + 'options_callback' => ['tl_module_recommendation', 'getReaderModules'], 'reference' => &$GLOBALS['TL_LANG']['tl_module'], - 'eval' => array('includeBlankOption'=>true, 'tl_class'=>'w50'), + 'eval' => ['includeBlankOption'=>true, 'tl_class'=>'w50'], 'sql' => "int(10) unsigned NOT NULL default 0" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_metaFields'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_metaFields'], - 'default' => array('date', 'author'), +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_metaFields'] = [ + 'default' => ['date', 'author'], 'exclude' => true, 'inputType' => 'checkbox', - 'options' => array('image', 'date', 'author', 'rating', 'location', 'customField'), + 'options' => ['image', 'date', 'author', 'rating', 'location', 'customField'], 'reference' => &$GLOBALS['TL_LANG']['tl_recommendation'], - 'eval' => array('multiple'=>true), + 'eval' => ['multiple'=>true], 'sql' => "varchar(255) NOT NULL default ''" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_order'] = array -( +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_order'] = [ 'exclude' => true, 'inputType' => 'select', - 'options' => array('order_date_asc', 'order_date_desc', 'order_random', 'order_rating_desc'), + 'options' => ['order_date_asc', 'order_date_desc', 'order_random', 'order_rating_desc'], 'reference' => &$GLOBALS['TL_LANG']['tl_recommendation_list'], - 'eval' => array('tl_class'=>'w50'), + 'eval' => ['tl_class'=>'w50'], 'sql' => "varchar(32) NOT NULL default 'order_date_desc'" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_minRating'] = array -( +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_minRating'] = [ 'exclude' => true, 'inputType' => 'select', - 'options' => array(1=> 'minOne', 2=>'minTwo', 3=>'minThree', 4=>'minFour', 5=>'minFive'), + 'options' => [1=> 'minOne', 2=>'minTwo', 3=>'minThree', 4=>'minFour', 5=>'minFive'], 'reference' => &$GLOBALS['TL_LANG']['tl_recommendation_list'], - 'eval' => array('tl_class'=>'w50'), + 'eval' => ['tl_class'=>'w50'], 'sql' => "char(1) NOT NULL default '1'" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_optionalFormFields'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_optionalFormFields'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_optionalFormFields'] = [ 'exclude' => true, 'inputType' => 'checkbox', - 'options' => array('title', 'location', 'email', 'customField'), + 'options' => ['title', 'location', 'email', 'customField'], 'reference' => &$GLOBALS['TL_LANG']['tl_recommendation'], - 'eval' => array('multiple'=>true, 'tl_class'=>'w50 clr'), + 'eval' => ['multiple'=>true, 'tl_class'=>'w50 clr'], 'sql' => "varchar(255) NOT NULL default ''" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_customFieldLabel'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_customFieldLabel'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_customFieldLabel'] = [ 'exclude' => true, 'inputType' => 'text', - 'eval' => array('maxlength'=>64, 'tl_class'=>'w50 clr'), + 'eval' => ['maxlength'=>64, 'tl_class'=>'w50 clr'], 'sql' => "varchar(64) NOT NULL default ''" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_notify'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_notify'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_notify'] = [ 'default' => true, 'exclude' => true, 'inputType' => 'checkbox', - 'eval' => array('tl_class'=>'w50 clr'), + 'eval' => ['tl_class'=>'w50 clr'], 'sql' => "char(1) NOT NULL default '1'" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_moderate'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_moderate'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_moderate'] = [ 'default' => true, 'exclude' => true, 'inputType' => 'checkbox', - 'eval' => array('tl_class'=>'w50'), + 'eval' => ['tl_class'=>'w50'], 'sql' => "char(1) NOT NULL default '1'" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_disableCaptcha'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_disableCaptcha'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_disableCaptcha'] = [ 'exclude' => true, 'inputType' => 'checkbox', - 'eval' => array('tl_class'=>'w50'), + 'eval' => ['tl_class'=>'w50'], 'sql' => "char(1) NOT NULL default ''" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_disableCaptcha'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_disableCaptcha'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_disableCaptcha'] = [ 'exclude' => true, 'inputType' => 'checkbox', - 'eval' => array('tl_class'=>'w50'), + 'eval' => ['tl_class'=>'w50'], 'sql' => "char(1) NOT NULL default ''" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_privacyText'] = array -( +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_privacyText'] = [ 'exclude' => true, 'inputType' => 'textarea', - 'eval' => array('style'=>'height:100px', 'allowHtml'=>true), + 'eval' => ['style'=>'height:100px', 'allowHtml'=>true], 'sql' => "text NULL" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_activate'] = array -( +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_activate'] = [ 'exclude' => true, 'inputType' => 'checkbox', - 'eval' => array('submitOnChange'=>true), + 'eval' => ['submitOnChange'=>true], 'sql' => "char(1) NOT NULL default ''" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_activateJumpTo'] = array -( +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_activateJumpTo'] = [ 'exclude' => true, 'inputType' => 'pageTree', 'foreignKey' => 'tl_page.title', - 'eval' => array('fieldType'=>'radio'), + 'eval' => ['fieldType'=>'radio'], 'sql' => "int(10) unsigned NOT NULL default 0", - 'relation' => array('type'=>'hasOne', 'load'=>'lazy') -); + 'relation' => ['type'=>'hasOne', 'load'=>'lazy'] +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_activateText'] = array -( +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_activateText'] = [ 'exclude' => true, 'inputType' => 'textarea', - 'eval' => array('style'=>'height:120px', 'decodeEntities'=>true, 'alwaysSave'=>true), - 'load_callback' => array - ( - array('tl_module_recommendation', 'getRecommendationActivationDefault') - ), + 'eval' => ['style'=>'height:120px', 'decodeEntities'=>true, 'alwaysSave'=>true], + 'load_callback' => + [ + ['tl_module_recommendation', 'getRecommendationActivationDefault'] + ], 'sql' => "text NULL" -); +]; -$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_template'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_module']['recommendation_template'], +$GLOBALS['TL_DCA']['tl_module']['fields']['recommendation_template'] = [ 'exclude' => true, 'inputType' => 'select', 'options_callback' => static fn () => Controller::getTemplateGroup('recommendation_'), - 'eval' => array('includeBlankOption' => true, 'chosen' => true, 'tl_class'=>'w50'), + 'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class'=>'w50'], 'sql' => "varchar(64) NOT NULL default ''" -); +]; /** @@ -228,10 +201,10 @@ public function getRecommendationArchives() { if (!$this->User->isAdmin && !is_array($this->User->recommendations)) { - return array(); + return []; } - $arrArchives = array(); + $arrArchives = []; $objArchives = $this->Database->execute("SELECT id, title FROM tl_recommendation_archive ORDER BY title"); while ($objArchives->next()) @@ -252,7 +225,7 @@ public function getRecommendationArchives() */ public function getReaderModules() { - $arrModules = array(); + $arrModules = []; $objModules = $this->Database->execute("SELECT m.id, m.name, t.name AS theme FROM tl_module m LEFT JOIN tl_theme t ON m.pid=t.id WHERE m.type='recommendationreader' ORDER BY t.name, m.name"); while ($objModules->next()) diff --git a/src/Resources/contao/dca/tl_recommendation.php b/contao/dca/tl_recommendation.php similarity index 75% rename from src/Resources/contao/dca/tl_recommendation.php rename to contao/dca/tl_recommendation.php index 03290bb..4a47eda 100644 --- a/src/Resources/contao/dca/tl_recommendation.php +++ b/contao/dca/tl_recommendation.php @@ -8,327 +8,254 @@ use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; -$GLOBALS['TL_DCA']['tl_recommendation'] = array -( +$GLOBALS['TL_DCA']['tl_recommendation'] = [ // Config - 'config' => array - ( + 'config' => [ 'dataContainer' => 'Table', 'ptable' => 'tl_recommendation_archive', 'switchToEdit' => true, 'enableVersioning' => true, - 'onload_callback' => array - ( - array('tl_recommendation', 'checkPermission'), - array('tl_recommendation', 'generateSitemap') - ), - 'oncut_callback' => array - ( - array('tl_recommendation', 'scheduleUpdate') - ), - 'ondelete_callback' => array - ( - array('tl_recommendation', 'scheduleUpdate') - ), - 'onsubmit_callback' => array - ( - array('tl_recommendation', 'adjustTime'), - array('tl_recommendation', 'scheduleUpdate') - ), - 'oninvalidate_cache_tags_callback' => array - ( - array('tl_recommendation', 'addSitemapCacheInvalidationTag'), - ), - 'sql' => array - ( - 'keys' => array - ( + 'onload_callback' => [ + ['tl_recommendation', 'checkPermission'], + ['tl_recommendation', 'generateSitemap'] + ], + 'oncut_callback' => [ + ['tl_recommendation', 'scheduleUpdate'] + ], + 'ondelete_callback' => [ + ['tl_recommendation', 'scheduleUpdate'] + ], + 'onsubmit_callback' => [ + ['tl_recommendation', 'adjustTime'], + ['tl_recommendation', 'scheduleUpdate'] + ], + 'oninvalidate_cache_tags_callback' => [ + ['tl_recommendation', 'addSitemapCacheInvalidationTag'], + ], + 'sql' => [ + 'keys' => [ 'id' => 'primary', 'alias' => 'index', 'pid,start,stop,published' => 'index' - ) - ) - ), + ] + ] + ], // List - 'list' => array - ( - 'sorting' => array - ( + 'list' => [ + 'sorting' => [ 'mode' => 4, - 'fields' => array('date DESC'), - 'headerFields' => array('title', 'jumpTo', 'tstamp', 'protected'), + 'fields' => ['date DESC'], + 'headerFields' => ['title', 'jumpTo', 'tstamp', 'protected'], 'panelLayout' => 'filter;sort,search,limit', - 'child_record_callback' => array('tl_recommendation', 'listRecommendations'), + 'child_record_callback' => ['tl_recommendation', 'listRecommendations'], 'child_record_class' => 'no_padding' - ), - 'global_operations' => array - ( - 'all' => array - ( - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], + ], + 'global_operations' => [ + 'all' => [ 'href' => 'act=select', 'class' => 'header_edit_all', 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"' - ) - ), - 'operations' => array - ( - 'edit' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['edit'], + ] + ], + 'operations' => [ + 'edit' => [ 'href' => 'act=edit', 'icon' => 'edit.svg' - ), - 'copy' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['copy'], + ], + 'copy' => [ 'href' => 'act=paste&mode=copy', 'icon' => 'copy.svg' - ), - 'cut' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['cut'], + ], + 'cut' => [ 'href' => 'act=paste&mode=cut', 'icon' => 'cut.svg' - ), - 'delete' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['delete'], + ], + 'delete' => [ 'href' => 'act=delete', 'icon' => 'delete.svg', 'attributes' => 'onclick="if(!confirm(\'' . ($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null) . '\'))return false;Backend.getScrollOffset()"' - ), - 'toggle' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['toggle'], + ], + 'toggle' => [ 'icon' => 'visible.svg', 'attributes' => 'onclick="Backend.getScrollOffset();return AjaxRequest.toggleVisibility(this,%s)"', - 'button_callback' => array('tl_recommendation', 'toggleIcon') - ), - 'feature' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['feature'], + 'button_callback' => ['tl_recommendation', 'toggleIcon'] + ], + 'feature' => [ 'icon' => 'featured.svg', 'attributes' => 'onclick="Backend.getScrollOffset();return AjaxRequest.toggleFeatured(this,%s)"', - 'button_callback' => array('tl_recommendation', 'iconFeatured') - ), - 'show' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['show'], + 'button_callback' => ['tl_recommendation', 'iconFeatured'] + ], + 'show' => [ 'href' => 'act=show', 'icon' => 'show.svg' - ) - ) - ), + ] + ] + ], // Palettes - 'palettes' => array - ( + 'palettes' => [ 'default' => '{title_legend},author,title,alias,email,location;{date_legend},date,time;{recommendation_legend},text,imageUrl,rating,customField;{teaser_legend:hide},teaser;{expert_legend:hide},cssClass,featured;{publish_legend},published,start,stop' - ), + ], // Fields - 'fields' => array - ( - 'id' => array - ( + 'fields' => [ + 'id' => [ 'sql' => "int(10) unsigned NOT NULL auto_increment" - ), - 'pid' => array - ( + ], + 'pid' => [ 'foreignKey' => 'tl_recommendation_archive.title', 'sql' => "int(10) unsigned NOT NULL default '0'", - 'relation' => array('type'=>'belongsTo', 'load'=>'lazy') - ), - 'tstamp' => array - ( + 'relation' => ['type'=>'belongsTo', 'load'=>'lazy'] + ], + 'tstamp' => [ 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'title' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['title'], + ], + 'title' => [ 'exclude' => true, 'search' => true, 'sorting' => true, 'flag' => 1, 'inputType' => 'text', - 'eval' => array('maxlength'=>255, 'tl_class'=>'w50 clr'), + 'eval' => ['maxlength'=>255, 'tl_class'=>'w50 clr'], 'sql' => "varchar(255) NOT NULL default ''" - ), - 'alias' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['alias'], + ], + 'alias' => [ 'exclude' => true, 'search' => true, 'inputType' => 'text', - 'eval' => array('rgxp'=>'alias', 'doNotCopy'=>true, 'unique'=>true, 'maxlength'=>128, 'tl_class'=>'w50'), - 'save_callback' => array - ( - array('tl_recommendation', 'generateAlias') - ), + 'eval' => ['rgxp'=>'alias', 'doNotCopy'=>true, 'unique'=>true, 'maxlength'=>128, 'tl_class'=>'w50'], + 'save_callback' => [ + ['tl_recommendation', 'generateAlias'] + ], 'sql' => "varchar(255) BINARY NOT NULL default ''" - ), - 'author' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['author'], + ], + 'author' => [ 'exclude' => true, 'search' => true, 'sorting' => true, 'flag' => 1, 'inputType' => 'text', - 'eval' => array('doNotCopy'=>true, 'mandatory'=>true, 'maxlength'=>128, 'tl_class'=>'w50'), + 'eval' => ['doNotCopy'=>true, 'mandatory'=>true, 'maxlength'=>128, 'tl_class'=>'w50'], 'sql' => "varchar(128) NOT NULL default ''" - ), - 'email' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['email'], + ], + 'email' => [ 'exclude' => true, 'search' => true, 'inputType' => 'text', - 'eval' => array('doNotCopy'=>true, 'maxlength'=>255, 'rgxp'=>'email', 'decodeEntities'=>true, 'tl_class'=>'w50'), + 'eval' => ['doNotCopy'=>true, 'maxlength'=>255, 'rgxp'=>'email', 'decodeEntities'=>true, 'tl_class'=>'w50'], 'sql' => "varchar(255) NOT NULL default ''" - ), - 'location' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['location'], + ], + 'location' => [ 'exclude' => true, 'search' => true, 'sorting' => true, 'flag' => 1, 'inputType' => 'text', - 'eval' => array('doNotCopy'=>true, 'maxlength'=>128, 'tl_class'=>'w50'), + 'eval' => ['doNotCopy'=>true, 'maxlength'=>128, 'tl_class'=>'w50'], 'sql' => "varchar(128) NOT NULL default ''" - ), - 'date' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['date'], + ], + 'date' => [ 'default' => time(), 'exclude' => true, 'filter' => true, 'sorting' => true, 'flag' => 8, 'inputType' => 'text', - 'eval' => array('rgxp'=>'date', 'mandatory'=>true, 'doNotCopy'=>true, 'datepicker'=>true, 'tl_class'=>'w50 wizard'), - 'load_callback' => array - ( - array('tl_recommendation', 'loadDate') - ), - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'time' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['time'], + 'eval' => ['rgxp'=>'date', 'mandatory'=>true, 'doNotCopy'=>true, 'datepicker'=>true, 'tl_class'=>'w50 wizard'], + 'load_callback' => [ + ['tl_recommendation', 'loadDate'] + ], + 'sql' => "int(10) unsigned NOT NULL default 0" + ], + 'time' => [ 'default' => time(), 'exclude' => true, 'inputType' => 'text', - 'eval' => array('rgxp'=>'time', 'mandatory'=>true, 'doNotCopy'=>true, 'tl_class'=>'w50'), - 'load_callback' => array - ( - array('tl_recommendation', 'loadTime') - ), - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'teaser' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['teaser'], + 'eval' => ['rgxp'=>'time', 'mandatory'=>true, 'doNotCopy'=>true, 'tl_class'=>'w50'], + 'load_callback' => [ + ['tl_recommendation', 'loadTime'] + ], + 'sql' => "int(10) unsigned NOT NULL default 0" + ], + 'teaser' => [ 'exclude' => true, 'search' => true, 'inputType' => 'textarea', - 'eval' => array('rte'=>'tinyMCE', 'tl_class'=>'clr'), + 'eval' => ['rte'=>'tinyMCE', 'tl_class'=>'clr'], 'sql' => "mediumtext NULL" - ), - 'text' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['text'], + ], + 'text' => [ 'exclude' => true, 'search' => true, 'inputType' => 'textarea', - 'eval' => array('mandatory'=>true, 'rte'=>'tinyMCE', 'tl_class'=>'clr'), + 'eval' => ['mandatory'=>true, 'rte'=>'tinyMCE', 'tl_class'=>'clr'], 'sql' => "mediumtext NULL" - ), - 'imageUrl' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['imageUrl'], + ], + 'imageUrl' => [ 'exclude' => true, 'search' => true, 'inputType' => 'text', - 'eval' => array('rgxp'=>'url', 'decodeEntities'=>true, 'maxlength'=>255, 'dcaPicker'=>true, 'tl_class'=>'w50 wizard'), + 'eval' => ['rgxp'=>'url', 'decodeEntities'=>true, 'maxlength'=>255, 'dcaPicker'=>true, 'tl_class'=>'w50 wizard'], 'sql' => "varchar(255) NOT NULL default ''" - ), - 'rating' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['rating'], + ], + 'rating' => [ 'default' => 5, 'exclude' => true, 'search' => true, 'filter' => true, 'sorting' => true, 'inputType' => 'select', - 'options' => array(1,2,3,4,5), - 'eval' => array('mandatory'=>true, 'tl_class'=>'w50'), + 'options' => [1,2,3,4,5], + 'eval' => ['mandatory'=>true, 'tl_class'=>'w50'], 'sql' => "char(1) NOT NULL default ''" - ), - 'customField' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['customField'], + ], + 'customField' => [ 'exclude' => true, 'inputType' => 'text', - 'eval' => array('doNotCopy'=>true, 'maxlength'=>255, 'tl_class'=>'w100 clr'), + 'eval' => ['doNotCopy'=>true, 'maxlength'=>255, 'tl_class'=>'w100 clr'], 'sql' => "varchar(255) NOT NULL default ''" - ), - 'cssClass' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['cssClass'], + ], + 'cssClass' => [ 'exclude' => true, 'inputType' => 'text', - 'eval' => array('tl_class'=>'w50'), + 'eval' => ['tl_class'=>'w50'], 'sql' => "varchar(255) NOT NULL default ''" - ), - 'featured' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['featured'], + ], + 'featured' => [ 'exclude' => true, 'filter' => true, 'inputType' => 'checkbox', - 'eval' => array('tl_class'=>'w50 m12'), + 'eval' => ['tl_class'=>'w50 m12'], 'sql' => "char(1) NOT NULL default ''" - ), - 'verified' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['verified'], + ], + 'verified' => [ 'filter' => true, - 'eval' => array('isBoolean'=>true), + 'eval' => ['isBoolean'=>true], 'sql' => "char(1) NOT NULL default '1'" - ), - 'published' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['published'], + ], + 'published' => [ 'exclude' => true, 'filter' => true, 'flag' => 1, 'inputType' => 'checkbox', - 'eval' => array('doNotCopy'=>true), + 'eval' => ['doNotCopy'=>true], 'sql' => "char(1) NOT NULL default ''" - ), - 'start' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['start'], + ], + 'start' => [ 'exclude' => true, 'inputType' => 'text', - 'eval' => array('rgxp'=>'datim', 'datepicker'=>true, 'tl_class'=>'w50 wizard'), + 'eval' => ['rgxp'=>'datim', 'datepicker'=>true, 'tl_class'=>'w50 wizard'], 'sql' => "varchar(10) NOT NULL default ''" - ), - 'stop' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation']['stop'], + ], + 'stop' => [ 'exclude' => true, 'inputType' => 'text', - 'eval' => array('rgxp'=>'datim', 'datepicker'=>true, 'tl_class'=>'w50 wizard'), + 'eval' => ['rgxp'=>'datim', 'datepicker'=>true, 'tl_class'=>'w50 wizard'], 'sql' => "varchar(10) NOT NULL default ''" - ) - ) -); + ] + ] +]; /** * Provide miscellaneous methods that are used by the data configuration array. @@ -337,7 +264,6 @@ */ class tl_recommendation extends Contao\Backend { - /** * Import the back end user object */ @@ -362,7 +288,7 @@ public function checkPermission() // Set the root IDs if (empty($this->User->recommendations) || !is_array($this->User->recommendations)) { - $root = array(0); + $root = [0]; } else { @@ -562,7 +488,7 @@ public function adjustTime(Contao\DataContainer $dc) return; } - $arrSet['date'] = strtotime(date('Y-m-d', $dc->activeRecord->date) . ' ' . date('H:i:s', $dc->activeRecord->time)); + $arrSet['date'] = strtotime(date('Y-m-d', $dc->activeRecord->date) . ' tl_recommendation.php' . date('H:i:s', $dc->activeRecord->time)); $arrSet['time'] = $arrSet['date']; $this->Database->prepare("UPDATE tl_recommendation %s WHERE id=?")->set($arrSet)->execute($dc->id); @@ -868,6 +794,6 @@ public function addSitemapCacheInvalidationTag($dc, array $tags) return $tags; } - return array_merge($tags, array('contao.sitemap.' . $pageModel->rootId)); + return array_merge($tags, ['contao.sitemap.' . $pageModel->rootId]); } } diff --git a/src/Resources/contao/dca/tl_recommendation_archive.php b/contao/dca/tl_recommendation_archive.php similarity index 73% rename from src/Resources/contao/dca/tl_recommendation_archive.php rename to contao/dca/tl_recommendation_archive.php index 75de17e..066517e 100644 --- a/src/Resources/contao/dca/tl_recommendation_archive.php +++ b/contao/dca/tl_recommendation_archive.php @@ -6,176 +6,136 @@ * (c) https://www.oveleon.de/ */ -$GLOBALS['TL_DCA']['tl_recommendation_archive'] = array -( +$GLOBALS['TL_DCA']['tl_recommendation_archive'] = [ // Config - 'config' => array - ( + 'config' => [ 'dataContainer' => 'Table', - 'ctable' => array('tl_recommendation'), + 'ctable' => ['tl_recommendation'], 'switchToEdit' => true, 'enableVersioning' => true, - 'onload_callback' => array - ( - array('tl_recommendation_archive', 'checkPermission') - ), - 'oncreate_callback' => array - ( - array('tl_recommendation_archive', 'adjustPermissions') - ), - 'oncopy_callback' => array - ( - array('tl_recommendation_archive', 'adjustPermissions') - ), - 'oninvalidate_cache_tags_callback' => array - ( - array('tl_recommendation_archive', 'addSitemapCacheInvalidationTag'), - ), - 'sql' => array - ( - 'keys' => array - ( + 'onload_callback' => [ + ['tl_recommendation_archive', 'checkPermission'] + ], + 'oncreate_callback' => [ + ['tl_recommendation_archive', 'adjustPermissions'] + ], + 'oncopy_callback' => [ + ['tl_recommendation_archive', 'adjustPermissions'] + ], + 'oninvalidate_cache_tags_callback' => [ + ['tl_recommendation_archive', 'addSitemapCacheInvalidationTag'], + ], + 'sql' => [ + 'keys' => [ 'id' => 'primary' - ) - ) - ), + ] + ] + ], // List - 'list' => array - ( - 'sorting' => array - ( + 'list' => [ + 'sorting' => [ 'mode' => 1, - 'fields' => array('title'), + 'fields' => ['title'], 'flag' => 1, 'panelLayout' => 'filter;search,limit' - ), - 'label' => array - ( - 'fields' => array('title'), + ], + 'label' => [ + 'fields' => ['title'], 'format' => '%s' - ), - 'global_operations' => array - ( - 'settings' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['settings'], + ], + 'global_operations' => [ + 'settings' => [ 'href' => 'do=recommendation_settings', 'class' => '', 'icon' => 'edit.svg', 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"' - ), - 'all' => array - ( - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], + ], + 'all' => [ 'href' => 'act=select', 'class' => 'header_edit_all', 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"' - ) - ), - 'operations' => array - ( - 'edit' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['edit'], + ] + ], + 'operations' => [ + 'edit' => [ 'href' => 'table=tl_recommendation', 'icon' => 'edit.svg' - ), - 'editheader' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['editheader'], + ], + 'editheader' => [ 'href' => 'act=edit', 'icon' => 'header.svg', - 'button_callback' => array('tl_recommendation_archive', 'editHeader') - ), - 'copy' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['copy'], + 'button_callback' => ['tl_recommendation_archive', 'editHeader'] + ], + 'copy' => [ 'href' => 'act=copy', 'icon' => 'copy.svg', - 'button_callback' => array('tl_recommendation_archive', 'copyArchive') - ), - 'delete' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['delete'], + 'button_callback' => ['tl_recommendation_archive', 'copyArchive'] + ], + 'delete' => [ 'href' => 'act=delete', 'icon' => 'delete.svg', 'attributes' => 'onclick="if(!confirm(\'' . ($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null) . '\'))return false;Backend.getScrollOffset()"', - 'button_callback' => array('tl_recommendation_archive', 'deleteArchive') - ), - 'show' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['show'], + 'button_callback' => ['tl_recommendation_archive', 'deleteArchive'] + ], + 'show' => [ 'href' => 'act=show', 'icon' => 'show.svg' - ) - ) - ), + ] + ] + ], // Palettes - 'palettes' => array - ( - '__selector__' => array('protected'), + 'palettes' => [ + '__selector__' => ['protected'], 'default' => '{title_legend},title,jumpTo;{protected_legend:hide},protected' - ), + ], // Subpalettes - 'subpalettes' => array - ( + 'subpalettes' => [ 'protected' => 'groups' - ), + ], // Fields - 'fields' => array - ( - 'id' => array - ( + 'fields' => [ + 'id' => [ 'sql' => "int(10) unsigned NOT NULL auto_increment" - ), - 'tstamp' => array - ( + ], + 'tstamp' => [ 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'title' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['title'], + ], + 'title' => [ 'exclude' => true, 'search' => true, 'inputType' => 'text', - 'eval' => array('mandatory'=>true, 'maxlength'=>255, 'tl_class'=>'w50'), + 'eval' => ['mandatory'=>true, 'maxlength'=>255, 'tl_class'=>'w50'], 'sql' => "varchar(255) NOT NULL default ''" - ), - 'jumpTo' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['jumpTo'], + ], + 'jumpTo' => [ 'exclude' => true, 'inputType' => 'pageTree', 'foreignKey' => 'tl_page.title', - 'eval' => array('fieldType'=>'radio', 'tl_class'=>'clr'), + 'eval' => ['fieldType'=>'radio', 'tl_class'=>'clr'], 'sql' => "int(10) unsigned NOT NULL default '0'", - 'relation' => array('type'=>'hasOne', 'load'=>'eager') - ), - 'protected' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['protected'], + 'relation' => ['type'=>'hasOne', 'load'=>'eager'] + ], + 'protected' => [ 'exclude' => true, 'filter' => true, 'inputType' => 'checkbox', - 'eval' => array('submitOnChange'=>true), + 'eval' => ['submitOnChange'=>true], 'sql' => "char(1) NOT NULL default ''" - ), - 'groups' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_archive']['groups'], + ], + 'groups' => [ 'exclude' => true, 'inputType' => 'checkbox', 'foreignKey' => 'tl_member_group.name', - 'eval' => array('mandatory'=>true, 'multiple'=>true), + 'eval' => ['mandatory'=>true, 'multiple'=>true], 'sql' => "blob NULL", - 'relation' => array('type'=>'hasMany', 'load'=>'lazy') - ) - ) -); + 'relation' => ['type'=>'hasMany', 'load'=>'lazy'] + ] + ] +]; /** * Provide miscellaneous methods that are used by the data configuration array. @@ -209,7 +169,7 @@ public function checkPermission() // Set root IDs if (empty($this->User->recommendations) || !is_array($this->User->recommendations)) { - $root = array(0); + $root = [0]; } else { @@ -267,7 +227,7 @@ public function checkPermission() if (Contao\Input::get('act') == 'deleteAll' && !$this->User->hasAccess('delete', 'recommendationp')) { - $session['CURRENT']['IDS'] = array(); + $session['CURRENT']['IDS'] = []; } else { @@ -306,7 +266,7 @@ public function adjustPermissions($insertId) // Set root IDs if (empty($this->User->recommendations) || !is_array($this->User->recommendations)) { - $root = array(0); + $root = [0]; } else { @@ -385,7 +345,7 @@ public function adjustPermissions($insertId) */ public function editHeader($row, $href, $label, $title, $icon, $attributes) { - return $this->User->canEditFieldsOf('tl_recommendation_archive') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)).' '; + return $this->User->canEditFieldsOf('tl_recommendation_archive') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' tl_recommendation_archive.php'; } /** @@ -402,7 +362,7 @@ public function editHeader($row, $href, $label, $title, $icon, $attributes) */ public function copyArchive($row, $href, $label, $title, $icon, $attributes) { - return $this->User->hasAccess('create', 'recommendationp') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)).' '; + return $this->User->hasAccess('create', 'recommendationp') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' tl_recommendation_archive.php'; } /** @@ -419,7 +379,7 @@ public function copyArchive($row, $href, $label, $title, $icon, $attributes) */ public function deleteArchive($row, $href, $label, $title, $icon, $attributes) { - return $this->User->hasAccess('delete', 'recommendationp') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)).' '; + return $this->User->hasAccess('delete', 'recommendationp') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' tl_recommendation_archive.php'; } /** @@ -436,6 +396,6 @@ public function addSitemapCacheInvalidationTag($dc, array $tags) return $tags; } - return array_merge($tags, array('contao.sitemap.' . $pageModel->rootId)); + return array_merge($tags, ['contao.sitemap.' . $pageModel->rootId]); } } diff --git a/contao/dca/tl_recommendation_settings.php b/contao/dca/tl_recommendation_settings.php new file mode 100644 index 0000000..9be3b28 --- /dev/null +++ b/contao/dca/tl_recommendation_settings.php @@ -0,0 +1,33 @@ + [ + 'dataContainer' => 'File', + 'closed' => true + ], + + // Palettes + 'palettes' => [ + 'default' => '{recommendation_legend},recommendationDefaultImage,recommendationActiveColor;' + ], + + // Fields + 'fields' => [ + 'recommendationDefaultImage' => [ + 'inputType' => 'fileTree', + 'eval' => ['fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Contao\Config::get('validImageTypes'), 'tl_class'=>'clr'] + ], + 'recommendationActiveColor' => [ + 'inputType' => 'text', + 'eval' => ['maxlength'=>6, 'multiple'=>true, 'size'=>1, 'colorpicker'=>true, 'isHexColor'=>true, 'decodeEntities'=>true, 'tl_class'=>'w50 wizard'], + ] + ] +]; diff --git a/contao/dca/tl_user.php b/contao/dca/tl_user.php new file mode 100644 index 0000000..a1cde50 --- /dev/null +++ b/contao/dca/tl_user.php @@ -0,0 +1,35 @@ +addLegend('recommendation_legend', 'amg_legend', PaletteManipulator::POSITION_BEFORE) + ->addField(['recommendations', 'recommendationp'], 'recommendation_legend', PaletteManipulator::POSITION_APPEND) + ->applyToPalette('extend', 'tl_user') + ->applyToPalette('custom', 'tl_user') +; + +// Add fields to tl_user_group +$GLOBALS['TL_DCA']['tl_user']['fields']['recommendations'] = [ + 'exclude' => true, + 'inputType' => 'checkbox', + 'foreignKey' => 'tl_recommendation_archive.title', + 'eval' => ['multiple'=>true], + 'sql' => "blob NULL" +]; + +$GLOBALS['TL_DCA']['tl_user']['fields']['recommendationp'] = [ + 'exclude' => true, + 'inputType' => 'checkbox', + 'options' => ['create', 'delete'], + 'reference' => &$GLOBALS['TL_LANG']['MSC'], + 'eval' => ['multiple'=>true], + 'sql' => "blob NULL" +]; diff --git a/src/Resources/contao/dca/tl_user_group.php b/contao/dca/tl_user_group.php similarity index 50% rename from src/Resources/contao/dca/tl_user_group.php rename to contao/dca/tl_user_group.php index 52071ad..0af419e 100644 --- a/src/Resources/contao/dca/tl_user_group.php +++ b/contao/dca/tl_user_group.php @@ -6,31 +6,29 @@ * (c) https://www.oveleon.de/ */ +use Contao\CoreBundle\DataContainer\PaletteManipulator; + // Extend the default palette -Contao\CoreBundle\DataContainer\PaletteManipulator::create() - ->addLegend('recommendation_legend', 'amg_legend', Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_BEFORE) - ->addField(array('recommendations', 'recommendationp'), 'recommendation_legend', Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_APPEND) +PaletteManipulator::create() + ->addLegend('recommendation_legend', 'amg_legend', PaletteManipulator::POSITION_BEFORE) + ->addField(['recommendations', 'recommendationp'], 'recommendation_legend', PaletteManipulator::POSITION_APPEND) ->applyToPalette('default', 'tl_user_group') ; // Add fields to tl_user_group -$GLOBALS['TL_DCA']['tl_user_group']['fields']['recommendations'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_user']['recommendations'], +$GLOBALS['TL_DCA']['tl_user_group']['fields']['recommendations'] = [ 'exclude' => true, 'inputType' => 'checkbox', 'foreignKey' => 'tl_recommendation_archive.title', - 'eval' => array('multiple'=>true), + 'eval' => ['multiple'=>true], 'sql' => "blob NULL" -); +]; -$GLOBALS['TL_DCA']['tl_user_group']['fields']['recommendationp'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_user']['recommendationp'], +$GLOBALS['TL_DCA']['tl_user_group']['fields']['recommendationp'] = [ 'exclude' => true, 'inputType' => 'checkbox', - 'options' => array('create', 'delete'), + 'options' => ['create', 'delete'], 'reference' => &$GLOBALS['TL_LANG']['MSC'], - 'eval' => array('multiple'=>true), + 'eval' => ['multiple'=>true], 'sql' => "blob NULL" -); +]; diff --git a/src/Resources/contao/languages/de/default.xlf b/contao/languages/de/default.xlf similarity index 70% rename from src/Resources/contao/languages/de/default.xlf rename to contao/languages/de/default.xlf index 1d7701f..97241b7 100644 --- a/src/Resources/contao/languages/de/default.xlf +++ b/contao/languages/de/default.xlf @@ -1,5 +1,5 @@ - + Currently there are no recommendations. diff --git a/src/Resources/contao/languages/de/modules.xlf b/contao/languages/de/modules.xlf similarity index 94% rename from src/Resources/contao/languages/de/modules.xlf rename to contao/languages/de/modules.xlf index a187565..9b6d02e 100644 --- a/src/Resources/contao/languages/de/modules.xlf +++ b/contao/languages/de/modules.xlf @@ -1,5 +1,5 @@ - + Recommendations diff --git a/src/Resources/contao/languages/de/tl_module.xlf b/contao/languages/de/tl_module.xlf similarity index 98% rename from src/Resources/contao/languages/de/tl_module.xlf rename to contao/languages/de/tl_module.xlf index 123da80..8fed375 100644 --- a/src/Resources/contao/languages/de/tl_module.xlf +++ b/contao/languages/de/tl_module.xlf @@ -1,5 +1,5 @@ - + Data security diff --git a/src/Resources/contao/languages/de/tl_recommendation.xlf b/contao/languages/de/tl_recommendation.xlf similarity index 99% rename from src/Resources/contao/languages/de/tl_recommendation.xlf rename to contao/languages/de/tl_recommendation.xlf index db80856..f5b4311 100644 --- a/src/Resources/contao/languages/de/tl_recommendation.xlf +++ b/contao/languages/de/tl_recommendation.xlf @@ -1,5 +1,5 @@ - + Not verified diff --git a/src/Resources/contao/languages/de/tl_recommendation_archive.xlf b/contao/languages/de/tl_recommendation_archive.xlf similarity index 97% rename from src/Resources/contao/languages/de/tl_recommendation_archive.xlf rename to contao/languages/de/tl_recommendation_archive.xlf index 6647544..fee7720 100644 --- a/src/Resources/contao/languages/de/tl_recommendation_archive.xlf +++ b/contao/languages/de/tl_recommendation_archive.xlf @@ -1,5 +1,5 @@ - + Title diff --git a/src/Resources/contao/languages/de/tl_recommendation_list.xlf b/contao/languages/de/tl_recommendation_list.xlf similarity index 94% rename from src/Resources/contao/languages/de/tl_recommendation_list.xlf rename to contao/languages/de/tl_recommendation_list.xlf index 8cb36e9..4dcc15c 100644 --- a/src/Resources/contao/languages/de/tl_recommendation_list.xlf +++ b/contao/languages/de/tl_recommendation_list.xlf @@ -1,5 +1,5 @@ - + Show all recommendations diff --git a/src/Resources/contao/languages/de/tl_recommendation_notification.xlf b/contao/languages/de/tl_recommendation_notification.xlf similarity index 95% rename from src/Resources/contao/languages/de/tl_recommendation_notification.xlf rename to contao/languages/de/tl_recommendation_notification.xlf index a1ba652..faafae8 100644 --- a/src/Resources/contao/languages/de/tl_recommendation_notification.xlf +++ b/contao/languages/de/tl_recommendation_notification.xlf @@ -1,5 +1,5 @@ - + Your recommendation has been added and is now pending for approval. diff --git a/src/Resources/contao/languages/de/tl_recommendation_settings.xlf b/contao/languages/de/tl_recommendation_settings.xlf similarity index 89% rename from src/Resources/contao/languages/de/tl_recommendation_settings.xlf rename to contao/languages/de/tl_recommendation_settings.xlf index 37347ab..cd147f0 100644 --- a/src/Resources/contao/languages/de/tl_recommendation_settings.xlf +++ b/contao/languages/de/tl_recommendation_settings.xlf @@ -1,5 +1,5 @@ - + Default image diff --git a/src/Resources/contao/languages/de/tl_user.xlf b/contao/languages/de/tl_user.xlf similarity index 89% rename from src/Resources/contao/languages/de/tl_user.xlf rename to contao/languages/de/tl_user.xlf index e0bbd9b..305763a 100644 --- a/src/Resources/contao/languages/de/tl_user.xlf +++ b/contao/languages/de/tl_user.xlf @@ -1,5 +1,5 @@ - + Allowed archives diff --git a/contao/languages/de/tl_user_group.xlf b/contao/languages/de/tl_user_group.xlf new file mode 100644 index 0000000..faf9fe6 --- /dev/null +++ b/contao/languages/de/tl_user_group.xlf @@ -0,0 +1,26 @@ + + + + + Recommendation permissions + Bewertungsrechte + + + Allowed archives + Erlaubte Archive + + + Here you can grant access to one or more recommendation archives. + Hier können Sie den Zugriff auf ein oder mehrere Bewertungs-Archive erlauben. + + + Recommendation permissions + Archivrechte + + + Here you can define the recommendation permissions. + Hier können Sie die Archivrechte festlegen. + + + + diff --git a/src/Resources/contao/languages/en/default.xlf b/contao/languages/en/default.xlf similarity index 68% rename from src/Resources/contao/languages/en/default.xlf rename to contao/languages/en/default.xlf index 48abcdb..7244fee 100644 --- a/src/Resources/contao/languages/en/default.xlf +++ b/contao/languages/en/default.xlf @@ -1,5 +1,5 @@ - + Currently there are no recommendations. diff --git a/src/Resources/contao/languages/en/modules.xlf b/contao/languages/en/modules.xlf similarity index 93% rename from src/Resources/contao/languages/en/modules.xlf rename to contao/languages/en/modules.xlf index 6002ff0..cb85905 100644 --- a/src/Resources/contao/languages/en/modules.xlf +++ b/contao/languages/en/modules.xlf @@ -1,5 +1,5 @@ - + Recommendations diff --git a/src/Resources/contao/languages/en/tl_module.xlf b/contao/languages/en/tl_module.xlf similarity index 98% rename from src/Resources/contao/languages/en/tl_module.xlf rename to contao/languages/en/tl_module.xlf index a7e0744..45df350 100644 --- a/src/Resources/contao/languages/en/tl_module.xlf +++ b/contao/languages/en/tl_module.xlf @@ -1,5 +1,5 @@ - + Data security diff --git a/src/Resources/contao/languages/en/tl_recommendation.xlf b/contao/languages/en/tl_recommendation.xlf similarity index 98% rename from src/Resources/contao/languages/en/tl_recommendation.xlf rename to contao/languages/en/tl_recommendation.xlf index 665104b..1073f30 100644 --- a/src/Resources/contao/languages/en/tl_recommendation.xlf +++ b/contao/languages/en/tl_recommendation.xlf @@ -1,5 +1,5 @@ - + Not verified diff --git a/src/Resources/contao/languages/en/tl_recommendation_archive.xlf b/contao/languages/en/tl_recommendation_archive.xlf similarity index 96% rename from src/Resources/contao/languages/en/tl_recommendation_archive.xlf rename to contao/languages/en/tl_recommendation_archive.xlf index 65cea85..d44fc23 100644 --- a/src/Resources/contao/languages/en/tl_recommendation_archive.xlf +++ b/contao/languages/en/tl_recommendation_archive.xlf @@ -1,5 +1,5 @@ - + Title diff --git a/src/Resources/contao/languages/en/tl_recommendation_list.xlf b/contao/languages/en/tl_recommendation_list.xlf similarity index 93% rename from src/Resources/contao/languages/en/tl_recommendation_list.xlf rename to contao/languages/en/tl_recommendation_list.xlf index c602cf1..7014289 100644 --- a/src/Resources/contao/languages/en/tl_recommendation_list.xlf +++ b/contao/languages/en/tl_recommendation_list.xlf @@ -1,5 +1,5 @@ - + Show all recommendations diff --git a/src/Resources/contao/languages/en/tl_recommendation_notification.xlf b/contao/languages/en/tl_recommendation_notification.xlf similarity index 93% rename from src/Resources/contao/languages/en/tl_recommendation_notification.xlf rename to contao/languages/en/tl_recommendation_notification.xlf index 32535f5..dda2248 100644 --- a/src/Resources/contao/languages/en/tl_recommendation_notification.xlf +++ b/contao/languages/en/tl_recommendation_notification.xlf @@ -1,5 +1,5 @@ - + Your recommendation has been added and is now pending for approval. diff --git a/src/Resources/contao/languages/en/tl_recommendation_settings.xlf b/contao/languages/en/tl_recommendation_settings.xlf similarity index 88% rename from src/Resources/contao/languages/en/tl_recommendation_settings.xlf rename to contao/languages/en/tl_recommendation_settings.xlf index e4e3396..15c2e9a 100644 --- a/src/Resources/contao/languages/en/tl_recommendation_settings.xlf +++ b/contao/languages/en/tl_recommendation_settings.xlf @@ -1,5 +1,5 @@ - + Default image diff --git a/src/Resources/contao/languages/en/tl_user.xlf b/contao/languages/en/tl_user.xlf similarity index 88% rename from src/Resources/contao/languages/en/tl_user.xlf rename to contao/languages/en/tl_user.xlf index 26da2b3..59e1c15 100644 --- a/src/Resources/contao/languages/en/tl_user.xlf +++ b/contao/languages/en/tl_user.xlf @@ -1,5 +1,5 @@ - + Allowed archives diff --git a/contao/languages/en/tl_user_group.xlf b/contao/languages/en/tl_user_group.xlf new file mode 100644 index 0000000..aa0555d --- /dev/null +++ b/contao/languages/en/tl_user_group.xlf @@ -0,0 +1,21 @@ + + + + + Recommendation permissions + + + Allowed archives + + + Here you can grant access to one or more recommendation archives. + + + Recommendation permissions + + + Here you can define the recommendation permissions. + + + + diff --git a/src/Resources/contao/models/RecommendationArchiveModel.php b/contao/models/RecommendationArchiveModel.php similarity index 100% rename from src/Resources/contao/models/RecommendationArchiveModel.php rename to contao/models/RecommendationArchiveModel.php diff --git a/src/Resources/contao/models/RecommendationModel.php b/contao/models/RecommendationModel.php similarity index 97% rename from src/Resources/contao/models/RecommendationModel.php rename to contao/models/RecommendationModel.php index 45f6ee0..9bcd114 100644 --- a/src/Resources/contao/models/RecommendationModel.php +++ b/contao/models/RecommendationModel.php @@ -133,7 +133,7 @@ public static function findPublishedByParentAndIdOrAlias($varId, $arrPids, array } $t = static::$strTable; - $arrColumns = !is_numeric($varId) ? array("$t.alias=?") : array("$t.id=?"); + $arrColumns = !is_numeric($varId) ? ["$t.alias=?"] : ["$t.id=?"]; $arrColumns[] = "$t.pid IN(" . implode(',', array_map('\intval', $arrPids)) . ") AND $t.verified='1'"; if (!static::isPreviewMode($arrOptions)) @@ -156,7 +156,7 @@ public static function findPublishedByParentAndIdOrAlias($varId, $arrPids, array public static function findPublishedByPid($intPid, array $arrOptions=array()) { $t = static::$strTable; - $arrColumns = array("$t.pid=? AND $t.verified='1'"); + $arrColumns = ["$t.pid=? AND $t.verified='1'"]; if (!static::isPreviewMode($arrOptions)) { @@ -191,7 +191,7 @@ public static function findPublishedByPids($arrPids, $blnFeatured=null, $intLimi } $t = static::$strTable; - $arrColumns = array("$t.pid IN(" . implode(',', array_map('\intval', $arrPids)) . ") AND $t.verified='1'"); + $arrColumns = ["$t.pid IN(" . implode(',', array_map('\intval', $arrPids)) . ") AND $t.verified='1'"]; if ($blnFeatured === true) { @@ -241,7 +241,7 @@ public static function countPublishedByPids($arrPids, $blnFeatured=null, $minRat } $t = static::$strTable; - $arrColumns = array("$t.pid IN(" . implode(',', array_map('\intval', $arrPids)) . ") AND $t.verified='1'"); + $arrColumns = ["$t.pid IN(" . implode(',', array_map('\intval', $arrPids)) . ") AND $t.verified='1'"]; if ($blnFeatured === true) { diff --git a/src/Resources/contao/modules/ModuleRecommendation.php b/contao/modules/ModuleRecommendation.php similarity index 97% rename from src/Resources/contao/modules/ModuleRecommendation.php rename to contao/modules/ModuleRecommendation.php index d55781a..40cee75 100644 --- a/src/Resources/contao/modules/ModuleRecommendation.php +++ b/contao/modules/ModuleRecommendation.php @@ -48,7 +48,7 @@ protected function sortOutProtected($arrArchives) $this->import(FrontendUser::class, 'User'); $objArchive = RecommendationArchiveModel::findMultipleByIds($arrArchives); - $arrArchives = array(); + $arrArchives = []; if ($objArchive !== null) { @@ -181,7 +181,7 @@ protected function parseRecommendation($objRecommendation, $objRecommendationArc if (System::getContainer()->has('fos_http_cache.http.symfony_response_tagger')) { $responseTagger = System::getContainer()->get('fos_http_cache.http.symfony_response_tagger'); - $responseTagger->addTags(array('contao.db.tl_recommendation.' . $objRecommendation->id)); + $responseTagger->addTags(['contao.db.tl_recommendation.' . $objRecommendation->id]); } return $objTemplate->parse(); @@ -200,11 +200,11 @@ protected function parseRecommendations($objRecommendations) if ($limit < 1) { - return array(); + return []; } $count = 0; - $arrRecommendations = array(); + $arrRecommendations = []; while ($objRecommendations->next()) { @@ -233,13 +233,13 @@ protected function getMetaFields($objRecommendation) if (!\is_array($meta)) { - return array(); + return []; } /** @var PageModel $objPage */ global $objPage; - $return = array(); + $return = []; foreach ($meta as $field) { diff --git a/src/Resources/contao/modules/ModuleRecommendationForm.php b/contao/modules/ModuleRecommendationForm.php similarity index 89% rename from src/Resources/contao/modules/ModuleRecommendationForm.php rename to contao/modules/ModuleRecommendationForm.php index 4957d83..0308fab 100644 --- a/src/Resources/contao/modules/ModuleRecommendationForm.php +++ b/contao/modules/ModuleRecommendationForm.php @@ -98,70 +98,61 @@ protected function compile() } // Form fields - $arrFields = array - ( - 'author' => array - ( + $arrFields = [ + 'author' => [ 'name' => 'author', 'label' => $GLOBALS['TL_LANG']['tl_recommendation']['author'], 'inputType' => 'text', - 'eval' => array('mandatory'=>true, 'maxlength'=>128) - ), - 'rating' => array - ( + 'eval' => ['mandatory'=>true, 'maxlength'=>128] + ], + 'rating' => [ 'name' => 'rating', 'label' => $GLOBALS['TL_LANG']['tl_recommendation']['rating'], 'inputType' => 'select', - 'options' => array(5,4,3,2,1), - 'eval' => array('mandatory'=>true) - ), - 'title' => array - ( + 'options' => [5,4,3,2,1], + 'eval' => ['mandatory'=>true] + ], + 'title' => [ 'name' => 'title', 'label' => $GLOBALS['TL_LANG']['tl_recommendation']['title'], 'inputType' => 'text', - 'eval' => array('optional'=>true, 'maxlength'=>255), - ), - 'customField' => array - ( + 'eval' => ['optional'=>true, 'maxlength'=>255], + ], + 'customField' => [ 'name' => 'customField', 'label' => $this->recommendation_customFieldLabel ?: $GLOBALS['TL_LANG']['tl_recommendation']['customFieldLabel'], 'inputType' => 'text', - 'eval' => array('optional'=>true, 'maxlength'=>255), - ), - 'location' => array - ( + 'eval' => ['optional'=>true, 'maxlength'=>255], + ], + 'location' => [ 'name' => 'location', 'label' => $GLOBALS['TL_LANG']['tl_recommendation']['location'], 'inputType' => 'text', - 'eval' => array('optional'=>true, 'maxlength'=>128), - ), - 'text' => array - ( + 'eval' => ['optional'=>true, 'maxlength'=>128], + ], + 'text' => [ 'name' => 'text', 'label' => $GLOBALS['TL_LANG']['tl_recommendation']['text'], 'inputType' => 'textarea', - 'eval' => array('mandatory'=>true, 'rows'=>4, 'cols'=>40) - ), - 'email' => array - ( + 'eval' => ['mandatory'=>true, 'rows'=>4, 'cols'=>40] + ], + 'email' => [ 'name' => 'email', 'label' => $GLOBALS['TL_LANG']['tl_recommendation']['email'], 'inputType' => 'text', - 'eval' => array('optional'=>true, 'maxlength'=>255, 'rgxp'=>'email', 'decodeEntities'=>true), - ), - ); + 'eval' => ['optional'=>true, 'maxlength'=>255, 'rgxp'=>'email', 'decodeEntities'=>true], + ], + ]; // Captcha if (!$this->recommendation_disableCaptcha == true) { - $arrFields['captcha'] = array - ( + $arrFields['captcha'] = [ 'name' => 'captcha', 'label' => $GLOBALS['TL_LANG']['MSC']['securityQuestion'], 'inputType' => 'captcha', - 'eval' => array('mandatory'=>true) - ); + 'eval' => ['mandatory'=>true] + ]; } // Set e-mail as mandatory and non-optional if comments should be validated via activation mail @@ -174,17 +165,16 @@ protected function compile() // Set an opt-in checkbox when privacy text is given if ($this->recommendation_privacyText) { - $arrFields['privacy'] = array - ( + $arrFields['privacy'] = [ 'name' => 'privacy', 'inputType' => 'checkbox', - 'options' => array(1=>$this->recommendation_privacyText), - 'eval' => array('mandatory'=>true) - ); + 'options' => [1=>$this->recommendation_privacyText], + 'eval' => ['mandatory'=>true] + ]; } $doNotSubmit = false; - $arrWidgets = array(); + $arrWidgets = []; $strFormId = 'recommendation_' . $this->id; // Optional recommendation form fields @@ -255,7 +245,7 @@ protected function compile() // Do not parse any tags in the recommendation $strText = StringUtil::specialchars(trim($arrWidgets['text']->value)); - $strText = str_replace(array('&', '<', '>'), array('[&]', '[lt]', '[gt]'), $strText); + $strText = str_replace(['&', '<', '>'], ['[&]', '[lt]', '[gt]'], $strText); // Remove multiple line feeds $strText = preg_replace('@\n\n+@', "\n\n", $strText); @@ -383,7 +373,7 @@ protected function sendNotificationMail($objRecommendation) // Convert the recommendation to plain text $strText = strip_tags($strText); $strText = StringUtil::decodeEntities($strText); - $strText = str_replace(array('[&]', '[lt]', '[gt]'), array('&', '<', '>'), $strText); + $strText = str_replace(['[&]', '[lt]', '[gt]'], ['&', '<', '>'], $strText); // Add the recommendation details $objEmail->text = sprintf( @@ -415,7 +405,7 @@ protected function sendVerificationMail($arrData, $id) { /** @var OptIn $optIn */ $optIn = System::getContainer()->get('contao.opt-in'); - $optInToken = $optIn->create('rec', $arrData['email'], array('tl_recommendation'=>array($id))); + $optInToken = $optIn->create('rec', $arrData['email'], ['tl_recommendation'=> [$id]]); // Prepare the simple token data $arrTokenData = $arrData; @@ -455,7 +445,7 @@ protected function verifyRecommendation() return; } - $arrRecommendations = array(); + $arrRecommendations = []; foreach ($arrIds as $intId) { @@ -492,7 +482,7 @@ protected function verifyRecommendation() // Log activity $logger = System::getContainer()->get('monolog.logger.contao'); - $logger->log(LogLevel::INFO, 'Recommendation ID ' . $objRecommendation->id . ' (' . Idna::decodeEmail($objRecommendation->email) . ') has been verified', array('contao' => new ContaoContext(__METHOD__, TL_ACCESS))); + $logger->log(LogLevel::INFO, 'Recommendation ID ' . $objRecommendation->id . ' (' . Idna::decodeEmail($objRecommendation->email) . ') has been verified', ['contao' => new ContaoContext(__METHOD__, TL_ACCESS)]); // Redirect to the jumpTo page if (($objTarget = $this->objModel->getRelated('recommendation_activateJumpTo')) instanceof PageModel) diff --git a/src/Resources/contao/modules/ModuleRecommendationList.php b/contao/modules/ModuleRecommendationList.php similarity index 98% rename from src/Resources/contao/modules/ModuleRecommendationList.php rename to contao/modules/ModuleRecommendationList.php index b414791..5fd1155 100644 --- a/src/Resources/contao/modules/ModuleRecommendationList.php +++ b/contao/modules/ModuleRecommendationList.php @@ -114,7 +114,7 @@ protected function compile() $blnFeatured = null; } - $this->Template->recommendations = array(); + $this->Template->recommendations = []; $this->Template->empty = $GLOBALS['TL_LANG']['MSC']['emptyRecommendationList']; // Get the total number of items @@ -257,6 +257,6 @@ protected function fetchItems($recommendationArchives, $blnFeatured, $limit, $of $order .= "$t.date DESC"; } - return RecommendationModel::findPublishedByPids($recommendationArchives, $blnFeatured, $limit, $offset, $minRating, array('order'=>$order)); + return RecommendationModel::findPublishedByPids($recommendationArchives, $blnFeatured, $limit, $offset, $minRating, ['order'=>$order]); } } diff --git a/src/Resources/contao/modules/ModuleRecommendationReader.php b/contao/modules/ModuleRecommendationReader.php similarity index 100% rename from src/Resources/contao/modules/ModuleRecommendationReader.php rename to contao/modules/ModuleRecommendationReader.php diff --git a/src/Resources/contao/templates/modules/mod_recommendationform.html5 b/contao/templates/modules/mod_recommendationform.html5 similarity index 100% rename from src/Resources/contao/templates/modules/mod_recommendationform.html5 rename to contao/templates/modules/mod_recommendationform.html5 diff --git a/src/Resources/contao/templates/modules/mod_recommendationlist.html5 b/contao/templates/modules/mod_recommendationlist.html5 similarity index 100% rename from src/Resources/contao/templates/modules/mod_recommendationlist.html5 rename to contao/templates/modules/mod_recommendationlist.html5 diff --git a/src/Resources/contao/templates/modules/mod_recommendationreader.html5 b/contao/templates/modules/mod_recommendationreader.html5 similarity index 100% rename from src/Resources/contao/templates/modules/mod_recommendationreader.html5 rename to contao/templates/modules/mod_recommendationreader.html5 diff --git a/src/Resources/contao/templates/recommendation/recommendation_default.html5 b/contao/templates/recommendation/recommendation_default.html5 similarity index 100% rename from src/Resources/contao/templates/recommendation/recommendation_default.html5 rename to contao/templates/recommendation/recommendation_default.html5 diff --git a/src/Resources/contao/templates/recommendation/recommendation_full.html5 b/contao/templates/recommendation/recommendation_full.html5 similarity index 100% rename from src/Resources/contao/templates/recommendation/recommendation_full.html5 rename to contao/templates/recommendation/recommendation_full.html5 diff --git a/src/Resources/contao/templates/recommendation/recommendation_latest.html5 b/contao/templates/recommendation/recommendation_latest.html5 similarity index 100% rename from src/Resources/contao/templates/recommendation/recommendation_latest.html5 rename to contao/templates/recommendation/recommendation_latest.html5 diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index ba93dbd..f7c583c 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -26,12 +26,12 @@ class Plugin implements BundlePluginInterface /** * {@inheritdoc} */ - public function getBundles(ParserInterface $parser) + public function getBundles(ParserInterface $parser): array { return [ BundleConfig::create(ContaoRecommendationBundle::class) + ->setReplace(['recommendation']) ->setLoadAfter([ContaoCoreBundle::class]) - ->setReplace(['recommendation']), ]; } } diff --git a/src/ContaoRecommendationBundle.php b/src/ContaoRecommendationBundle.php index 462578f..065b109 100644 --- a/src/ContaoRecommendationBundle.php +++ b/src/ContaoRecommendationBundle.php @@ -17,4 +17,8 @@ */ class ContaoRecommendationBundle extends Bundle { + public function getPath(): string + { + return \dirname(__DIR__); + } } diff --git a/src/Resources/contao/dca/tl_recommendation_settings.php b/src/Resources/contao/dca/tl_recommendation_settings.php deleted file mode 100644 index 6c81a7c..0000000 --- a/src/Resources/contao/dca/tl_recommendation_settings.php +++ /dev/null @@ -1,41 +0,0 @@ - array - ( - 'dataContainer' => 'File', - 'closed' => true - ), - - // Palettes - 'palettes' => array - ( - 'default' => '{recommendation_legend},recommendationDefaultImage,recommendationActiveColor;' - ), - - // Fields - 'fields' => array - ( - 'recommendationDefaultImage' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_settings']['recommendationDefaultImage'], - 'inputType' => 'fileTree', - 'eval' => array('fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Contao\Config::get('validImageTypes'), 'tl_class'=>'clr') - ), - 'recommendationActiveColor' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_recommendation_settings']['recommendationActiveColor'], - 'inputType' => 'text', - 'eval' => array('maxlength'=>6, 'multiple'=>true, 'size'=>1, 'colorpicker'=>true, 'isHexColor'=>true, 'decodeEntities'=>true, 'tl_class'=>'w50 wizard'), - ) - ) -); diff --git a/src/Resources/contao/dca/tl_user.php b/src/Resources/contao/dca/tl_user.php deleted file mode 100644 index 88907c4..0000000 --- a/src/Resources/contao/dca/tl_user.php +++ /dev/null @@ -1,37 +0,0 @@ -addLegend('recommendation_legend', 'amg_legend', Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_BEFORE) - ->addField(array('recommendations', 'recommendationp'), 'recommendation_legend', Contao\CoreBundle\DataContainer\PaletteManipulator::POSITION_APPEND) - ->applyToPalette('extend', 'tl_user') - ->applyToPalette('custom', 'tl_user') -; - -// Add fields to tl_user_group -$GLOBALS['TL_DCA']['tl_user']['fields']['recommendations'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_user']['recommendations'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'foreignKey' => 'tl_recommendation_archive.title', - 'eval' => array('multiple'=>true), - 'sql' => "blob NULL" -); - -$GLOBALS['TL_DCA']['tl_user']['fields']['recommendationp'] = array -( - 'label' => &$GLOBALS['TL_LANG']['tl_user']['recommendationp'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'options' => array('create', 'delete'), - 'reference' => &$GLOBALS['TL_LANG']['MSC'], - 'eval' => array('multiple'=>true), - 'sql' => "blob NULL" -); diff --git a/src/Resources/contao/languages/de/tl_user_group.xlf b/src/Resources/contao/languages/de/tl_user_group.xlf deleted file mode 100644 index a2d719b..0000000 --- a/src/Resources/contao/languages/de/tl_user_group.xlf +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Recommendation permissions - Bewertungsrechte - - - - diff --git a/src/Resources/contao/languages/en/tl_user_group.xlf b/src/Resources/contao/languages/en/tl_user_group.xlf deleted file mode 100644 index e79a5e2..0000000 --- a/src/Resources/contao/languages/en/tl_user_group.xlf +++ /dev/null @@ -1,9 +0,0 @@ - - - - - Recommendation permissions - - - - From 3c91ed1d29e1076fe0b7b84a0b5f8c486e27e18a Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 16:40:22 +0200 Subject: [PATCH 03/19] Prepare DataContainerListener for recommendations and archive --- contao/dca/tl_recommendation.php | 475 +----------------- contao/dca/tl_recommendation_archive.php | 25 +- .../de/tl_recommendation_archive.xlf | 2 +- .../en/tl_recommendation_archive.xlf | 2 +- contao/modules/ModuleRecommendationReader.php | 2 +- src/ContaoRecommendationPermissions.php | 12 + .../DataContainer/DataContainerListener.php | 209 ++++++++ 7 files changed, 256 insertions(+), 471 deletions(-) create mode 100644 src/ContaoRecommendationPermissions.php create mode 100644 src/EventListener/DataContainer/DataContainerListener.php diff --git a/contao/dca/tl_recommendation.php b/contao/dca/tl_recommendation.php index 4a47eda..0937f76 100644 --- a/contao/dca/tl_recommendation.php +++ b/contao/dca/tl_recommendation.php @@ -6,17 +6,20 @@ * (c) https://www.oveleon.de/ */ +use Contao\DataContainer; +use Contao\DC_Table; +use Oveleon\ContaoRecommendationBundle\EventListener\DataContainer\DataContainerListener; use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; $GLOBALS['TL_DCA']['tl_recommendation'] = [ // Config 'config' => [ - 'dataContainer' => 'Table', + 'dataContainer' => DC_Table::class, 'ptable' => 'tl_recommendation_archive', 'switchToEdit' => true, 'enableVersioning' => true, 'onload_callback' => [ - ['tl_recommendation', 'checkPermission'], + [DataContainerListener::class, 'checkRecommendationPermission'], ['tl_recommendation', 'generateSitemap'] ], 'oncut_callback' => [ @@ -26,7 +29,7 @@ ['tl_recommendation', 'scheduleUpdate'] ], 'onsubmit_callback' => [ - ['tl_recommendation', 'adjustTime'], + [DataContainerListener::class, 'adjustTime'], ['tl_recommendation', 'scheduleUpdate'] ], 'oninvalidate_cache_tags_callback' => [ @@ -44,7 +47,7 @@ // List 'list' => [ 'sorting' => [ - 'mode' => 4, + 'mode' => DataContainer::MODE_PARENT, 'fields' => ['date DESC'], 'headerFields' => ['title', 'jumpTo', 'tstamp', 'protected'], 'panelLayout' => 'filter;sort,search,limit', @@ -77,14 +80,13 @@ 'attributes' => 'onclick="if(!confirm(\'' . ($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null) . '\'))return false;Backend.getScrollOffset()"' ], 'toggle' => [ + 'href' => 'act=toggle&field=published', 'icon' => 'visible.svg', - 'attributes' => 'onclick="Backend.getScrollOffset();return AjaxRequest.toggleVisibility(this,%s)"', - 'button_callback' => ['tl_recommendation', 'toggleIcon'] + 'showInHeader' => true ], 'feature' => [ + 'href' => 'act=toggle&field=featured', 'icon' => 'featured.svg', - 'attributes' => 'onclick="Backend.getScrollOffset();return AjaxRequest.toggleFeatured(this,%s)"', - 'button_callback' => ['tl_recommendation', 'iconFeatured'] ], 'show' => [ 'href' => 'act=show', @@ -115,7 +117,7 @@ 'exclude' => true, 'search' => true, 'sorting' => true, - 'flag' => 1, + 'flag' => DataContainer::SORT_INITIAL_LETTER_ASC, 'inputType' => 'text', 'eval' => ['maxlength'=>255, 'tl_class'=>'w50 clr'], 'sql' => "varchar(255) NOT NULL default ''" @@ -126,7 +128,7 @@ 'inputType' => 'text', 'eval' => ['rgxp'=>'alias', 'doNotCopy'=>true, 'unique'=>true, 'maxlength'=>128, 'tl_class'=>'w50'], 'save_callback' => [ - ['tl_recommendation', 'generateAlias'] + [DataContainerListener::class, 'generateRecommendationAlias'] ], 'sql' => "varchar(255) BINARY NOT NULL default ''" ], @@ -134,7 +136,7 @@ 'exclude' => true, 'search' => true, 'sorting' => true, - 'flag' => 1, + 'flag' => DataContainer::SORT_INITIAL_LETTER_ASC, 'inputType' => 'text', 'eval' => ['doNotCopy'=>true, 'mandatory'=>true, 'maxlength'=>128, 'tl_class'=>'w50'], 'sql' => "varchar(128) NOT NULL default ''" @@ -150,7 +152,7 @@ 'exclude' => true, 'search' => true, 'sorting' => true, - 'flag' => 1, + 'flag' => DataContainer::SORT_INITIAL_LETTER_ASC, 'inputType' => 'text', 'eval' => ['doNotCopy'=>true, 'maxlength'=>128, 'tl_class'=>'w50'], 'sql' => "varchar(128) NOT NULL default ''" @@ -160,11 +162,11 @@ 'exclude' => true, 'filter' => true, 'sorting' => true, - 'flag' => 8, + 'flag' => DataContainer::SORT_MONTH_DESC, 'inputType' => 'text', 'eval' => ['rgxp'=>'date', 'mandatory'=>true, 'doNotCopy'=>true, 'datepicker'=>true, 'tl_class'=>'w50 wizard'], 'load_callback' => [ - ['tl_recommendation', 'loadDate'] + [DataContainerListener::class, 'loadDate'] ], 'sql' => "int(10) unsigned NOT NULL default 0" ], @@ -174,7 +176,7 @@ 'inputType' => 'text', 'eval' => ['rgxp'=>'time', 'mandatory'=>true, 'doNotCopy'=>true, 'tl_class'=>'w50'], 'load_callback' => [ - ['tl_recommendation', 'loadTime'] + [DataContainerListener::class, 'loadTime'] ], 'sql' => "int(10) unsigned NOT NULL default 0" ], @@ -237,7 +239,7 @@ 'published' => [ 'exclude' => true, 'filter' => true, - 'flag' => 1, + 'flag' => DataContainer::SORT_INITIAL_LETTER_ASC, 'inputType' => 'checkbox', 'eval' => ['doNotCopy'=>true], 'sql' => "char(1) NOT NULL default ''" @@ -273,191 +275,6 @@ public function __construct() $this->import('Contao\BackendUser', 'User'); } - /** - * Check permissions to edit table tl_recommendation - * - * @throws Contao\CoreBundle\Exception\AccessDeniedException - */ - public function checkPermission() - { - if ($this->User->isAdmin) - { - return; - } - - // Set the root IDs - if (empty($this->User->recommendations) || !is_array($this->User->recommendations)) - { - $root = [0]; - } - else - { - $root = $this->User->recommendations; - } - - $id = strlen(Contao\Input::get('id')) ? Contao\Input::get('id') : CURRENT_ID; - - // Check current action - switch (Contao\Input::get('act')) - { - case 'paste': - case 'select': - if (!in_array(CURRENT_ID, $root)) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to access recommendation archive ID ' . $id . '.'); - } - break; - - case 'create': - if (!Contao\Input::get('pid') || !in_array(Contao\Input::get('pid'), $root)) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to create recommendation item in recommendation archive ID ' . Input::get('pid') . '.'); - } - break; - - case 'cut': - case 'copy': - if (Contao\Input::get('act') == 'cut' && Contao\Input::get('mode') == 1) - { - $objArchive = $this->Database->prepare("SELECT pid FROM tl_recommendation WHERE id=?") - ->limit(1) - ->execute(Contao\Input::get('pid')); - - if ($objArchive->numRows < 1) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Invalid recommendation item ID ' . Contao\Input::get('pid') . '.'); - } - - $pid = $objArchive->pid; - } - else - { - $pid = Input::get('pid'); - } - - if (!in_array($pid, $root)) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to ' . Contao\Input::get('act') . ' recommendation item ID ' . $id . ' to recommendation archive ID ' . $pid . '.'); - } - // no break - - case 'edit': - case 'show': - case 'delete': - case 'toggle': - case 'feature': - $objArchive = $this->Database->prepare("SELECT pid FROM tl_recommendation WHERE id=?") - ->limit(1) - ->execute($id); - - if ($objArchive->numRows < 1) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Invalid recommendation item ID ' . $id . '.'); - } - - if (!in_array($objArchive->pid, $root)) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to ' . Contao\Input::get('act') . ' recommendation item ID ' . $id . ' of recommendation archive ID ' . $objArchive->pid . '.'); - } - break; - - case 'editAll': - case 'deleteAll': - case 'overrideAll': - case 'cutAll': - case 'copyAll': - if (!in_array($id, $root)) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to access recommendation archive ID ' . $id . '.'); - } - - $objArchive = $this->Database->prepare("SELECT id FROM tl_recommendation WHERE pid=?") - ->execute($id); - - /** @var Symfony\Component\HttpFoundation\Session\SessionInterface $objSession */ - $objSession = Contao\System::getContainer()->get('session'); - - $session = $objSession->all(); - $session['CURRENT']['IDS'] = array_intersect((array) $session['CURRENT']['IDS'], $objArchive->fetchEach('id')); - $objSession->replace($session); - break; - - default: - if (Contao\Input::get('act')) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Invalid command "' . Contao\Input::get('act') . '".'); - } - - if (!in_array($id, $root)) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to access recommendation archive ID ' . $id . '.'); - } - break; - } - } - - /** - * Auto-generate the recommendation alias if it has not been set yet - * - * @param mixed $varValue - * @param Contao\DataContainer $dc - * - * @return string - * - * @throws Exception - */ - public function generateAlias($varValue, Contao\DataContainer $dc) - { - $autoAlias = false; - - // Generate alias if title is set - if ($varValue == '' && !empty($dc->activeRecord->title)) - { - $autoAlias = true; - $varValue = Contao\StringUtil::generateAlias($dc->activeRecord->title); - } - - $objAlias = $this->Database->prepare("SELECT id FROM tl_recommendation WHERE alias=? AND alias!='' AND id!=?") - ->execute($varValue, $dc->id); - - // Check whether the recommendation alias exists - if ($objAlias->numRows) - { - if (!$autoAlias) - { - throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasExists'], $varValue)); - } - - $varValue .= '-' . $dc->id; - } - - return $varValue; - } - - /** - * Set the timestamp to 00:00:00 - * - * @param integer $value - * - * @return integer - */ - public function loadDate($value) - { - return strtotime(date('Y-m-d', $value) . ' 00:00:00'); - } - - /** - * Set the timestamp to 1970-01-01 - * - * @param integer $value - * - * @return integer - */ - public function loadTime($value) - { - return strtotime('1970-01-01 ' . date('H:i:s', $value)); - } - /** * List a recommendation record * @@ -475,25 +292,6 @@ public function listRecommendations($arrRow) return '
' . $arrRow['author'] . ' [' . Date::parse(Config::get('datimFormat'), $arrRow['date']) . ']
'; } - /** - * Adjust start end end time of the event based on date, span, startTime and endTime - * - * @param Contao\DataContainer $dc - */ - public function adjustTime(Contao\DataContainer $dc) - { - // Return if there is no active record (override all) - if (!$dc->activeRecord) - { - return; - } - - $arrSet['date'] = strtotime(date('Y-m-d', $dc->activeRecord->date) . ' tl_recommendation.php' . date('H:i:s', $dc->activeRecord->time)); - $arrSet['time'] = $arrSet['date']; - - $this->Database->prepare("UPDATE tl_recommendation %s WHERE id=?")->set($arrSet)->execute($dc->id); - } - /** * Check for modified recommendation and update the XML files if necessary */ @@ -542,243 +340,6 @@ public function scheduleUpdate(Contao\DataContainer $dc) $objSession->set('recommendation_updater', array_unique($session)); } - /** - * Return the "feature/unfeature element" button - * - * @param array $row - * @param string $href - * @param string $label - * @param string $title - * @param string $icon - * @param string $attributes - * - * @return string - */ - public function iconFeatured($row, $href, $label, $title, $icon, $attributes) - { - if (strlen(Contao\Input::get('fid'))) - { - $this->toggleFeatured(Contao\Input::get('fid'), (Input::get('state') == 1), (@func_get_arg(12) ?: null)); - $this->redirect($this->getReferer()); - } - - // Check permissions AFTER checking the fid, so hacking attempts are logged - if (!$this->User->hasAccess('tl_recommendation::featured', 'alexf')) - { - return ''; - } - - $href .= '&fid='.$row['id'].'&state='.($row['featured'] ? '' : 1); - - if (!$row['featured']) - { - $icon = 'featured_.svg'; - } - - return ''.Contao\Image::getHtml($icon, $label, 'data-state="' . ($row['featured'] ? 1 : 0) . '"').' '; - } - - /** - * Feature/unfeature a recommendation - * - * @param integer $intId - * @param boolean $blnVisible - * @param Contao\DataContainer $dc - * - * @throws Contao\CoreBundle\Exception\AccessDeniedException - */ - public function toggleFeatured($intId, $blnVisible, Contao\DataContainer $dc=null) - { - // Check permissions to edit - Contao\Input::setGet('id', $intId); - Contao\Input::setGet('act', 'feature'); - $this->checkPermission(); - - // Check permissions to feature - if (!$this->User->hasAccess('tl_recommendation::featured', 'alexf')) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to feature/unfeature recommendation ID ' . $intId . '.'); - } - - $objVersions = new Contao\Versions('tl_recommendation', $intId); - $objVersions->initialize(); - - // Trigger the save_callback - if (is_array($GLOBALS['TL_DCA']['tl_recommendation']['fields']['featured']['save_callback'] ?? null)) - { - foreach ($GLOBALS['TL_DCA']['tl_recommendation']['fields']['featured']['save_callback'] as $callback) - { - if (is_array($callback)) - { - $this->import($callback[0]); - $blnVisible = $this->{$callback[0]}->{$callback[1]}($blnVisible, $dc); - } - elseif (is_callable($callback)) - { - $blnVisible = $callback($blnVisible, $this); - } - } - } - - // Update the database - $this->Database->prepare("UPDATE tl_recommendation SET tstamp=". time() .", featured='" . ($blnVisible ? 1 : '') . "' WHERE id=?") - ->execute($intId); - - $objVersions->create(); - - if ($dc) - { - $dc->invalidateCacheTags(); - } - } - - /** - * Return the "toggle visibility" button - * - * @param array $row - * @param string $href - * @param string $label - * @param string $title - * @param string $icon - * @param string $attributes - * - * @return string - */ - public function toggleIcon($row, $href, $label, $title, $icon, $attributes) - { - if (strlen(Input::get('tid'))) - { - $this->toggleVisibility(Contao\Input::get('tid'), (Contao\Input::get('state') == 1), (func_num_args() <= 12 ? null : func_get_arg(12))); - $this->redirect($this->getReferer()); - } - - // Check permissions AFTER checking the tid, so hacking attempts are logged - if (!$this->User->hasAccess('tl_recommendation::published', 'alexf')) - { - return ''; - } - - $href .= '&tid=' . $row['id'] . '&state=' . ($row['published'] ? '' : 1); - - if (!$row['published']) - { - $icon = 'invisible.svg'; - } - - return '' . Image::getHtml($icon, $label, 'data-state="' . ($row['published'] ? 1 : 0) . '"') . ' '; - } - - /** - * Disable/enable a recommendation - * - * @param integer $intId - * @param boolean $blnVisible - * @param Contao\DataContainer $dc - */ - public function toggleVisibility($intId, $blnVisible, Contao\DataContainer $dc=null) - { - // Set the ID and action - Contao\Input::setGet('id', $intId); - Contao\Input::setGet('act', 'toggle'); - - if ($dc) - { - $dc->id = $intId; - } - - // Trigger the onload_callback - if (is_array($GLOBALS['TL_DCA']['tl_recommendation']['config']['onload_callback'] ?? null)) - { - foreach ($GLOBALS['TL_DCA']['tl_recommendation']['config']['onload_callback'] as $callback) - { - if (is_array($callback)) - { - $this->import($callback[0]); - $this->{$callback[0]}->{$callback[1]}($dc); - } - elseif (is_callable($callback)) - { - $callback($dc); - } - } - } - - // Check the field access - if (!$this->User->hasAccess('tl_recommendation::published', 'alexf')) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to publish/unpublish recommendation ID ' . $intId . '.'); - } - - // Set the current record - if ($dc) - { - $objRow = $this->Database->prepare("SELECT * FROM tl_recommendation WHERE id=?") - ->limit(1) - ->execute($intId); - - if ($objRow->numRows) - { - $dc->activeRecord = $objRow; - } - } - - $objVersions = new Contao\Versions('tl_recommendation', $intId); - $objVersions->initialize(); - - // Trigger the save_callback - if (is_array($GLOBALS['TL_DCA']['tl_recommendation']['fields']['published']['save_callback'] ?? null)) - { - foreach ($GLOBALS['TL_DCA']['tl_recommendation']['fields']['published']['save_callback'] as $callback) - { - if (is_array($callback)) - { - $this->import($callback[0]); - $blnVisible = $this->{$callback[0]}->{$callback[1]}($blnVisible, $dc); - } - elseif (is_callable($callback)) - { - $blnVisible = $callback($blnVisible, $dc); - } - } - } - - $time = time(); - - // Update the database - $this->Database->prepare("UPDATE tl_recommendation SET tstamp=$time, published='" . ($blnVisible ? '1' : '') . "' WHERE id=?") - ->execute($intId); - - if ($dc) - { - $dc->activeRecord->tstamp = $time; - $dc->activeRecord->published = ($blnVisible ? '1' : ''); - } - - // Trigger the onsubmit_callback - if (is_array($GLOBALS['TL_DCA']['tl_recommendation']['config']['onsubmit_callback'] ?? null)) - { - foreach ($GLOBALS['TL_DCA']['tl_recommendation']['config']['onsubmit_callback'] as $callback) - { - if (is_array($callback)) - { - $this->import($callback[0]); - $this->{$callback[0]}->{$callback[1]}($dc); - } - elseif (is_callable($callback)) - { - $callback($dc); - } - } - } - - $objVersions->create(); - - if ($dc) - { - $dc->invalidateCacheTags(); - } - } - /** * @param Contao\DataContainer $dc * diff --git a/contao/dca/tl_recommendation_archive.php b/contao/dca/tl_recommendation_archive.php index 066517e..b0ffa0d 100644 --- a/contao/dca/tl_recommendation_archive.php +++ b/contao/dca/tl_recommendation_archive.php @@ -6,10 +6,14 @@ * (c) https://www.oveleon.de/ */ +use Contao\DataContainer; +use Contao\DC_Table; +use Contao\PageModel; + $GLOBALS['TL_DCA']['tl_recommendation_archive'] = [ // Config 'config' => [ - 'dataContainer' => 'Table', + 'dataContainer' => DC_Table::class, 'ctable' => ['tl_recommendation'], 'switchToEdit' => true, 'enableVersioning' => true, @@ -35,9 +39,9 @@ // List 'list' => [ 'sorting' => [ - 'mode' => 1, + 'mode' => DataContainer::MODE_SORTED, 'fields' => ['title'], - 'flag' => 1, + 'flag' => DataContainer::SORT_INITIAL_LETTER_ASC, 'panelLayout' => 'filter;search,limit' ], 'label' => [ @@ -58,25 +62,25 @@ ] ], 'operations' => [ - 'edit' => [ - 'href' => 'table=tl_recommendation', - 'icon' => 'edit.svg' - ], 'editheader' => [ 'href' => 'act=edit', 'icon' => 'header.svg', - 'button_callback' => ['tl_recommendation_archive', 'editHeader'] + //'button_callback' => ['tl_recommendation_archive', 'editHeader'] + ], + 'edit' => [ + 'href' => 'table=tl_recommendation', + 'icon' => 'edit.svg' ], 'copy' => [ 'href' => 'act=copy', 'icon' => 'copy.svg', - 'button_callback' => ['tl_recommendation_archive', 'copyArchive'] + //'button_callback' => ['tl_recommendation_archive', 'copyArchive'] ], 'delete' => [ 'href' => 'act=delete', 'icon' => 'delete.svg', 'attributes' => 'onclick="if(!confirm(\'' . ($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null) . '\'))return false;Backend.getScrollOffset()"', - 'button_callback' => ['tl_recommendation_archive', 'deleteArchive'] + //'button_callback' => ['tl_recommendation_archive', 'deleteArchive'] ], 'show' => [ 'href' => 'act=show', @@ -144,7 +148,6 @@ */ class tl_recommendation_archive extends Contao\Backend { - /** * Import the back end user object */ diff --git a/contao/languages/de/tl_recommendation_archive.xlf b/contao/languages/de/tl_recommendation_archive.xlf index fee7720..dade3fa 100644 --- a/contao/languages/de/tl_recommendation_archive.xlf +++ b/contao/languages/de/tl_recommendation_archive.xlf @@ -6,7 +6,7 @@ Titel
- Please enter a news archive title. + Please enter a recommendation archive title. Bitte geben Sie den Archiv-Titel ein. diff --git a/contao/languages/en/tl_recommendation_archive.xlf b/contao/languages/en/tl_recommendation_archive.xlf index d44fc23..966798d 100644 --- a/contao/languages/en/tl_recommendation_archive.xlf +++ b/contao/languages/en/tl_recommendation_archive.xlf @@ -5,7 +5,7 @@ Title - Please enter a news archive title. + Please enter a recommendation archive title. Redirect page diff --git a/contao/modules/ModuleRecommendationReader.php b/contao/modules/ModuleRecommendationReader.php index 465d6db..e534e78 100644 --- a/contao/modules/ModuleRecommendationReader.php +++ b/contao/modules/ModuleRecommendationReader.php @@ -85,7 +85,7 @@ protected function compile() $this->Template->referer = 'javascript:history.go(-1)'; $this->Template->back = $GLOBALS['TL_LANG']['MSC']['goBack']; - // Get the news item + // Get the recommendation item $objRecommendation = RecommendationModel::findPublishedByParentAndIdOrAlias(Input::get('items'), $this->recommendation_archives); if (null === $objRecommendation) diff --git a/src/ContaoRecommendationPermissions.php b/src/ContaoRecommendationPermissions.php new file mode 100644 index 0000000..6d2aa2f --- /dev/null +++ b/src/ContaoRecommendationPermissions.php @@ -0,0 +1,12 @@ +activeRecord) + { + return; + } + + $arrSet['date'] = strtotime(date('Y-m-d', $dc->activeRecord->date) . ' ' . date('H:i:s', $dc->activeRecord->time)); + $arrSet['time'] = $arrSet['date']; + + $db = Database::getInstance(); + $db->prepare("UPDATE tl_recommendation %s WHERE id=?")->set($arrSet)->execute($dc->id); + } + + /** + * @throws Exception + */ + public function generateRecommendationAlias($varValue, DataContainer $dc) + { + $db = Database::getInstance(); + + $aliasExists = function (string $alias) use ($dc, $db): bool + { + return $db->prepare("SELECT id FROM tl_recommendation WHERE alias=? AND id!=?")->execute($alias, $dc->id)->numRows > 0; + }; + + // Generate alias if there is none + if (!$varValue) + { + $varValue = System::getContainer()->get('contao.slug')->generate($dc->activeRecord->title, RecommendationArchiveModel::findByPk($dc->activeRecord->pid)->jumpTo, $aliasExists); + } + elseif (preg_match('/^[1-9]\d*$/', $varValue)) + { + throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasNumeric'], $varValue)); + } + elseif ($aliasExists($varValue)) + { + throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasExists'], $varValue)); + } + + return $varValue; + + } + + public function checkRecommendationPermission(DataContainer $dc) + { + $objUser = Controller::getContainer()->get('security.helper')->getUser(); + + if ($objUser->isAdmin) + { + return; + } + + // Set the root IDs + if (empty($objUser->recommendations) || !is_array($objUser->recommendations)) + { + $root = [0]; + } + else + { + $root = $objUser->recommendations; + } + + $id = strlen(Input::get('id')) ? Input::get('id') : $dc->currentPid; + $db = Database::getInstance(); + + // Check current action + switch (Input::get('act')) + { + case 'paste': + case 'select': + // Check currentPid here (see #247) + if (!in_array($dc->currentPid, $root)) + { + throw new AccessDeniedException('Not enough permissions to access recommendation archive ID ' . $id . '.'); + } + break; + + case 'create': + if (!Input::get('pid') || !in_array(Input::get('pid'), $root)) + { + throw new AccessDeniedException('Not enough permissions to create recommendation items in recommendation archive ID ' . Input::get('pid') . '.'); + } + break; + + case 'cut': + case 'copy': + if (Input::get('act') == 'cut' && Input::get('mode') == 1) + { + $objArchive = $db->prepare("SELECT pid FROM tl_recommendation WHERE id=?") + ->limit(1) + ->execute(Input::get('pid')); + + if ($objArchive->numRows < 1) + { + throw new AccessDeniedException('Invalid recommendation item ID ' . Input::get('pid') . '.'); + } + + $pid = $objArchive->pid; + } + else + { + $pid = Input::get('pid'); + } + + if (!in_array($pid, $root)) + { + throw new AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' recommendation item ID ' . $id . ' to recommendation archive ID ' . $pid . '.'); + } + // no break + + case 'edit': + case 'show': + case 'delete': + case 'toggle': + $objArchive = $db->prepare("SELECT pid FROM tl_recommendation WHERE id=?") + ->limit(1) + ->execute($id); + + if ($objArchive->numRows < 1) + { + throw new AccessDeniedException('Invalid recommendation item ID ' . $id . '.'); + } + + if (!in_array($objArchive->pid, $root)) + { + throw new AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' recommendation item ID ' . $id . ' of recommendation archive ID ' . $objArchive->pid . '.'); + } + break; + + case 'editAll': + case 'deleteAll': + case 'overrideAll': + case 'cutAll': + case 'copyAll': + if (!in_array($id, $root)) + { + throw new AccessDeniedException('Not enough permissions to access recommendation archive ID ' . $id . '.'); + } + + $objArchive = $db->prepare("SELECT id FROM tl_recommendation WHERE pid=?") + ->execute($id); + + $objSession = System::getContainer()->get('request_stack')->getSession(); + + $session = $objSession->all(); + $session['CURRENT']['IDS'] = array_intersect((array) $session['CURRENT']['IDS'], $objArchive->fetchEach('id')); + $objSession->replace($session); + break; + + default: + if (Input::get('act')) + { + throw new AccessDeniedException('Invalid command "' . Input::get('act') . '".'); + } + + if (!in_array($id, $root)) + { + throw new AccessDeniedException('Not enough permissions to access recommendation archive ID ' . $id . '.'); + } + break; + } + } +} From 2298b438e6fef04eb72dd98a3b4f2e68ced41486 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 17:05:14 +0200 Subject: [PATCH 04/19] Register DataContainerListener --- config/services.yaml | 9 +++++++++ .../ContaoRecommendationExtension.php | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 config/services.yaml create mode 100644 src/DependencyInjection/ContaoRecommendationExtension.php diff --git a/config/services.yaml b/config/services.yaml new file mode 100644 index 0000000..e31ca06 --- /dev/null +++ b/config/services.yaml @@ -0,0 +1,9 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: true + + Oveleon\ContaoRecommendationBundle\: + resource: '../src/' + exclude: '../src/{Model,DependencyInjection,Resources}' diff --git a/src/DependencyInjection/ContaoRecommendationExtension.php b/src/DependencyInjection/ContaoRecommendationExtension.php new file mode 100644 index 0000000..947e245 --- /dev/null +++ b/src/DependencyInjection/ContaoRecommendationExtension.php @@ -0,0 +1,20 @@ +load('services.yaml'); + } +} From b21dc22dbf0499f952cbe76bd12a094119553368 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 17:05:30 +0200 Subject: [PATCH 05/19] Add featured and publish toggle --- contao/dca/tl_recommendation.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contao/dca/tl_recommendation.php b/contao/dca/tl_recommendation.php index 0937f76..5f67a63 100644 --- a/contao/dca/tl_recommendation.php +++ b/contao/dca/tl_recommendation.php @@ -226,6 +226,7 @@ ], 'featured' => [ 'exclude' => true, + 'toggle' => true, 'filter' => true, 'inputType' => 'checkbox', 'eval' => ['tl_class'=>'w50 m12'], @@ -238,6 +239,7 @@ ], 'published' => [ 'exclude' => true, + 'toggle' => true, 'filter' => true, 'flag' => DataContainer::SORT_INITIAL_LETTER_ASC, 'inputType' => 'checkbox', From 69194b033cf6b9859cd963f7c4c23bb6e88fd168 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 22:37:11 +0200 Subject: [PATCH 06/19] Rewrite DataContainer listeners --- .../RecommendationArchiveListener.php | 151 ++++++++++++++++++ ...istener.php => RecommendationListener.php} | 85 +++++++++- 2 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 src/EventListener/DataContainer/RecommendationArchiveListener.php rename src/EventListener/DataContainer/{DataContainerListener.php => RecommendationListener.php} (71%) diff --git a/src/EventListener/DataContainer/RecommendationArchiveListener.php b/src/EventListener/DataContainer/RecommendationArchiveListener.php new file mode 100644 index 0000000..f511a84 --- /dev/null +++ b/src/EventListener/DataContainer/RecommendationArchiveListener.php @@ -0,0 +1,151 @@ +get('security.helper')->isGranted(ContaoCorePermissions::USER_CAN_EDIT_FIELDS_OF_TABLE, 'tl_recommendation_archive') ? '' . Image::getHtml($icon, $label) . ' ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' '; + } + + /** + * Return the copy archive button + */ + public function copyArchive(array $row, string $href, string $label, string $title, string $icon, string $attributes): string + { + return System::getContainer()->get('security.helper')->isGranted(ContaoRecommendationPermissions::USER_CAN_CREATE_ARCHIVES) ? '' . Image::getHtml($icon, $label) . ' ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' '; + } + + /** + * Return the delete archive button + */ + public function deleteArchive(array $row, string $href, string $label, string $title, string $icon, string $attributes): string + { + return System::getContainer()->get('security.helper')->isGranted(ContaoRecommendationPermissions::USER_CAN_DELETE_ARCHIVES) ? '' . Image::getHtml($icon, $label) . ' ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' '; + } + + /** + * Add the new archive to the permissions + */ + public function adjustPermissions(int|string $insertId): void + { + // The oncreate_callback passes $insertId as second argument + if (func_num_args() == 4) + { + $insertId = func_get_arg(1); + } + + $objUser = Controller::getContainer()->get('security.helper')->getUser(); + + if ($objUser->isAdmin) + { + return; + } + + // Set root IDs + if (empty($objUser->recommendations) || !is_array($objUser->recommendations)) + { + $root = [0]; + } + else + { + $root = $objUser->recommendations; + } + + // The archive is enabled already + if (in_array($insertId, $root)) + { + return; + } + + /** @var AttributeBagInterface $objSessionBag */ + $objSessionBag = System::getContainer()->get('request_stack')->getSession()->getBag('contao_backend'); + + $arrNew = $objSessionBag->get('new_records'); + + if (is_array($arrNew['tl_recommendation_archive']) && in_array($insertId, $arrNew['tl_recommendation_archive'])) + { + $db = Database::getInstance(); + + // Add the permissions on group level + if ($objUser->inherit != 'custom') + { + $objGroup = $db->execute("SELECT id, recommendations, recommendationp FROM tl_user_group WHERE id IN(" . implode(',', array_map('\intval', $objUser->groups)) . ")"); + + while ($objGroup->next()) + { + $arrRecommendationp = StringUtil::deserialize($objGroup->recommendationp); + + if (is_array($arrRecommendationp) && in_array('create', $arrRecommendationp)) + { + $arrRecommendations = StringUtil::deserialize($objGroup->recommendations, true); + $arrRecommendations[] = $insertId; + + $db->prepare("UPDATE tl_user_group SET recommendations=? WHERE id=?") + ->execute(serialize($arrRecommendations), $objGroup->id); + } + } + } + + // Add the permissions on user level + if ($objUser->inherit != 'group') + { + $objUser = $db->prepare("SELECT recommendations, recommendationp FROM tl_user WHERE id=?") + ->limit(1) + ->execute($objUser->id); + + $arrRecommendationp = StringUtil::deserialize($objUser->recommendationp); + + if (is_array($arrRecommendationp) && in_array('create', $arrRecommendationp)) + { + $arrRecommendations = StringUtil::deserialize($objUser->recommendations, true); + $arrRecommendations[] = $insertId; + + $db->prepare("UPDATE tl_user SET recommendations=? WHERE id=?") + ->execute(serialize($arrRecommendations), $objUser->id); + } + } + + // Add the new element to the user object + $root[] = $insertId; + $objUser->recommendations = $root; + } + } + + public function addSitemapCacheInvalidationTag(DataContainer $dc, array $tags): array + { + $pageModel = PageModel::findWithDetails($dc->activeRecord->jumpTo); + + if ($pageModel === null) + { + return $tags; + } + + return array_merge($tags, ['contao.sitemap.' . $pageModel->rootId]); + } +} diff --git a/src/EventListener/DataContainer/DataContainerListener.php b/src/EventListener/DataContainer/RecommendationListener.php similarity index 71% rename from src/EventListener/DataContainer/DataContainerListener.php rename to src/EventListener/DataContainer/RecommendationListener.php index 1b051a2..0e7f98b 100644 --- a/src/EventListener/DataContainer/DataContainerListener.php +++ b/src/EventListener/DataContainer/RecommendationListener.php @@ -2,19 +2,24 @@ namespace Oveleon\ContaoRecommendationBundle\EventListener\DataContainer; +use Contao\Automator; +use Contao\Config; use Contao\Controller; use Contao\CoreBundle\Exception\AccessDeniedException; use Contao\CoreBundle\Framework\ContaoFramework; use Contao\Database; use Contao\DataContainer; +use Contao\Date; use Contao\Input; +use Contao\PageModel; use Contao\System; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; +use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Core\Security; -class DataContainerListener +class RecommendationListener { public function __construct( protected ContaoFramework $framework, @@ -40,9 +45,8 @@ public function loadTime(int $value): bool|int /** * Adjust start and end time of the event based on date, span, startTime and endTime - * @throws Exception */ - public function adjustTime(DataContainer $dc) + public function adjustTime(DataContainer $dc): void { // Return if there is no active record (override all) if (!$dc->activeRecord) @@ -84,7 +88,6 @@ public function generateRecommendationAlias($varValue, DataContainer $dc) } return $varValue; - } public function checkRecommendationPermission(DataContainer $dc) @@ -114,7 +117,7 @@ public function checkRecommendationPermission(DataContainer $dc) { case 'paste': case 'select': - // Check currentPid here (see #247) + // Check currentPid if (!in_array($dc->currentPid, $root)) { throw new AccessDeniedException('Not enough permissions to access recommendation archive ID ' . $id . '.'); @@ -206,4 +209,76 @@ public function checkRecommendationPermission(DataContainer $dc) break; } } + + /** + * List a recommendation record + */ + public function listRecommendations(array $arrRow): string + { + if(!$arrRow['verified']) + { + return '
' . $arrRow['author'] . ' [' . ($GLOBALS['TL_LANG']['tl_recommendation']['notVerified'] ?? null) . ']
'; + } + + return '
' . $arrRow['author'] . ' [' . Date::parse(Config::get('datimFormat'), $arrRow['date']) . ']
'; + } + + /** + * Check for modified recommendation and update the XML files if necessary + */ + public function generateSitemap(): void + { + /** @var SessionInterface $objSession */ + $objSession = System::getContainer()->get('session'); + + $session = $objSession->get('recommendation_updater'); + + if (empty($session) || !is_array($session)) + { + return; + } + + $automator = new Automator(); + $automator->generateSitemap(); + + $objSession->set('recommendation_updater', null); + } + + /** + * Schedule a recommendation update + * + * This method is triggered when a single recommendation or multiple recommendations + * are modified (edit/editAll), moved (cut/cutAll) or deleted (delete/deleteAll). + * Since duplicated items are unpublished by default, it is not necessary to + * schedule updates on copyAll as well. + */ + public function scheduleUpdate(DataContainer $dc): void + { + // Return if there is no ID + if (!$dc->activeRecord || !$dc->activeRecord->pid || Input::get('act') == 'copy') + { + return; + } + + /** @var SessionInterface $objSession */ + $objSession = System::getContainer()->get('session'); + + // Store the ID in the session + $session = $objSession->get('recommendation_updater'); + $session[] = $dc->activeRecord->pid; + $objSession->set('recommendation_updater', array_unique($session)); + } + + public function addSitemapCacheInvalidationTag(DataContainer $dc, array $tags): array + { + $archiveModel = RecommendationArchiveModel::findByPk($dc->activeRecord->pid); + $pageModel = PageModel::findWithDetails($archiveModel->jumpTo); + + if ($pageModel === null) + { + return $tags; + } + + return array_merge($tags, ['contao.sitemap.' . $pageModel->rootId]); + } } From 8fce6564bb9a06e4ab7b5ed2864afe13151fd7d9 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 22:37:52 +0200 Subject: [PATCH 07/19] Register Cron and SitemapListener --- contao/config/config.php | 32 +++---- src/Cron/PurgeRecommendationsCron.php | 38 ++++++++ src/EventListener/SitemapListener.php | 126 ++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 src/Cron/PurgeRecommendationsCron.php create mode 100644 src/EventListener/SitemapListener.php diff --git a/contao/config/config.php b/contao/config/config.php index a69b991..94433de 100644 --- a/contao/config/config.php +++ b/contao/config/config.php @@ -3,22 +3,22 @@ declare(strict_types=1); use Contao\ArrayUtil; +use Oveleon\ContaoRecommendationBundle\ModuleRecommendationForm; +use Oveleon\ContaoRecommendationBundle\ModuleRecommendationList; +use Oveleon\ContaoRecommendationBundle\ModuleRecommendationReader; +use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; +use Oveleon\ContaoRecommendationBundle\RecommendationModel; // Back end modules ArrayUtil::arrayInsert($GLOBALS['BE_MOD']['content'], 5, [ 'recommendation' => [ - 'tables' => [ - 'tl_recommendation_archive', - 'tl_recommendation' - ] + 'tables' => ['tl_recommendation_archive', 'tl_recommendation'] ] ]); ArrayUtil::arrayInsert($GLOBALS['BE_MOD']['system'], 3, [ 'recommendation_settings' => [ - 'tables' => [ - 'tl_recommendation_settings' - ], + 'tables' => ['tl_recommendation_settings'], 'hideInNavigation' => true ] ]); @@ -26,22 +26,16 @@ // Front end modules ArrayUtil::arrayInsert($GLOBALS['FE_MOD'], 2, [ 'recommendation' => [ - 'recommendationlist' => 'Oveleon\ContaoRecommendationBundle\ModuleRecommendationList', - 'recommendationreader' => 'Oveleon\ContaoRecommendationBundle\ModuleRecommendationReader', - 'recommendationform' => 'Oveleon\ContaoRecommendationBundle\ModuleRecommendationForm', + 'recommendationform' => ModuleRecommendationForm::class, + 'recommendationlist' => ModuleRecommendationList::class, + 'recommendationreader' => ModuleRecommendationReader::class ] ]); -// Models -$GLOBALS['TL_MODELS']['tl_recommendation'] = 'Oveleon\ContaoRecommendationBundle\RecommendationModel'; -$GLOBALS['TL_MODELS']['tl_recommendation_archive'] = 'Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel'; - // Add permissions $GLOBALS['TL_PERMISSIONS'][] = 'recommendations'; $GLOBALS['TL_PERMISSIONS'][] = 'recommendationp'; -// Cron jobs -$GLOBALS['TL_CRON']['daily']['purgeRecommendations'] = ['Oveleon\ContaoRecommendationBundle\Recommendation', 'purgeRecommendations']; - -// Register hooks -$GLOBALS['TL_HOOKS']['getSearchablePages'][] = ['Oveleon\ContaoRecommendationBundle\Recommendation', 'getSearchablePages']; +// Models +$GLOBALS['TL_MODELS']['tl_recommendation'] = RecommendationModel::class; +$GLOBALS['TL_MODELS']['tl_recommendation_archive'] = RecommendationArchiveModel::class; diff --git a/src/Cron/PurgeRecommendationsCron.php b/src/Cron/PurgeRecommendationsCron.php new file mode 100644 index 0000000..a04fd3f --- /dev/null +++ b/src/Cron/PurgeRecommendationsCron.php @@ -0,0 +1,38 @@ +framework->initialize(); + + $recommendations = $this->framework->getAdapter(RecommendationModel::class)->findExpiredRecommendations(); + + if (null === $recommendations) + { + return; + } + + /** @var RecommendationModel $recommendation */ + foreach ($recommendations as $recommendation) + { + $recommendation->delete(); + } + + $this->logger?->info('Purged the unactivated recommendations'); + } +} diff --git a/src/EventListener/SitemapListener.php b/src/EventListener/SitemapListener.php new file mode 100644 index 0000000..6fd311b --- /dev/null +++ b/src/EventListener/SitemapListener.php @@ -0,0 +1,126 @@ +framework->createInstance(Database::class)->getChildRecords($event->getRootPageIds(), 'tl_page'); + + // Early return here in the unlikely case that there are no pages + if (empty($arrRoot)) + { + return; + } + + $arrPages = []; + $time = time(); + + // Get all recommendation archives + $objArchives = $this->framework->getAdapter(RecommendationArchiveModel::class)->findByProtected(''); + + if (null === $objArchives) + { + return; + } + + // Walk through each recommendation archive + foreach ($objArchives as $objArchive) + { + // Skip recommendation archives without target page + if (!$objArchive->jumpTo) + { + continue; + } + + // Skip recommendation archives outside the root nodes + if (!\in_array($objArchive->jumpTo, $arrRoot, true)) + { + continue; + } + + $objParent = $this->framework->getAdapter(PageModel::class)->findWithDetails($objArchive->jumpTo); + + // The target page does not exist + if (null === $objParent) + { + continue; + } + + // The target page has not been published + if (!$objParent->published || ($objParent->start && $objParent->start > $time) || ($objParent->stop && $objParent->stop <= $time)) + { + continue; + } + + // The target page is protected + if ($objParent->protected) + { + continue; + } + + // The target page is exempt from the sitemap + if ('noindex,nofollow' === $objParent->robots) + { + continue; + } + + // Get the items + $objRecommendations = $this->framework->getAdapter(RecommendationModel::class)->findPublishedByPid($objArchive->id); + + if (null === $objRecommendations) + { + continue; + } + + foreach ($objRecommendations as $objRecommendation) + { + $arrPages[] = $objParent->getAbsoluteUrl('/' . ($objRecommendation->alias ?: $objRecommendation->id)); + } + } + + foreach ($arrPages as $strUrl) + { + $this->addUrlToDefaultUrlSet($strUrl, $event); + } + } + + private function addUrlToDefaultUrlSet(string $url, $event): self + { + $sitemap = $event->getDocument(); + $urlSet = $sitemap->getElementsByTagNameNS('https://www.sitemaps.org/schemas/sitemap/0.9', 'urlset')->item(0); + + if (null === $urlSet) + { + return $this; + } + + $loc = $sitemap->createElement('loc', $url); + $urlEl = $sitemap->createElement('url'); + $urlEl->appendChild($loc); + $urlSet->appendChild($urlEl); + + $sitemap->appendChild($urlSet); + + return $this; + } +} From 4d58250445c33b7d52fabbbffcb653d1f09d5db0 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 22:38:41 +0200 Subject: [PATCH 08/19] Update DCAs --- contao/dca/tl_module.php | 22 +- contao/dca/tl_recommendation.php | 125 +--------- contao/dca/tl_recommendation_archive.php | 279 +---------------------- 3 files changed, 23 insertions(+), 403 deletions(-) diff --git a/contao/dca/tl_module.php b/contao/dca/tl_module.php index ef4df7f..cbc5f86 100644 --- a/contao/dca/tl_module.php +++ b/contao/dca/tl_module.php @@ -7,6 +7,7 @@ */ use Contao\System; +use Contao\Controller; System::loadLanguageFile('tl_recommendation'); System::loadLanguageFile('tl_recommendation_notification'); @@ -174,15 +175,8 @@ 'sql' => "varchar(64) NOT NULL default ''" ]; - -/** - * Provide miscellaneous methods that are used by the data configuration array. - * - * @author Fabian Ekert - */ class tl_module_recommendation extends Contao\Backend { - /** * Import the back end user object */ @@ -194,10 +188,8 @@ public function __construct() /** * Get all recommendation archives and return them as array - * - * @return array */ - public function getRecommendationArchives() + public function getRecommendationArchives(): array { if (!$this->User->isAdmin && !is_array($this->User->recommendations)) { @@ -220,10 +212,8 @@ public function getRecommendationArchives() /** * Get all recommendation reader modules and return them as array - * - * @return array */ - public function getReaderModules() + public function getReaderModules(): array { $arrModules = []; $objModules = $this->Database->execute("SELECT m.id, m.name, t.name AS theme FROM tl_module m LEFT JOIN tl_theme t ON m.pid=t.id WHERE m.type='recommendationreader' ORDER BY t.name, m.name"); @@ -238,12 +228,8 @@ public function getReaderModules() /** * Load the default recommendation activation text - * - * @param mixed $varValue - * - * @return mixed */ - public function getRecommendationActivationDefault($varValue) + public function getRecommendationActivationDefault(mixed $varValue): mixed { if (trim($varValue) === '') { diff --git a/contao/dca/tl_recommendation.php b/contao/dca/tl_recommendation.php index 5f67a63..28f655b 100644 --- a/contao/dca/tl_recommendation.php +++ b/contao/dca/tl_recommendation.php @@ -8,8 +8,7 @@ use Contao\DataContainer; use Contao\DC_Table; -use Oveleon\ContaoRecommendationBundle\EventListener\DataContainer\DataContainerListener; -use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; +use Oveleon\ContaoRecommendationBundle\EventListener\DataContainer\RecommendationListener; $GLOBALS['TL_DCA']['tl_recommendation'] = [ // Config @@ -19,21 +18,21 @@ 'switchToEdit' => true, 'enableVersioning' => true, 'onload_callback' => [ - [DataContainerListener::class, 'checkRecommendationPermission'], - ['tl_recommendation', 'generateSitemap'] + [RecommendationListener::class, 'checkRecommendationPermission'], + [RecommendationListener::class, 'generateSitemap'] ], 'oncut_callback' => [ - ['tl_recommendation', 'scheduleUpdate'] + [RecommendationListener::class, 'scheduleUpdate'] ], 'ondelete_callback' => [ - ['tl_recommendation', 'scheduleUpdate'] + [RecommendationListener::class, 'scheduleUpdate'] ], 'onsubmit_callback' => [ - [DataContainerListener::class, 'adjustTime'], - ['tl_recommendation', 'scheduleUpdate'] + [RecommendationListener::class, 'adjustTime'], + [RecommendationListener::class, 'scheduleUpdate'] ], 'oninvalidate_cache_tags_callback' => [ - ['tl_recommendation', 'addSitemapCacheInvalidationTag'], + [RecommendationListener::class, 'addSitemapCacheInvalidationTag'], ], 'sql' => [ 'keys' => [ @@ -51,7 +50,7 @@ 'fields' => ['date DESC'], 'headerFields' => ['title', 'jumpTo', 'tstamp', 'protected'], 'panelLayout' => 'filter;sort,search,limit', - 'child_record_callback' => ['tl_recommendation', 'listRecommendations'], + 'child_record_callback' => [RecommendationListener::class, 'listRecommendations'], 'child_record_class' => 'no_padding' ], 'global_operations' => [ @@ -128,7 +127,7 @@ 'inputType' => 'text', 'eval' => ['rgxp'=>'alias', 'doNotCopy'=>true, 'unique'=>true, 'maxlength'=>128, 'tl_class'=>'w50'], 'save_callback' => [ - [DataContainerListener::class, 'generateRecommendationAlias'] + [RecommendationListener::class, 'generateRecommendationAlias'] ], 'sql' => "varchar(255) BINARY NOT NULL default ''" ], @@ -166,7 +165,7 @@ 'inputType' => 'text', 'eval' => ['rgxp'=>'date', 'mandatory'=>true, 'doNotCopy'=>true, 'datepicker'=>true, 'tl_class'=>'w50 wizard'], 'load_callback' => [ - [DataContainerListener::class, 'loadDate'] + [RecommendationListener::class, 'loadDate'] ], 'sql' => "int(10) unsigned NOT NULL default 0" ], @@ -176,7 +175,7 @@ 'inputType' => 'text', 'eval' => ['rgxp'=>'time', 'mandatory'=>true, 'doNotCopy'=>true, 'tl_class'=>'w50'], 'load_callback' => [ - [DataContainerListener::class, 'loadTime'] + [RecommendationListener::class, 'loadTime'] ], 'sql' => "int(10) unsigned NOT NULL default 0" ], @@ -260,103 +259,3 @@ ] ] ]; - -/** - * Provide miscellaneous methods that are used by the data configuration array. - * - * @author Fabian Ekert - */ -class tl_recommendation extends Contao\Backend -{ - /** - * Import the back end user object - */ - public function __construct() - { - parent::__construct(); - $this->import('Contao\BackendUser', 'User'); - } - - /** - * List a recommendation record - * - * @param array $arrRow - * - * @return string - */ - public function listRecommendations($arrRow) - { - if(!$arrRow['verified']) - { - return '
' . $arrRow['author'] . ' [' . $GLOBALS['TL_LANG']['tl_recommendation']['notVerified'] . ']
'; - } - - return '
' . $arrRow['author'] . ' [' . Date::parse(Config::get('datimFormat'), $arrRow['date']) . ']
'; - } - - /** - * Check for modified recommendation and update the XML files if necessary - */ - public function generateSitemap() - { - /** @var Symfony\Component\HttpFoundation\Session\SessionInterface $objSession */ - $objSession = Contao\System::getContainer()->get('session'); - - $session = $objSession->get('recommendation_updater'); - - if (empty($session) || !is_array($session)) - { - return; - } - - $this->import('Contao\Automator', 'Automator'); - $this->Automator->generateSitemap(); - - $objSession->set('recommendation_updater', null); - } - - /** - * Schedule a recommendation update - * - * This method is triggered when a single recommendation or multiple recommendations - * are modified (edit/editAll), moved (cut/cutAll) or deleted (delete/deleteAll). - * Since duplicated items are unpublished by default, it is not necessary to - * schedule updates on copyAll as well. - * - * @param Contao\DataContainer $dc - */ - public function scheduleUpdate(Contao\DataContainer $dc) - { - // Return if there is no ID - if (!$dc->activeRecord || !$dc->activeRecord->pid || Contao\Input::get('act') == 'copy') - { - return; - } - - /** @var Symfony\Component\HttpFoundation\Session\SessionInterface $objSession */ - $objSession = Contao\System::getContainer()->get('session'); - - // Store the ID in the session - $session = $objSession->get('recommendation_updater'); - $session[] = $dc->activeRecord->pid; - $objSession->set('recommendation_updater', array_unique($session)); - } - - /** - * @param Contao\DataContainer $dc - * - * @return array - */ - public function addSitemapCacheInvalidationTag($dc, array $tags) - { - $archiveModel = RecommendationArchiveModel::findByPk($dc->activeRecord->pid); - $pageModel = Contao\PageModel::findWithDetails($archiveModel->jumpTo); - - if ($pageModel === null) - { - return $tags; - } - - return array_merge($tags, ['contao.sitemap.' . $pageModel->rootId]); - } -} diff --git a/contao/dca/tl_recommendation_archive.php b/contao/dca/tl_recommendation_archive.php index b0ffa0d..fe5a269 100644 --- a/contao/dca/tl_recommendation_archive.php +++ b/contao/dca/tl_recommendation_archive.php @@ -8,7 +8,7 @@ use Contao\DataContainer; use Contao\DC_Table; -use Contao\PageModel; +use Oveleon\ContaoRecommendationBundle\EventListener\DataContainer\RecommendationArchiveListener; $GLOBALS['TL_DCA']['tl_recommendation_archive'] = [ // Config @@ -17,17 +17,14 @@ 'ctable' => ['tl_recommendation'], 'switchToEdit' => true, 'enableVersioning' => true, - 'onload_callback' => [ - ['tl_recommendation_archive', 'checkPermission'] - ], 'oncreate_callback' => [ - ['tl_recommendation_archive', 'adjustPermissions'] + [RecommendationArchiveListener::class, 'adjustPermissions'] ], 'oncopy_callback' => [ - ['tl_recommendation_archive', 'adjustPermissions'] + [RecommendationArchiveListener::class, 'adjustPermissions'] ], 'oninvalidate_cache_tags_callback' => [ - ['tl_recommendation_archive', 'addSitemapCacheInvalidationTag'], + [RecommendationArchiveListener::class, 'addSitemapCacheInvalidationTag'], ], 'sql' => [ 'keys' => [ @@ -65,7 +62,7 @@ 'editheader' => [ 'href' => 'act=edit', 'icon' => 'header.svg', - //'button_callback' => ['tl_recommendation_archive', 'editHeader'] + 'button_callback' => [RecommendationArchiveListener::class, 'editHeader'] ], 'edit' => [ 'href' => 'table=tl_recommendation', @@ -74,13 +71,13 @@ 'copy' => [ 'href' => 'act=copy', 'icon' => 'copy.svg', - //'button_callback' => ['tl_recommendation_archive', 'copyArchive'] + 'button_callback' => [RecommendationArchiveListener::class, 'copyArchive'] ], 'delete' => [ 'href' => 'act=delete', 'icon' => 'delete.svg', 'attributes' => 'onclick="if(!confirm(\'' . ($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null) . '\'))return false;Backend.getScrollOffset()"', - //'button_callback' => ['tl_recommendation_archive', 'deleteArchive'] + 'button_callback' => [RecommendationArchiveListener::class, 'deleteArchive'] ], 'show' => [ 'href' => 'act=show', @@ -140,265 +137,3 @@ ] ] ]; - -/** - * Provide miscellaneous methods that are used by the data configuration array. - * - * @author Fabian Ekert - */ -class tl_recommendation_archive extends Contao\Backend -{ - /** - * Import the back end user object - */ - public function __construct() - { - parent::__construct(); - $this->import('Contao\BackendUser', 'User'); - } - - /** - * Check permissions to edit table tl_recommendation_archive - * - * @throws Contao\CoreBundle\Exception\AccessDeniedException - */ - public function checkPermission() - { - if ($this->User->isAdmin) - { - return; - } - - // Set root IDs - if (empty($this->User->recommendations) || !is_array($this->User->recommendations)) - { - $root = [0]; - } - else - { - $root = $this->User->recommendations; - } - - $GLOBALS['TL_DCA']['tl_recommendation_archive']['list']['sorting']['root'] = $root; - - // Check permissions to add archives - if (!$this->User->hasAccess('create', 'recommendationp')) - { - $GLOBALS['TL_DCA']['tl_recommendation_archive']['config']['closed'] = true; - $GLOBALS['TL_DCA']['tl_recommendation_archive']['config']['notCreatable'] = true; - $GLOBALS['TL_DCA']['tl_recommendation_archive']['config']['notCopyable'] = true; - } - - // Check permissions to delete calendars - if (!$this->User->hasAccess('delete', 'recommendationp')) - { - $GLOBALS['TL_DCA']['tl_recommendation_archive']['config']['notDeletable'] = true; - } - - /** @var Symfony\Component\HttpFoundation\Session\SessionInterface $objSession */ - $objSession = Contao\System::getContainer()->get('session'); - - // Check current action - switch (Contao\Input::get('act')) - { - case 'select': - // Allow - break; - - case 'create': - if (!$this->User->hasAccess('create', 'recommendationp')) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to create recommendation archives.'); - } - break; - - case 'edit': - case 'copy': - case 'delete': - case 'show': - if (!in_array(Contao\Input::get('id'), $root) || (Contao\Input::get('act') == 'delete' && !$this->User->hasAccess('delete', 'recommendationp'))) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to ' . Contao\Input::get('act') . ' recommendation archive ID ' . Contao\Input::get('id') . '.'); - } - break; - - case 'editAll': - case 'deleteAll': - case 'overrideAll': - case 'copyAll': - $session = $objSession->all(); - - if (Contao\Input::get('act') == 'deleteAll' && !$this->User->hasAccess('delete', 'recommendationp')) - { - $session['CURRENT']['IDS'] = []; - } - else - { - $session['CURRENT']['IDS'] = array_intersect((array) $session['CURRENT']['IDS'], $root); - } - $objSession->replace($session); - break; - - default: - if (strlen(Contao\Input::get('act'))) - { - throw new Contao\CoreBundle\Exception\AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' recommendation archives.'); - } - break; - } - } - - /** - * Add the new archive to the permissions - * - * @param $insertId - */ - public function adjustPermissions($insertId) - { - // The oncreate_callback passes $insertId as second argument - if (func_num_args() == 4) - { - $insertId = func_get_arg(1); - } - - if ($this->User->isAdmin) - { - return; - } - - // Set root IDs - if (empty($this->User->recommendations) || !is_array($this->User->recommendations)) - { - $root = [0]; - } - else - { - $root = $this->User->recommendations; - } - - // The archive is enabled already - if (in_array($insertId, $root)) - { - return; - } - - /** @var Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface $objSessionBag */ - $objSessionBag = Contao\System::getContainer()->get('session')->getBag('contao_backend'); - - $arrNew = $objSessionBag->get('new_records'); - - if (is_array($arrNew['tl_recommendation_archive']) && in_array($insertId, $arrNew['tl_recommendation_archive'])) - { - // Add the permissions on group level - if ($this->User->inherit != 'custom') - { - $objGroup = $this->Database->execute("SELECT id, recommendations, recommendationp FROM tl_user_group WHERE id IN(" . implode(',', array_map('\intval', $this->User->groups)) . ")"); - - while ($objGroup->next()) - { - $arrRecommendationp = Contao\StringUtil::deserialize($objGroup->recommendationp); - - if (is_array($arrRecommendationp) && in_array('create', $arrRecommendationp)) - { - $arrRecommendations = Contao\StringUtil::deserialize($objGroup->recommendations, true); - $arrRecommendations[] = $insertId; - - $this->Database->prepare("UPDATE tl_user_group SET recommendations=? WHERE id=?") - ->execute(serialize($arrRecommendations), $objGroup->id); - } - } - } - - // Add the permissions on user level - if ($this->User->inherit != 'group') - { - $objUser = $this->Database->prepare("SELECT recommendations, recommendationp FROM tl_user WHERE id=?") - ->limit(1) - ->execute($this->User->id); - - $arrRecommendationp = Contao\StringUtil::deserialize($objUser->recommendationp); - - if (is_array($arrRecommendationp) && in_array('create', $arrRecommendationp)) - { - $arrRecommendations = Contao\StringUtil::deserialize($objUser->recommendations, true); - $arrRecommendations[] = $insertId; - - $this->Database->prepare("UPDATE tl_user SET recommendations=? WHERE id=?") - ->execute(serialize($arrRecommendations), $this->User->id); - } - } - - // Add the new element to the user object - $root[] = $insertId; - $this->User->recommendations = $root; - } - } - - /** - * Return the edit header button - * - * @param array $row - * @param string $href - * @param string $label - * @param string $title - * @param string $icon - * @param string $attributes - * - * @return string - */ - public function editHeader($row, $href, $label, $title, $icon, $attributes) - { - return $this->User->canEditFieldsOf('tl_recommendation_archive') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' tl_recommendation_archive.php'; - } - - /** - * Return the copy archive button - * - * @param array $row - * @param string $href - * @param string $label - * @param string $title - * @param string $icon - * @param string $attributes - * - * @return string - */ - public function copyArchive($row, $href, $label, $title, $icon, $attributes) - { - return $this->User->hasAccess('create', 'recommendationp') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' tl_recommendation_archive.php'; - } - - /** - * Return the delete archive button - * - * @param array $row - * @param string $href - * @param string $label - * @param string $title - * @param string $icon - * @param string $attributes - * - * @return string - */ - public function deleteArchive($row, $href, $label, $title, $icon, $attributes) - { - return $this->User->hasAccess('delete', 'recommendationp') ? ''.Contao\Image::getHtml($icon, $label).' ' : Contao\Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' tl_recommendation_archive.php'; - } - - /** - * @param Contao\DataContainer $dc - * - * @return array - */ - public function addSitemapCacheInvalidationTag($dc, array $tags) - { - $pageModel = PageModel::findWithDetails($dc->activeRecord->jumpTo); - - if ($pageModel === null) - { - return $tags; - } - - return array_merge($tags, ['contao.sitemap.' . $pageModel->rootId]); - } -} From 8e7cf17318f1c152139c919b5f36c54eb9c2eb6b Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 22:39:15 +0200 Subject: [PATCH 09/19] Add class-aliases --- contao/models/RecommendationArchiveModel.php | 4 ++-- contao/models/RecommendationModel.php | 4 ++-- contao/modules/ModuleRecommendation.php | 5 ++--- contao/modules/ModuleRecommendationForm.php | 3 +++ contao/modules/ModuleRecommendationList.php | 4 ++-- contao/modules/ModuleRecommendationReader.php | 4 ++-- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/contao/models/RecommendationArchiveModel.php b/contao/models/RecommendationArchiveModel.php index b9fe6ad..c78e8da 100644 --- a/contao/models/RecommendationArchiveModel.php +++ b/contao/models/RecommendationArchiveModel.php @@ -46,8 +46,6 @@ * @method static integer countByJumpTo($val, array $opt=array()) * @method static integer countByProtected($val, array $opt=array()) * @method static integer countByGroups($val, array $opt=array()) - * - * @author Fabian Ekert */ class RecommendationArchiveModel extends Model { @@ -58,3 +56,5 @@ class RecommendationArchiveModel extends Model */ protected static $strTable = 'tl_recommendation_archive'; } + +class_alias(RecommendationArchiveModel::class, 'RecommendationArchiveModel'); diff --git a/contao/models/RecommendationModel.php b/contao/models/RecommendationModel.php index 9bcd114..12bfe81 100644 --- a/contao/models/RecommendationModel.php +++ b/contao/models/RecommendationModel.php @@ -104,8 +104,6 @@ * @method static integer countByPublished($val, array $opt=array()) * @method static integer countByStart($val, array $opt=array()) * @method static integer countByStop($val, array $opt=array()) - * - * @author Fabian Ekert */ class RecommendationModel extends Model { @@ -289,3 +287,5 @@ public static function findExpiredRecommendations(array $arrOptions=array()) return static::createCollectionFromDbResult($objResult, $t); } } + +class_alias(RecommendationModel::class, 'RecommendationModel'); diff --git a/contao/modules/ModuleRecommendation.php b/contao/modules/ModuleRecommendation.php index 40cee75..81b38dc 100644 --- a/contao/modules/ModuleRecommendation.php +++ b/contao/modules/ModuleRecommendation.php @@ -25,9 +25,6 @@ * * @property string $recommendation_template * @property mixed $recommendation_metaFields - * - * @author Fabian Ekert - * @author Sebastian Zoglowek */ abstract class ModuleRecommendation extends Module { @@ -56,6 +53,7 @@ protected function sortOutProtected($arrArchives) { if ($objArchive->protected) { + // ToDo: rewrite for contao ^5.x if (!FE_USER_LOGGED_IN) { continue; @@ -320,6 +318,7 @@ protected function isExternal($strPath) */ protected function addInternalImage($objModel, $objRecommendation, &$objTemplate) { + // ToDo: rewrite for contao ^5.x if ($objModel !== null && is_file(TL_ROOT . '/' . $objModel->path)) { $objTemplate->addInternalImage = true; diff --git a/contao/modules/ModuleRecommendationForm.php b/contao/modules/ModuleRecommendationForm.php index 0308fab..d63f81a 100644 --- a/contao/modules/ModuleRecommendationForm.php +++ b/contao/modules/ModuleRecommendationForm.php @@ -481,6 +481,7 @@ protected function verifyRecommendation() $optInToken->confirm(); // Log activity + // ToDo: rewrite for contao ^5.x $logger = System::getContainer()->get('monolog.logger.contao'); $logger->log(LogLevel::INFO, 'Recommendation ID ' . $objRecommendation->id . ' (' . Idna::decodeEmail($objRecommendation->email) . ') has been verified', ['contao' => new ContaoContext(__METHOD__, TL_ACCESS)]); @@ -502,3 +503,5 @@ protected function verifyRecommendation() } } } + +class_alias(ModuleRecommendationForm::class, 'ModuleRecommendationForm'); diff --git a/contao/modules/ModuleRecommendationList.php b/contao/modules/ModuleRecommendationList.php index 5fd1155..4feeb41 100644 --- a/contao/modules/ModuleRecommendationList.php +++ b/contao/modules/ModuleRecommendationList.php @@ -24,8 +24,6 @@ * @property array $recommendation_archives * @property string $recommendation_featured * @property string $recommendation_order - * - * @author Fabian Ekert */ class ModuleRecommendationList extends ModuleRecommendation { @@ -260,3 +258,5 @@ protected function fetchItems($recommendationArchives, $blnFeatured, $limit, $of return RecommendationModel::findPublishedByPids($recommendationArchives, $blnFeatured, $limit, $offset, $minRating, ['order'=>$order]); } } + +class_alias(ModuleRecommendationList::class, 'ModuleRecommendationList'); diff --git a/contao/modules/ModuleRecommendationReader.php b/contao/modules/ModuleRecommendationReader.php index e534e78..1752d10 100644 --- a/contao/modules/ModuleRecommendationReader.php +++ b/contao/modules/ModuleRecommendationReader.php @@ -21,8 +21,6 @@ * Front end module "recommendation reader". * * @property array $recommendation_archives - * - * @author Fabian Ekert */ class ModuleRecommendationReader extends ModuleRecommendation { @@ -100,3 +98,5 @@ protected function compile() $this->Template->recommendation = $arrRecommendation; } } + +class_alias(ModuleRecommendationReader::class, 'ModuleRecommendationReader'); From 7ce0c92471e8c0532a697963195437f917646534 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 22:39:38 +0200 Subject: [PATCH 10/19] Remove unnecessary Recommendation-class (now in DataContainer listeners) --- contao/classes/Recommendation.php | 141 ------------------------------ 1 file changed, 141 deletions(-) delete mode 100644 contao/classes/Recommendation.php diff --git a/contao/classes/Recommendation.php b/contao/classes/Recommendation.php deleted file mode 100644 index 815ef37..0000000 --- a/contao/classes/Recommendation.php +++ /dev/null @@ -1,141 +0,0 @@ - - */ -class Recommendation extends Backend -{ - /** - * Purge recommendations that have not been activated within 24 hours - */ - public function purgeRecommendations() - { - $objRecommendation = RecommendationModel::findExpiredRecommendations(); - - if ($objRecommendation === null) - { - return; - } - - while ($objRecommendation->next()) - { - $objRecommendation->delete(); - } - - // Add a log entry - $logger = System::getContainer()->get('monolog.logger.contao'); - $logger->log(LogLevel::INFO, 'Purged the unactivated recommendations', array('contao' => new ContaoContext(__METHOD__, TL_CRON))); - } - - /** - * Add Recommendations to the indexer - * - * @param array $arrPages - * @param integer $intRoot - * @param boolean $blnIsSitemap - * - * @return array - */ - public function getSearchablePages($arrPages, $intRoot=0, $blnIsSitemap=false) - { - $arrRoot = array(); - - if ($intRoot > 0) - { - $arrRoot = $this->Database->getChildRecords($intRoot, 'tl_page'); - } - - $arrProcessed = array(); - $time = time(); - - // Get all categories - $objRecommendationArchive = RecommendationArchiveModel::findByProtected(''); - - // Walk through each archive - if ($objRecommendationArchive !== null) - { - while($objRecommendationArchive->next()) - { - // Skip archives without a target page - if (!$objRecommendationArchive->jumpTo) - { - continue; - } - - // Skip archives outside the root nodes - if (!empty($arrRoot) && !\in_array($objRecommendationArchive->jumpTo, $arrRoot)) - { - continue; - } - - // Get the URL of the jumpTo page - if (!isset($arrProcessed[$objRecommendationArchive->jumpTo])) - { - $objParent = PageModel::findWithDetails($objRecommendationArchive->jumpTo); - - // The target page does not exist - if ($objParent === null) - { - continue; - } - - // The target page has not been published - if (!$objParent->published || ($objParent->start && $objParent->start > $time) || ($objParent->stop && $objParent->stop <= $time)) - { - continue; - } - - if($blnIsSitemap) - { - // The target page is protected - if ($objParent->protected) - { - continue; - } - - // the target page is exempt from the sitemap - if ($objParent->robots == 'noindex,nofollow') - { - continue; - } - } - - // Generate the URL - $arrProcessed[$objRecommendationArchive->jumpTo] = $objParent->getAbsoluteUrl(Config::get('useAutoItem') ? '/%s' : '/items/%s'); - } - - $strUrl = $arrProcessed[$objRecommendationArchive->jumpTo]; - - // Get the items - $objItems = RecommendationModel::findPublishedByPid($objRecommendationArchive->id); - - if($objItems !== null) - { - while ($objItems->next()) - { - $arrPages[] = sprintf(preg_replace('/%(?!s)/', '%%', $strUrl), ($objItems->alias ?: $objItems->id)); - } - } - } - } - - return $arrPages; - } -} From 3fef0b078c17412620a686dcc3d740c3314835cc Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Wed, 31 May 2023 22:40:19 +0200 Subject: [PATCH 11/19] Move permissions, add new keywords and add authors to composer.json --- composer.json | 16 ++++++++++++++-- src/ContaoManager/Plugin.php | 5 ----- src/ContaoRecommendationBundle.php | 5 ----- src/ContaoRecommendationPermissions.php | 12 ------------ src/Security/ContaoRecommendationPermissions.php | 12 ++++++++++++ 5 files changed, 26 insertions(+), 24 deletions(-) delete mode 100644 src/ContaoRecommendationPermissions.php create mode 100644 src/Security/ContaoRecommendationPermissions.php diff --git a/composer.json b/composer.json index e390bef..0f5a244 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,9 @@ "description": "Recommendation integration for Contao 4 Open Source CMS", "keywords": [ "contao", - "recommendation-bundle" + "recommendation-bundle", + "recommendation", + "reviews" ], "homepage": "https://www.oveleon.de/", "license": "MIT", @@ -13,11 +15,21 @@ "name": "Oveleon", "homepage": "https://oveleon.de/", "role": "Developer" + }, + { + "name": "Sebastian Zoglowek", + "homepage": "https://github.com/zoglo", + "role": "Developer" + }, + { + "name": "Fabian Ekert", + "homepage": "https://github.com/eki89", + "role": "Developer" } ], "require": { "php": "^8.1", - "contao/core-bundle": "^4.13" + "contao/core-bundle": "^4.13 || ^5.1" }, "require-dev": { "contao/manager-plugin": "^2.0" diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index f7c583c..d32d03c 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -16,11 +16,6 @@ use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; use Oveleon\ContaoRecommendationBundle\ContaoRecommendationBundle; -/** - * Plugin for the Contao Manager. - * - * @author Fabian Ekert - */ class Plugin implements BundlePluginInterface { /** diff --git a/src/ContaoRecommendationBundle.php b/src/ContaoRecommendationBundle.php index 065b109..fbc6ae7 100644 --- a/src/ContaoRecommendationBundle.php +++ b/src/ContaoRecommendationBundle.php @@ -10,11 +10,6 @@ use Symfony\Component\HttpKernel\Bundle\Bundle; -/** - * Configures the Contao recommendation bundle. - * - * @author Fabian Ekert - */ class ContaoRecommendationBundle extends Bundle { public function getPath(): string diff --git a/src/ContaoRecommendationPermissions.php b/src/ContaoRecommendationPermissions.php deleted file mode 100644 index 6d2aa2f..0000000 --- a/src/ContaoRecommendationPermissions.php +++ /dev/null @@ -1,12 +0,0 @@ - Date: Wed, 31 May 2023 23:58:50 +0200 Subject: [PATCH 12/19] Move Models and change global variables --- contao/config/config.php | 4 +- contao/dca/tl_recommendation_settings.php | 7 +- contao/modules/ModuleRecommendation.php | 103 +++++------------- contao/modules/ModuleRecommendationForm.php | 45 +++----- contao/modules/ModuleRecommendationList.php | 12 +- contao/modules/ModuleRecommendationReader.php | 2 + src/Cron/PurgeRecommendationsCron.php | 2 +- .../DataContainer/RecommendationListener.php | 6 +- src/EventListener/SitemapListener.php | 4 +- .../Model}/RecommendationArchiveModel.php | 2 +- .../Model}/RecommendationModel.php | 42 +------ 11 files changed, 66 insertions(+), 163 deletions(-) rename {contao/models => src/Model}/RecommendationArchiveModel.php (98%) rename {contao/models => src/Model}/RecommendationModel.php (83%) diff --git a/contao/config/config.php b/contao/config/config.php index 94433de..36e49be 100644 --- a/contao/config/config.php +++ b/contao/config/config.php @@ -6,8 +6,8 @@ use Oveleon\ContaoRecommendationBundle\ModuleRecommendationForm; use Oveleon\ContaoRecommendationBundle\ModuleRecommendationList; use Oveleon\ContaoRecommendationBundle\ModuleRecommendationReader; -use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; -use Oveleon\ContaoRecommendationBundle\RecommendationModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationArchiveModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; // Back end modules ArrayUtil::arrayInsert($GLOBALS['BE_MOD']['content'], 5, [ diff --git a/contao/dca/tl_recommendation_settings.php b/contao/dca/tl_recommendation_settings.php index 9be3b28..5772b6f 100644 --- a/contao/dca/tl_recommendation_settings.php +++ b/contao/dca/tl_recommendation_settings.php @@ -6,11 +6,14 @@ * (c) https://www.oveleon.de/ */ +use Contao\Config; +use Contao\DC_File; + $GLOBALS['TL_DCA']['tl_recommendation_settings'] = [ // Config 'config' => [ - 'dataContainer' => 'File', + 'dataContainer' => DC_File::class, 'closed' => true ], @@ -23,7 +26,7 @@ 'fields' => [ 'recommendationDefaultImage' => [ 'inputType' => 'fileTree', - 'eval' => ['fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=>Contao\Config::get('validImageTypes'), 'tl_class'=>'clr'] + 'eval' => ['fieldType'=>'radio', 'filesOnly'=>true, 'isGallery'=>true, 'extensions'=> Config::get('validImageTypes'), 'tl_class'=>'clr'] ], 'recommendationActiveColor' => [ 'inputType' => 'text', diff --git a/contao/modules/ModuleRecommendation.php b/contao/modules/ModuleRecommendation.php index 81b38dc..160f0bc 100644 --- a/contao/modules/ModuleRecommendation.php +++ b/contao/modules/ModuleRecommendation.php @@ -10,15 +10,18 @@ use Contao\Config; use Contao\Controller; +use Contao\CoreBundle\Security\ContaoCorePermissions; use Contao\Date; use Contao\FilesModel; use Contao\FrontendTemplate; -use Contao\FrontendUser; use Contao\Model\Collection; use Contao\Module; use Contao\PageModel; use Contao\StringUtil; use Contao\System; +use Exception; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationArchiveModel; /** * Parent class for recommendation modules. @@ -28,43 +31,28 @@ */ abstract class ModuleRecommendation extends Module { - /** * Sort out protected archives - * - * @param array $arrArchives - * - * @return array */ - protected function sortOutProtected($arrArchives) + protected function sortOutProtected(array $arrArchives): array { if (empty($arrArchives) || !\is_array($arrArchives)) { return $arrArchives; } - $this->import(FrontendUser::class, 'User'); $objArchive = RecommendationArchiveModel::findMultipleByIds($arrArchives); $arrArchives = []; if ($objArchive !== null) { + $security = System::getContainer()->get('security.helper'); + while ($objArchive->next()) { - if ($objArchive->protected) + if ($objArchive->protected && !$security->isGranted(ContaoCorePermissions::MEMBER_IN_GROUPS, StringUtil::deserialize($objArchive->groups, true))) { - // ToDo: rewrite for contao ^5.x - if (!FE_USER_LOGGED_IN) - { - continue; - } - - $groups = StringUtil::deserialize($objArchive->groups); - - if (empty($groups) || !\is_array($groups) || !\count(array_intersect($groups, $this->User->groups))) - { - continue; - } + continue; } $arrArchives[] = $objArchive->id; @@ -76,21 +64,13 @@ protected function sortOutProtected($arrArchives) /** * Parse an item and return it as string - * - * @param RecommendationModel $objRecommendation - * @param RecommendationArchiveModel $objRecommendationArchive - * @param string $strClass - * @param integer $intCount - * - * @return string */ - protected function parseRecommendation($objRecommendation, $objRecommendationArchive, $strClass='', $intCount=0) + protected function parseRecommendation(RecommendationModel $objRecommendation, RecommendationArchiveModel $objRecommendationArchive, string $strClass='', int $intCount=0): string { - /** @var FrontendTemplate|object $objTemplate */ $objTemplate = new FrontendTemplate($this->recommendation_template ?: 'recommendation_default'); $objTemplate->setData($objRecommendation->row()); - if ($objRecommendation->cssClass != '') + if ($objRecommendation->cssClass) { $strClass = ' ' . $objRecommendation->cssClass . $strClass; } @@ -187,12 +167,8 @@ protected function parseRecommendation($objRecommendation, $objRecommendationArc /** * Parse one or more items and return them as array - * - * @param Collection $objRecommendations - * - * @return array */ - protected function parseRecommendations($objRecommendations) + protected function parseRecommendations(Collection $objRecommendations): array { $limit = $objRecommendations->count(); @@ -220,12 +196,8 @@ protected function parseRecommendations($objRecommendations) /** * Return the meta fields of a recommendation as array - * - * @param RecommendationModel $objRecommendation - * - * @return array */ - protected function getMetaFields($objRecommendation) + protected function getMetaFields(RecommendationModel $objRecommendation): array { $meta = StringUtil::deserialize($this->recommendation_metaFields); @@ -261,15 +233,8 @@ protected function getMetaFields($objRecommendation) /** * Generate a link and return it as string - * - * @param string $strLink - * @param RecommendationModel $objRecommendation - * @param string $strTitle - * @param boolean $blnIsReadMore - * - * @return string */ - protected function generateLink($strLink, $objRecommendation, $strTitle, $blnIsReadMore=false) + protected function generateLink(string $strLink, RecommendationModel $objRecommendation, string $strTitle, bool $blnIsReadMore=false): string { return sprintf('', $this->generateRecommendationUrl($objRecommendation), @@ -280,28 +245,20 @@ protected function generateLink($strLink, $objRecommendation, $strTitle, $blnIsR /** * Generate a URL and return it as string - * - * @param RecommendationModel $objRecommendation - * - * @return string */ - protected function generateRecommendationUrl($objRecommendation) + protected function generateRecommendationUrl(RecommendationModel $objRecommendation): string { $objPage = PageModel::findByPk($objRecommendation->getRelated('pid')->jumpTo); - return ampersand($objPage->getFrontendUrl((Config::get('useAutoItem') ? '/' : '/items/') . ($objRecommendation->alias ?: $objRecommendation->id))); + return StringUtil::ampersand($objPage->getFrontendUrl((Config::get('useAutoItem') ? '/' : '/items/') . ($objRecommendation->alias ?: $objRecommendation->id))); } /** * Check whether path is external - * - * @param string $strPath The file path - * - * @return boolean */ - protected function isExternal($strPath) + protected function isExternal(string $strPath): bool { - if (substr($strPath, 0, 7) == 'http://' || substr($strPath, 0, 8) == 'https://') + if (str_starts_with($strPath, 'http://') || str_starts_with($strPath, 'https://')) { return true; } @@ -316,10 +273,10 @@ protected function isExternal($strPath) * @param RecommendationModel $objRecommendation The recommendation model * @param FrontendTemplate $objTemplate The frontend template */ - protected function addInternalImage($objModel, $objRecommendation, &$objTemplate) + protected function addInternalImage($objModel, $objRecommendation, &$objTemplate): void { - // ToDo: rewrite for contao ^5.x - if ($objModel !== null && is_file(TL_ROOT . '/' . $objModel->path)) + // ToDo: Contao 5.x compatibility + if ($objModel !== null && is_file(System::getContainer()->getParameter('kernel.project_dir') . '/' . $objModel->path)) { $objTemplate->addInternalImage = true; @@ -343,17 +300,14 @@ protected function addInternalImage($objModel, $objRecommendation, &$objTemplate } /** - * Parses a timestamp into a human readable string - * - * @param string $strDateTime - * - * @return string + * Parses a timestamp into a human-readable string + * @throws Exception */ - protected function getElapsedTime($strDateTime) + protected function getElapsedTime(string $strDateTime): string { $objElapsedTime = (new \DateTime($strDateTime))->diff(new \DateTime()); - if(($years = $objElapsedTime->y) > 0) + if (($years = $objElapsedTime->y) > 0) { return $this->translateElapsedTime($years, 'year'); } @@ -381,13 +335,8 @@ protected function getElapsedTime($strDateTime) /** * Translates elapsed time - * - * @param integer $value - * @param string $strUnit - * - * @return string */ - protected function translateElapsedTime($value, $strUnit = 'justNow') + protected function translateElapsedTime(int $value, string $strUnit = 'justNow'): string { return sprintf($GLOBALS['TL_LANG']['tl_recommendation'][$strUnit][!($value>1)], $value); } diff --git a/contao/modules/ModuleRecommendationForm.php b/contao/modules/ModuleRecommendationForm.php index d63f81a..aaf017a 100644 --- a/contao/modules/ModuleRecommendationForm.php +++ b/contao/modules/ModuleRecommendationForm.php @@ -10,7 +10,6 @@ use Contao\BackendTemplate; use Contao\CoreBundle\Exception\InternalServerErrorException; -use Contao\CoreBundle\Monolog\ContaoContext; use Contao\CoreBundle\OptIn\OptIn; use Contao\Email; use Contao\Environment; @@ -21,7 +20,6 @@ use Contao\StringUtil; use Contao\System; use Contao\Widget; -use Psr\Log\LogLevel; /** * Front end module "recommendation form". @@ -55,7 +53,7 @@ class ModuleRecommendationForm extends ModuleRecommendation * * @return string */ - public function generate() + public function generate(): string { $request = System::getContainer()->get('request_stack')->getCurrentRequest(); @@ -84,7 +82,7 @@ public function generate() /** * Generate the module */ - protected function compile() + protected function compile(): void { System::loadLanguageFile('tl_recommendation'); System::loadLanguageFile('tl_recommendation_notification'); @@ -225,12 +223,12 @@ protected function compile() $this->Template->formId = $strFormId; $this->Template->hasError = $doNotSubmit; - $session = System::getContainer()->get('session'); + $objSession = System::getContainer()->get('request_stack')->getSession(); // Do not index or cache the page with the confirmation message - if ($session->isStarted()) + if ($objSession->isStarted()) { - $flashBag = $session->getFlashBag(); + $flashBag = $objSession->getFlashBag(); if ($flashBag->has('recommendation_added')) { @@ -298,7 +296,7 @@ protected function compile() } else { - $session->getFlashBag()->set('recommendation_added', $this->getFlashBagMessage()); + $objSession->getFlashBag()->set('recommendation_added', $this->getFlashBagMessage()); } $this->reload(); @@ -307,12 +305,8 @@ protected function compile() /** * Convert line feeds to
tags - * - * @param string $strText - * - * @return string */ - public function convertLineFeeds($strText) + public function convertLineFeeds(string $strText): string { $strText = preg_replace('/\r?\n/', '
', $strText); @@ -335,10 +329,8 @@ public function convertLineFeeds($strText) /** * Get flashbag message - * - * @return string */ - protected function getFlashBagMessage() + protected function getFlashBagMessage(): string { // Confirmation e-mail if ($this->recommendation_activate) @@ -358,10 +350,8 @@ protected function getFlashBagMessage() /** * Sends a notification to the administrator - * - * @param RecommendationModel $objRecommendation */ - protected function sendNotificationMail($objRecommendation) + protected function sendNotificationMail(RecommendationModel $objRecommendation): void { $strText = $objRecommendation->text; @@ -397,11 +387,8 @@ protected function sendNotificationMail($objRecommendation) /** * Send the verification mail - * - * @param array $arrData - * @param integer $id */ - protected function sendVerificationMail($arrData, $id) + protected function sendVerificationMail(array $arrData, int $id): void { /** @var OptIn $optIn */ $optIn = System::getContainer()->get('contao.opt-in'); @@ -409,19 +396,20 @@ protected function sendVerificationMail($arrData, $id) // Prepare the simple token data $arrTokenData = $arrData; - $arrTokenData['token'] = $optInToken->getIdentifier(); - $arrTokenData['domain'] = Idna::decode(Environment::get('host')); - $arrTokenData['link'] = Idna::decode(Environment::get('base')) . Environment::get('request') . ((strpos(Environment::get('request'), '?') !== false) ? '&' : '?') . 'token=' . $optInToken->getIdentifier(); + $arrTokenData['token'] = $optInToken->getIdentifier(); + $arrTokenData['domain'] = Idna::decode(Environment::get('host')); + $arrTokenData['link'] = Idna::decode(Environment::get('base')) . Environment::get('request') . ((str_contains(Environment::get('request'), '?')) ? '&' : '?') . 'token=' . $optInToken->getIdentifier(); $arrTokenData['channel'] = ''; // Send the token + // ToDo: Contao 5.x compatibility (parseSimpleTokens) $optInToken->send(sprintf($GLOBALS['TL_LANG']['tl_recommendation_notification']['email_activation'][0], Idna::decode(Environment::get('host'))), StringUtil::parseSimpleTokens($this->recommendation_activateText, $arrTokenData)); } /** * Verifies the recommendation */ - protected function verifyRecommendation() + protected function verifyRecommendation(): void { $this->Template = new FrontendTemplate('mod_message'); @@ -481,9 +469,8 @@ protected function verifyRecommendation() $optInToken->confirm(); // Log activity - // ToDo: rewrite for contao ^5.x $logger = System::getContainer()->get('monolog.logger.contao'); - $logger->log(LogLevel::INFO, 'Recommendation ID ' . $objRecommendation->id . ' (' . Idna::decodeEmail($objRecommendation->email) . ') has been verified', ['contao' => new ContaoContext(__METHOD__, TL_ACCESS)]); + $logger?->info('Recommendation ID ' . $objRecommendation->id . ' (' . Idna::decodeEmail($objRecommendation->email) . ') has been verified'); // Redirect to the jumpTo page if (($objTarget = $this->objModel->getRelated('recommendation_activateJumpTo')) instanceof PageModel) diff --git a/contao/modules/ModuleRecommendationList.php b/contao/modules/ModuleRecommendationList.php index 4feeb41..4bb7264 100644 --- a/contao/modules/ModuleRecommendationList.php +++ b/contao/modules/ModuleRecommendationList.php @@ -17,6 +17,7 @@ use Contao\Pagination; use Contao\StringUtil; use Contao\System; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; /** * Front end module "recommendation list". @@ -27,7 +28,6 @@ */ class ModuleRecommendationList extends ModuleRecommendation { - /** * Template * @var string @@ -39,7 +39,7 @@ class ModuleRecommendationList extends ModuleRecommendation * * @return string */ - public function generate() + public function generate(): string { $request = System::getContainer()->get('request_stack')->getCurrentRequest(); @@ -201,14 +201,6 @@ protected function countItems($recommendationArchives, $blnFeatured, $minRating) /** * Fetch the matching items - * - * @param array $recommendationArchives - * @param boolean $blnFeatured - * @param integer $limit - * @param integer $offset - * @param integer $minRating - * - * @return Collection|RecommendationModel|null */ protected function fetchItems($recommendationArchives, $blnFeatured, $limit, $offset, $minRating) { diff --git a/contao/modules/ModuleRecommendationReader.php b/contao/modules/ModuleRecommendationReader.php index 1752d10..1e5aca5 100644 --- a/contao/modules/ModuleRecommendationReader.php +++ b/contao/modules/ModuleRecommendationReader.php @@ -16,6 +16,8 @@ use Contao\Input; use Contao\StringUtil; use Contao\System; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationArchiveModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; /** * Front end module "recommendation reader". diff --git a/src/Cron/PurgeRecommendationsCron.php b/src/Cron/PurgeRecommendationsCron.php index a04fd3f..aebb7e8 100644 --- a/src/Cron/PurgeRecommendationsCron.php +++ b/src/Cron/PurgeRecommendationsCron.php @@ -6,7 +6,7 @@ use Contao\CoreBundle\DependencyInjection\Attribute\AsCronJob; use Contao\CoreBundle\Framework\ContaoFramework; -use Oveleon\ContaoRecommendationBundle\RecommendationModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; use Psr\Log\LoggerInterface; #[AsCronJob('daily')] diff --git a/src/EventListener/DataContainer/RecommendationListener.php b/src/EventListener/DataContainer/RecommendationListener.php index 0e7f98b..c68e2a3 100644 --- a/src/EventListener/DataContainer/RecommendationListener.php +++ b/src/EventListener/DataContainer/RecommendationListener.php @@ -15,7 +15,7 @@ use Contao\System; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; -use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationArchiveModel; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Core\Security; @@ -229,7 +229,7 @@ public function listRecommendations(array $arrRow): string public function generateSitemap(): void { /** @var SessionInterface $objSession */ - $objSession = System::getContainer()->get('session'); + $objSession = System::getContainer()->get('request_stack')->getSession(); $session = $objSession->get('recommendation_updater'); @@ -261,7 +261,7 @@ public function scheduleUpdate(DataContainer $dc): void } /** @var SessionInterface $objSession */ - $objSession = System::getContainer()->get('session'); + $objSession = System::getContainer()->get('request_stack')->getSession(); // Store the ID in the session $session = $objSession->get('recommendation_updater'); diff --git a/src/EventListener/SitemapListener.php b/src/EventListener/SitemapListener.php index 6fd311b..2434ddc 100644 --- a/src/EventListener/SitemapListener.php +++ b/src/EventListener/SitemapListener.php @@ -9,8 +9,8 @@ use Contao\CoreBundle\Framework\ContaoFramework; use Contao\Database; use Contao\PageModel; -use Oveleon\ContaoRecommendationBundle\RecommendationArchiveModel; -use Oveleon\ContaoRecommendationBundle\RecommendationModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationArchiveModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; use Terminal42\ServiceAnnotationBundle\Annotation\ServiceTag; /** diff --git a/contao/models/RecommendationArchiveModel.php b/src/Model/RecommendationArchiveModel.php similarity index 98% rename from contao/models/RecommendationArchiveModel.php rename to src/Model/RecommendationArchiveModel.php index c78e8da..a283ac5 100644 --- a/contao/models/RecommendationArchiveModel.php +++ b/src/Model/RecommendationArchiveModel.php @@ -6,7 +6,7 @@ * (c) https://www.oveleon.de/ */ -namespace Oveleon\ContaoRecommendationBundle; +namespace Oveleon\ContaoRecommendationBundle\Model; use Contao\Model; use Contao\Model\Collection; diff --git a/contao/models/RecommendationModel.php b/src/Model/RecommendationModel.php similarity index 83% rename from contao/models/RecommendationModel.php rename to src/Model/RecommendationModel.php index 12bfe81..e4dd912 100644 --- a/contao/models/RecommendationModel.php +++ b/src/Model/RecommendationModel.php @@ -6,7 +6,7 @@ * (c) https://www.oveleon.de/ */ -namespace Oveleon\ContaoRecommendationBundle; +namespace Oveleon\ContaoRecommendationBundle\Model; use Contao\Database; use Contao\Date; @@ -107,7 +107,6 @@ */ class RecommendationModel extends Model { - /** * Table name * @var string @@ -116,14 +115,8 @@ class RecommendationModel extends Model /** * Find a published recommendation from one or more recommendation archives by its ID or alias - * - * @param mixed $varId The numeric ID or alias name - * @param array $arrPids An array of parent IDs - * @param array $arrOptions An optional options array - * - * @return RecommendationModel|null The model or null if there are no recommendations */ - public static function findPublishedByParentAndIdOrAlias($varId, $arrPids, array $arrOptions=array()) + public static function findPublishedByParentAndIdOrAlias(mixed $varId, $arrPids, array $arrOptions=array()): ?RecommendationModel { if (empty($arrPids) || !\is_array($arrPids)) { @@ -145,13 +138,8 @@ public static function findPublishedByParentAndIdOrAlias($varId, $arrPids, array /** * Find published recommendations with the default redirect target by their parent ID - * - * @param integer $intPid The recommendation archive ID - * @param array $arrOptions An optional options array - * - * @return Collection|RecommendationModel[]|RecommendationModel|null A collection of models or null if there are no recommendations */ - public static function findPublishedByPid($intPid, array $arrOptions=array()) + public static function findPublishedByPid(int $intPid, array $arrOptions=array()): Collection|RecommendationModel|array|null { $t = static::$strTable; $arrColumns = ["$t.pid=? AND $t.verified='1'"]; @@ -172,16 +160,8 @@ public static function findPublishedByPid($intPid, array $arrOptions=array()) /** * Find published recommendations by their parent ID - * - * @param array $arrPids An array of recommendation archive IDs - * @param boolean $blnFeatured If true, return only featured recommendations, if false, return only unfeatured recommendations - * @param integer $intLimit An optional limit - * @param integer $intOffset An optional offset - * @param array $arrOptions An optional options array - * - * @return Collection|RecommendationModel[]|RecommendationModel|null A collection of models or null if there are no recommendations */ - public static function findPublishedByPids($arrPids, $blnFeatured=null, $intLimit=0, $intOffset=0, $minRating=null, array $arrOptions=array()) + public static function findPublishedByPids($arrPids, bool $blnFeatured=null, int $intLimit=0, int $intOffset=0, $minRating=null, array $arrOptions=array()): Collection|RecommendationModel|array|null { if (empty($arrPids) || !\is_array($arrPids)) { @@ -224,14 +204,8 @@ public static function findPublishedByPids($arrPids, $blnFeatured=null, $intLimi /** * Count published recommendations by their parent ID - * - * @param array $arrPids An array of recommendation archive IDs - * @param boolean $blnFeatured If true, return only featured recommendations, if false, return only unfeatured recommendations - * @param array $arrOptions An optional options array - * - * @return integer The number of recommendations */ - public static function countPublishedByPids($arrPids, $blnFeatured=null, $minRating=null, array $arrOptions=array()) + public static function countPublishedByPids($arrPids, bool $blnFeatured=null, $minRating=null, array $arrOptions=array()): int { if (empty($arrPids) || !\is_array($arrPids)) { @@ -266,12 +240,8 @@ public static function countPublishedByPids($arrPids, $blnFeatured=null, $minRat /** * Find registrations that have not been activated for more than 24 hours - * - * @param array $arrOptions An optional options array - * - * @return Collection|RecommendationModel[]|RecommendationModel|null A collection of models or null if there are no expired recommendations */ - public static function findExpiredRecommendations(array $arrOptions=array()) + public static function findExpiredRecommendations(array $arrOptions=array()): Collection|RecommendationModel|array|null { $t = static::$strTable; $objDatabase = Database::getInstance(); From 5446ed3bd92b1668b8d4c5f5e2d8b2f1b14c174b Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek Date: Thu, 1 Jun 2023 12:21:08 +0200 Subject: [PATCH 13/19] Add referrer to module recommendation form and change opt_in service --- contao/modules/ModuleRecommendationForm.php | 57 ++++++++++--------- .../modules/mod_recommendationform.html5 | 2 +- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/contao/modules/ModuleRecommendationForm.php b/contao/modules/ModuleRecommendationForm.php index aaf017a..a5d76a3 100644 --- a/contao/modules/ModuleRecommendationForm.php +++ b/contao/modules/ModuleRecommendationForm.php @@ -20,6 +20,8 @@ use Contao\StringUtil; use Contao\System; use Contao\Widget; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationArchiveModel; +use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; /** * Front end module "recommendation form". @@ -37,8 +39,6 @@ * @property integer $jumpTo * @property boolean $recommendation_activate * @property string $recommendation_activateText - * - * @author Sebastian Zoglowek */ class ModuleRecommendationForm extends ModuleRecommendation { @@ -60,11 +60,11 @@ public function generate(): string if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) { $objTemplate = new BackendTemplate('be_wildcard'); - $objTemplate->wildcard = '### ' . mb_strtoupper($GLOBALS['TL_LANG']['FMD']['recommendationform'][0], 'UTF-8') . ' ###'; + $objTemplate->wildcard = '### ' . $GLOBALS['TL_LANG']['FMD']['recommendationform'][0] . ' ###'; $objTemplate->title = $this->headline; $objTemplate->id = $this->id; $objTemplate->link = $this->name; - $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id; + $objTemplate->href = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', array('do'=>'themes', 'table'=>'tl_module', 'act'=>'edit', 'id'=>$this->id))); return $objTemplate->parse(); } @@ -143,7 +143,7 @@ protected function compile(): void ]; // Captcha - if (!$this->recommendation_disableCaptcha == true) + if (!$this->recommendation_disableCaptcha) { $arrFields['captcha'] = [ 'name' => 'captcha', @@ -172,8 +172,8 @@ protected function compile(): void } $doNotSubmit = false; - $arrWidgets = []; - $strFormId = 'recommendation_' . $this->id; + $arrWidgets = []; + $strFormId = 'recommendation_' . $this->id; // Optional recommendation form fields $arrOptionalFormFields = StringUtil::deserialize($this->recommendation_optionalFormFields, true); @@ -182,7 +182,7 @@ protected function compile(): void foreach ($arrFields as $fieldName => $arrField) { // Check for optional form fields - if(($arrField['eval']['optional'] ?? null) && !\in_array($fieldName, $arrOptionalFormFields)) + if (($arrField['eval']['optional'] ?? null) && !\in_array($fieldName, $arrOptionalFormFields)) { continue; } @@ -218,10 +218,11 @@ protected function compile(): void $arrWidgets[$arrField['name']] = $objWidget; } - $this->Template->fields = $arrWidgets; - $this->Template->submit = $GLOBALS['TL_LANG']['tl_recommendation']['recommendation_submit']; - $this->Template->formId = $strFormId; - $this->Template->hasError = $doNotSubmit; + $this->Template->fields = $arrWidgets; + $this->Template->submit = $GLOBALS['TL_LANG']['tl_recommendation']['recommendation_submit']; + $this->Template->formId = $strFormId; + $this->Template->hasError = $doNotSubmit; + $this->Template->requestToken = System::getContainer()->get('contao.csrf.token_manager')->getDefaultTokenValue(); $objSession = System::getContainer()->get('request_stack')->getSession(); @@ -252,8 +253,7 @@ protected function compile(): void $strText = preg_replace('/(href|src|on[a-z]+)="[^"]*(contao\/main\.php|typolight\/main\.php|javascript|vbscri?pt|script|alert|document|cookie|window)[^"]*"+/i', '$1="#"', $strText); // Prepare the record - $arrData = array - ( + $arrData = [ 'tstamp' => $time, 'pid' => $this->recommendation_archive, 'title' => $arrWidgets['title']->value ?: '', @@ -267,7 +267,7 @@ protected function compile(): void 'text' => $this->convertLineFeeds($strText), 'rating' => $arrWidgets['rating']->value, 'published' => $this->recommendation_moderate ? '' : 1 - ); + ]; // Store the recommendation $objRecommendation = new RecommendationModel(); @@ -316,13 +316,12 @@ public function convertLineFeeds(string $strText): string $strText = '

' . $strText . '

'; } - $arrReplace = array - ( + $arrReplace = [ '@
\s?
\s?@' => "

\n

", // Convert two linebreaks into a new paragraph '@\s?

@' => '

', // Remove BR tags before closing P tags '@

'

@' => '' // Do not nest DIVs inside paragraphs - ); + ]; return preg_replace(array_keys($arrReplace), array_values($arrReplace), $strText); } @@ -356,8 +355,8 @@ protected function sendNotificationMail(RecommendationModel $objRecommendation): $strText = $objRecommendation->text; $objEmail = new Email(); - $objEmail->from = $GLOBALS['TL_ADMIN_EMAIL']; - $objEmail->fromName = $GLOBALS['TL_ADMIN_NAME']; + $objEmail->from = $GLOBALS['TL_ADMIN_EMAIL'] ?? null; + $objEmail->fromName = $GLOBALS['TL_ADMIN_NAME'] ?? null; $objEmail->subject = sprintf($GLOBALS['TL_LANG']['tl_recommendation_notification']['email_subject'], Idna::decode(Environment::get('host'))); // Convert the recommendation to plain text @@ -390,8 +389,10 @@ protected function sendNotificationMail(RecommendationModel $objRecommendation): */ protected function sendVerificationMail(array $arrData, int $id): void { + $container = System::getContainer(); + /** @var OptIn $optIn */ - $optIn = System::getContainer()->get('contao.opt-in'); + $optIn = $container->get('contao.opt_in'); $optInToken = $optIn->create('rec', $arrData['email'], ['tl_recommendation'=> [$id]]); // Prepare the simple token data @@ -402,8 +403,7 @@ protected function sendVerificationMail(array $arrData, int $id): void $arrTokenData['channel'] = ''; // Send the token - // ToDo: Contao 5.x compatibility (parseSimpleTokens) - $optInToken->send(sprintf($GLOBALS['TL_LANG']['tl_recommendation_notification']['email_activation'][0], Idna::decode(Environment::get('host'))), StringUtil::parseSimpleTokens($this->recommendation_activateText, $arrTokenData)); + $optInToken->send(sprintf($GLOBALS['TL_LANG']['tl_recommendation_notification']['email_activation'][0], Idna::decode(Environment::get('host'))), $container->get('contao.string.simple_token_parser')->parse($this->recommendation_activateText, $arrTokenData)); } /** @@ -414,10 +414,16 @@ protected function verifyRecommendation(): void $this->Template = new FrontendTemplate('mod_message'); /** @var OptIn $optin */ - $optIn = System::getContainer()->get('contao.opt-in'); + $optIn = System::getContainer()->get('contao.opt_in'); // Find an unconfirmed token - if ((!$optInToken = $optIn->find(Input::get('token'))) || !$optInToken->isValid() || \count($arrRelated = $optInToken->getRelatedRecords()) < 1 || key($arrRelated) != 'tl_recommendation' || \count($arrIds = current($arrRelated)) < 1) + if ( + (!$optInToken = $optIn->find(Input::get('token'))) || + !$optInToken->isValid() || + \count($arrRelated = $optInToken->getRelatedRecords()) < 1 || + key($arrRelated) != 'tl_recommendation' || + \count($arrIds = current($arrRelated)) < 1 + ) { $this->Template->type = 'error'; $this->Template->message = $GLOBALS['TL_LANG']['MSC']['invalidToken']; @@ -459,7 +465,6 @@ protected function verifyRecommendation(): void $objRecommendation->verified = 1; $objRecommendation->save(); - // Notify system administrator via e-mail if ($this->recommendation_notify) { diff --git a/contao/templates/modules/mod_recommendationform.html5 b/contao/templates/modules/mod_recommendationform.html5 index 0adb651..3eb51a8 100644 --- a/contao/templates/modules/mod_recommendationform.html5 +++ b/contao/templates/modules/mod_recommendationform.html5 @@ -10,7 +10,7 @@
- + fields as $field): ?> parse() ?> From b1439333f318f721e096baee4547c544ce7e95ae Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek Date: Thu, 1 Jun 2023 12:21:45 +0200 Subject: [PATCH 14/19] Update permission handling for USER_CAN_EDIT_ARCHIVE --- contao/dca/tl_module.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contao/dca/tl_module.php b/contao/dca/tl_module.php index cbc5f86..131df1b 100644 --- a/contao/dca/tl_module.php +++ b/contao/dca/tl_module.php @@ -6,8 +6,11 @@ * (c) https://www.oveleon.de/ */ +use Contao\Backend; +use Contao\BackendUser; use Contao\System; use Contao\Controller; +use Oveleon\ContaoRecommendationBundle\Security\ContaoRecommendationPermissions; System::loadLanguageFile('tl_recommendation'); System::loadLanguageFile('tl_recommendation_notification'); @@ -175,7 +178,7 @@ 'sql' => "varchar(64) NOT NULL default ''" ]; -class tl_module_recommendation extends Contao\Backend +class tl_module_recommendation extends Backend { /** * Import the back end user object @@ -183,7 +186,7 @@ class tl_module_recommendation extends Contao\Backend public function __construct() { parent::__construct(); - $this->import('Contao\BackendUser', 'User'); + $this->import(BackendUser::class, 'User'); } /** @@ -198,10 +201,11 @@ public function getRecommendationArchives(): array $arrArchives = []; $objArchives = $this->Database->execute("SELECT id, title FROM tl_recommendation_archive ORDER BY title"); + $security = System::getContainer()->get('security.helper'); while ($objArchives->next()) { - if ($this->User->hasAccess($objArchives->id, 'recommendations')) + if ($security->isGranted(ContaoRecommendationPermissions::USER_CAN_EDIT_ARCHIVE, $objArchives->id)) { $arrArchives[$objArchives->id] = $objArchives->title; } From 4df2e5f76aee283dad8cf55d94e5af4b541830ce Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek Date: Thu, 1 Jun 2023 12:23:06 +0200 Subject: [PATCH 15/19] Change image-processing to figure builder and replace insert tags via service --- contao/modules/ModuleRecommendation.php | 52 +++++++++++-------- contao/modules/ModuleRecommendationList.php | 6 +-- contao/modules/ModuleRecommendationReader.php | 4 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/contao/modules/ModuleRecommendation.php b/contao/modules/ModuleRecommendation.php index 160f0bc..e7a8b55 100644 --- a/contao/modules/ModuleRecommendation.php +++ b/contao/modules/ModuleRecommendation.php @@ -9,7 +9,6 @@ namespace Oveleon\ContaoRecommendationBundle; use Contao\Config; -use Contao\Controller; use Contao\CoreBundle\Security\ContaoCorePermissions; use Contao\Date; use Contao\FilesModel; @@ -22,6 +21,7 @@ use Exception; use Oveleon\ContaoRecommendationBundle\Model\RecommendationModel; use Oveleon\ContaoRecommendationBundle\Model\RecommendationArchiveModel; +use Symfony\Component\Filesystem\Path; /** * Parent class for recommendation modules. @@ -120,29 +120,34 @@ protected function parseRecommendation(RecommendationModel $objRecommendation, R // Parsing image meta field to template for backwards compatibility // Works for recommendation_default.html5 $objTemplate->addRecommendationImage = array_key_exists('image', $arrMeta); + $container = System::getContainer(); + // Add an image if ($objRecommendation->imageUrl != '') { - $objRecommendation->imageUrl = Controller::replaceInsertTags($objRecommendation->imageUrl); + $objRecommendation->imageUrl = $container->get('contao.insert_tag.parser')->replace($objRecommendation->imageUrl); + + // Insert tag parser on contao ^5 returns a leading slash whilst contao 4.13 does not + if (Path::isAbsolute($objRecommendation->imageUrl)) + { + $objRecommendation->imageUrl = substr($objRecommendation->imageUrl,1); + } if ($this->isExternal($objRecommendation->imageUrl)) { $objTemplate->addExternalImage = true; - $objTemplate->imageUrl = $objRecommendation->imageUrl; } else { $objModel = FilesModel::findByPath($objRecommendation->imageUrl); - - $this->addInternalImage($objModel, $objRecommendation, $objTemplate); + $this->addInternalImage($objModel, $objTemplate); } } elseif (Config::get('recommendationDefaultImage')) { $objModel = FilesModel::findByUuid(Config::get('recommendationDefaultImage')); - - $this->addInternalImage($objModel, $objRecommendation, $objTemplate); + $this->addInternalImage($objModel, $objTemplate); } // HOOK: add custom logic @@ -156,9 +161,9 @@ protected function parseRecommendation(RecommendationModel $objRecommendation, R } // Tag recommendations - if (System::getContainer()->has('fos_http_cache.http.symfony_response_tagger')) + if ($container->has('fos_http_cache.http.symfony_response_tagger')) { - $responseTagger = System::getContainer()->get('fos_http_cache.http.symfony_response_tagger'); + $responseTagger = $container->get('fos_http_cache.http.symfony_response_tagger'); $responseTagger->addTags(['contao.db.tl_recommendation.' . $objRecommendation->id]); } @@ -268,34 +273,35 @@ protected function isExternal(string $strPath): bool /** * Add an internal image to template - * - * @param FilesModel $objModel The files model - * @param RecommendationModel $objRecommendation The recommendation model - * @param FrontendTemplate $objTemplate The frontend template */ - protected function addInternalImage($objModel, $objRecommendation, &$objTemplate): void + protected function addInternalImage($objModel, &$objTemplate): void { - // ToDo: Contao 5.x compatibility - if ($objModel !== null && is_file(System::getContainer()->getParameter('kernel.project_dir') . '/' . $objModel->path)) + if (null !== $objModel) { + $imgSize = $this->imgSize ?: null; $objTemplate->addInternalImage = true; - // Do not override the field now that we have a model registry (see #6303) - $arrRecommendation = $objRecommendation->row(); - // Override the default image size - if ($this->imgSize != '') + if ($this->imgSize) { $size = StringUtil::deserialize($this->imgSize); if ($size[0] > 0 || $size[1] > 0 || is_numeric($size[2]) || ($size[2][0] ?? null) === '_') { - $arrRecommendation['size'] = $this->imgSize; + $imgSize = $this->imgSize; } } - $arrRecommendation['singleSRC'] = $objModel->path; - $this->addImageToTemplate($objTemplate, $arrRecommendation, null, null, $objModel); + $figureBuilder = System::getContainer() + ->get('contao.image.studio') + ->createFigureBuilder() + ->from($objModel->path) + ->setSize($imgSize); + + if (null !== ($figure = $figureBuilder->buildIfResourceExists())) + { + $figure->applyLegacyTemplateData($objTemplate); + } } } diff --git a/contao/modules/ModuleRecommendationList.php b/contao/modules/ModuleRecommendationList.php index 4bb7264..d74745a 100644 --- a/contao/modules/ModuleRecommendationList.php +++ b/contao/modules/ModuleRecommendationList.php @@ -46,11 +46,11 @@ public function generate(): string if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) { $objTemplate = new BackendTemplate('be_wildcard'); - $objTemplate->wildcard = '### ' . mb_strtoupper($GLOBALS['TL_LANG']['FMD']['recommendationlist'][0], 'UTF-8') . ' ###'; + $objTemplate->wildcard = '### ' . $GLOBALS['TL_LANG']['FMD']['recommendationlist'][0] . ' ###'; $objTemplate->title = $this->headline; $objTemplate->id = $this->id; $objTemplate->link = $this->name; - $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id; + $objTemplate->href = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', array('do'=>'themes', 'table'=>'tl_module', 'act'=>'edit', 'id'=>$this->id))); return $objTemplate->parse(); } @@ -135,7 +135,7 @@ protected function compile() } // Get the current page - $id = 'page_n' . $this->id; + $id = 'page_r' . $this->id; $page = Input::get($id) ?? 1; // Do not index or cache the page if the page number is outside the range diff --git a/contao/modules/ModuleRecommendationReader.php b/contao/modules/ModuleRecommendationReader.php index 1e5aca5..afe756f 100644 --- a/contao/modules/ModuleRecommendationReader.php +++ b/contao/modules/ModuleRecommendationReader.php @@ -45,11 +45,11 @@ public function generate() if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) { $objTemplate = new BackendTemplate('be_wildcard'); - $objTemplate->wildcard = '### ' . mb_strtoupper($GLOBALS['TL_LANG']['FMD']['recommendationreader'][0], 'UTF-8') . ' ###'; + $objTemplate->wildcard = '### ' . $GLOBALS['TL_LANG']['FMD']['recommendationreader'][0] . ' ###'; $objTemplate->title = $this->headline; $objTemplate->id = $this->id; $objTemplate->link = $this->name; - $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id; + $objTemplate->href = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', array('do'=>'themes', 'table'=>'tl_module', 'act'=>'edit', 'id'=>$this->id))); return $objTemplate->parse(); } From e86e2fc8f63428ba4795004de9d6475db58c14b4 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek Date: Thu, 1 Jun 2023 12:23:28 +0200 Subject: [PATCH 16/19] Update readme and add comments --- README.md | 2 +- composer.json | 5 ++++- src/Cron/PurgeRecommendationsCron.php | 6 ++++++ src/DependencyInjection/ContaoRecommendationExtension.php | 6 ++++++ .../DataContainer/RecommendationArchiveListener.php | 6 ++++++ src/EventListener/DataContainer/RecommendationListener.php | 6 ++++++ src/EventListener/SitemapListener.php | 6 ++++++ src/Security/ContaoRecommendationPermissions.php | 6 ++++++ 8 files changed, 41 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 00b1d7e..28a834a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ --- -> Working with **Contao 4.9** and up to **Contao 4.13** (PHP ^7.4 and PHP 8) +> Working with **Contao 4.13** and **Contao 5.1** (PHP ^8.1) --- diff --git a/composer.json b/composer.json index 0f5a244..93ff511 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "oveleon/contao-recommendation-bundle", "type": "contao-bundle", - "description": "Recommendation integration for Contao 4 Open Source CMS", + "description": "Recommendation integration for Contao Open Source CMS", "keywords": [ "contao", "recommendation-bundle", @@ -38,6 +38,9 @@ "contao/core": "*", "contao/manager-plugin": "<2.0 || >=3.0" }, + "suggest": { + "oveleon/contao-google-recommendation-bundle": "This bundle imports Google reviews into the contao-recommendation-bundle" + }, "autoload": { "psr-4": { "Oveleon\\ContaoRecommendationBundle\\": "src/" diff --git a/src/Cron/PurgeRecommendationsCron.php b/src/Cron/PurgeRecommendationsCron.php index aebb7e8..14e3665 100644 --- a/src/Cron/PurgeRecommendationsCron.php +++ b/src/Cron/PurgeRecommendationsCron.php @@ -2,6 +2,12 @@ declare(strict_types=1); +/* + * This file is part of Oveleon Recommendation Bundle. + * + * (c) https://www.oveleon.de/ + */ + namespace Oveleon\ContaoRecommendationBundle\Cron; use Contao\CoreBundle\DependencyInjection\Attribute\AsCronJob; diff --git a/src/DependencyInjection/ContaoRecommendationExtension.php b/src/DependencyInjection/ContaoRecommendationExtension.php index 947e245..16e0e07 100644 --- a/src/DependencyInjection/ContaoRecommendationExtension.php +++ b/src/DependencyInjection/ContaoRecommendationExtension.php @@ -1,5 +1,11 @@ Date: Thu, 1 Jun 2023 14:19:42 +0200 Subject: [PATCH 17/19] Convert recommendationActiveColor into a serialized string in contao ^5.1.7 --- contao/dca/tl_recommendation_settings.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/contao/dca/tl_recommendation_settings.php b/contao/dca/tl_recommendation_settings.php index 5772b6f..ebb78ff 100644 --- a/contao/dca/tl_recommendation_settings.php +++ b/contao/dca/tl_recommendation_settings.php @@ -8,6 +8,7 @@ use Contao\Config; use Contao\DC_File; +use Contao\StringUtil; $GLOBALS['TL_DCA']['tl_recommendation_settings'] = [ @@ -31,6 +32,18 @@ 'recommendationActiveColor' => [ 'inputType' => 'text', 'eval' => ['maxlength'=>6, 'multiple'=>true, 'size'=>1, 'colorpicker'=>true, 'isHexColor'=>true, 'decodeEntities'=>true, 'tl_class'=>'w50 wizard'], + 'save_callback' => [ + // See contao/issues (#6105) + static function ($value) + { + if (!\is_array($value)) + { + return StringUtil::restoreBasicEntities($value); + } + + return serialize(array_map('\Contao\StringUtil::restoreBasicEntities', $value)); + } + ] ] ] ]; From c5e686a69ac639a63df18bb4232da63324ef9e35 Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek Date: Thu, 1 Jun 2023 17:00:27 +0200 Subject: [PATCH 18/19] Change branch alias --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 93ff511..95b36ac 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-main": "1.3.x-dev" }, "contao-manager-plugin": "Oveleon\\ContaoRecommendationBundle\\ContaoManager\\Plugin" } From 73d6ff1c89e8fe22e8f9a1bc35a55d40debdc67d Mon Sep 17 00:00:00 2001 From: Sebastian Zoglowek <55794780+zoglo@users.noreply.github.com> Date: Thu, 1 Jun 2023 19:13:03 +0200 Subject: [PATCH 19/19] Add tests - Plugin - Cron - Sitemap --- .gitignore | 10 ++ composer.json | 12 ++- phpunit.xml.dist | 21 ++++ tests/ContaoManager/PluginTest.php | 28 ++++++ tests/Cron/PurgeRecommendationsCronTest.php | 33 +++++++ tests/EventListener/SitemapListenerTest.php | 102 ++++++++++++++++++++ 6 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 phpunit.xml.dist create mode 100644 tests/ContaoManager/PluginTest.php create mode 100644 tests/Cron/PurgeRecommendationsCronTest.php create mode 100644 tests/EventListener/SitemapListenerTest.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a01604a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Composer +/composer.lock +/vendor/ + +# PhpUnit +/.phpunit.result.cache +/phpunit.xml + +# IDE +/.idea diff --git a/composer.json b/composer.json index 95b36ac..0788d4e 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,11 @@ "contao/core-bundle": "^4.13 || ^5.1" }, "require-dev": { - "contao/manager-plugin": "^2.0" + "contao/manager-plugin": "^2.3.1", + "contao/test-case": "^5.1", + "phpunit/phpunit": "^9.5", + "symfony/http-client": "^5.4 || ^6.0", + "symfony/phpunit-bridge": "^5.4 || ^6.0" }, "conflict": { "contao/core": "*", @@ -60,5 +64,11 @@ "dev-main": "1.3.x-dev" }, "contao-manager-plugin": "Oveleon\\ContaoRecommendationBundle\\ContaoManager\\Plugin" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "contao/manager-plugin": true + } } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..b64057e --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + ./src + + + + + + + + + + ./tests + + + + + + diff --git a/tests/ContaoManager/PluginTest.php b/tests/ContaoManager/PluginTest.php new file mode 100644 index 0000000..3f16e4a --- /dev/null +++ b/tests/ContaoManager/PluginTest.php @@ -0,0 +1,28 @@ +createMock(ParserInterface::class); + + /** @var BundleConfig $config */ + $config = (new Plugin())->getBundles($parser)[0]; + + $this->assertInstanceOf(BundleConfig::class, $config); + $this->assertSame(ContaoRecommendationBundle::class, $config->getName()); + $this->assertSame([ContaoCoreBundle::class], $config->getLoadAfter()); + $this->assertSame(['recommendation'], $config->getReplace()); + } +} diff --git a/tests/Cron/PurgeRecommendationsCronTest.php b/tests/Cron/PurgeRecommendationsCronTest.php new file mode 100644 index 0000000..2a49a6d --- /dev/null +++ b/tests/Cron/PurgeRecommendationsCronTest.php @@ -0,0 +1,33 @@ +createMock(RecommendationModel::class); + $recommendationModel + ->expects($this->once()) + ->method('delete') + ; + + $recommendationModelAdapter = $this->mockAdapter(['findExpiredRecommendations']); + $recommendationModelAdapter + ->expects($this->once()) + ->method('findExpiredRecommendations') + ->willReturn(new Collection([$recommendationModel], RecommendationModel::getTable())) + ; + + $framework = $this->mockContaoFramework([RecommendationModel::class => $recommendationModelAdapter]); + + (new PurgeRecommendationsCron($framework, null))(); + } +} diff --git a/tests/EventListener/SitemapListenerTest.php b/tests/EventListener/SitemapListenerTest.php new file mode 100644 index 0000000..2b09e04 --- /dev/null +++ b/tests/EventListener/SitemapListenerTest.php @@ -0,0 +1,102 @@ + $this->mockConfiguredAdapter(['findByProtected' => null]), + ]; + + $sitemapEvent = $this->createSitemapEvent([]); + $listener = $this->createListener([], $adapters); + $listener($sitemapEvent); + + $this->assertStringNotContainsString('', (string) $sitemapEvent->getDocument()->saveXML()); + } + + public function testRecommendationIsAdded(): void + { + $jumpToPage = $this->mockClassWithProperties(PageModel::class, [ + 'published' => 1, + 'protected' => 0, + ]); + + $jumpToPage + ->method('getAbsoluteUrl') + ->willReturn('https://www.oveleon.de') + ; + + $adapters = [ + RecommendationArchiveModel::class => $this->mockConfiguredAdapter([ + 'findByProtected' => [ + $this->mockClassWithProperties(RecommendationArchiveModel::class, [ + 'jumpTo' => 21, + ]), + ], + ]), + PageModel::class => $this->mockConfiguredAdapter([ + 'findWithDetails' => $jumpToPage, + ]), + RecommendationModel::class => $this->mockConfiguredAdapter([ + 'findPublishedByPid' => [ + $this->mockClassWithProperties(RecommendationModel::class, [ + 'jumpTo' => 21, + ]), + ], + ]), + ]; + + $sitemapEvent = $this->createSitemapEvent([1]); + $listener = $this->createListener([1, 21], $adapters); + $listener($sitemapEvent); + + $this->assertStringContainsString('https://www.oveleon.de', (string) $sitemapEvent->getDocument()->saveXML()); + } + + private function createListener(array $allPages, array $adapters): SitemapListener + { + $database = $this->createMock(Database::class); + $database + ->method('getChildRecords') + ->willReturn($allPages) + ; + + $instances = [ + Database::class => $database, + ]; + + $framework = $this->mockContaoFramework($adapters, $instances); + + return new SitemapListener($framework); + } + + private function createSitemapEvent(array $rootPages): SitemapEvent + { + $sitemap = new \DOMDocument('1.0', 'UTF-8'); + $urlSet = $sitemap->createElementNS('https://www.sitemaps.org/schemas/sitemap/0.9', 'urlset'); + $sitemap->appendChild($urlSet); + + return new SitemapEvent($sitemap, new Request(), $rootPages); + } +}