From b275546bd70d0468ac06b3feccbb16a45f068e53 Mon Sep 17 00:00:00 2001 From: alikon Date: Sun, 7 May 2023 09:04:20 +0200 Subject: [PATCH 01/59] 2task --- .../en-GB/plg_task_privacyconsent.ini | 47 ++++ .../en-GB/plg_task_privacyconsent.sys.ini | 7 + .../privacyconsent/forms/invalidateForm.xml | 19 ++ .../task/privacyconsent/forms/remindForm.xml | 31 +++ .../task/privacyconsent/privacyconsent.xml | 22 ++ .../task/privacyconsent/services/provider.php | 50 ++++ .../src/Extension/PrivacyConsent.php | 242 ++++++++++++++++++ 7 files changed, 418 insertions(+) create mode 100644 administrator/language/en-GB/plg_task_privacyconsent.ini create mode 100644 administrator/language/en-GB/plg_task_privacyconsent.sys.ini create mode 100644 plugins/task/privacyconsent/forms/invalidateForm.xml create mode 100644 plugins/task/privacyconsent/forms/remindForm.xml create mode 100644 plugins/task/privacyconsent/privacyconsent.xml create mode 100644 plugins/task/privacyconsent/services/provider.php create mode 100644 plugins/task/privacyconsent/src/Extension/PrivacyConsent.php diff --git a/administrator/language/en-GB/plg_task_privacyconsent.ini b/administrator/language/en-GB/plg_task_privacyconsent.ini new file mode 100644 index 0000000000000..c0b23c2d1e02f --- /dev/null +++ b/administrator/language/en-GB/plg_task_privacyconsent.ini @@ -0,0 +1,47 @@ +; Joomla! Project +; (C) 2023 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_TASK_PRIVACYCONSENT="Task - Privacy Consents" +PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATION_DESC="Number of days after which the privacy consent shall expire." +PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATION_LABEL="Expiration" +PLG_TASK_PRIVACYCONSENT_INVALIDATE_TITLE ="Expiration of privacy consents" +PLG_TASK_PRIVACYCONSENT_INVALIDATE_DESC ="Manage the expiration of privacy consents" +PLG_TASK_PRIVACYCONSENT_REMIND_DESC="Manage the remind of expiration of privacy consents" +PLG_TASK_PRIVACYCONSENT_REMIND_TITLE="Remind" +PLG_TASK_PRIVACYCONSENT_BODY="

The user consented to storing their user information using the IP address %s

The user agent string of the user's browser was:
%s

This information was automatically recorded when the user submitted their details on the website and checked the confirm box

" +PLG_TASK_PRIVACYCONSENT_CACHETIMEOUT_DESC="How often the check is performed." +PLG_TASK_PRIVACYCONSENT_CACHETIMEOUT_LABEL="Periodic check (days)" +PLG_TASK_PRIVACYCONSENT_CONSENT="User {username} consented to the privacy policy." + +PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY="Your Privacy Consent given at {URL} will expire in few days, you can renew the privacy consent for this website.\n\nIn order to do this, you can complete one of the following tasks:\n\n1. Visit the following URL: {TOKENURL}\n\n2. Copy your token from this email, visit the referenced URL, and paste your token into the form.\nURL: {FORMURL}\nToken: {TOKEN}\n\nPlease note that this token is only valid for this account." +PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT="Privacy Consent at {SITENAME}" +PLG_TASK_PRIVACYCONSENT_EXPIRATION_FIELDSET_LABEL="Expiration" +PLG_TASK_PRIVACYCONSENT_FIELD_ARTICLE_DESC="Select the article from the list or create a new one." +PLG_TASK_PRIVACYCONSENT_FIELD_ARTICLE_LABEL="Privacy Article" +PLG_TASK_PRIVACYCONSENT_FIELD_ENABLED_DESC="When enabled it performs checks for consent expiration." +PLG_TASK_PRIVACYCONSENT_FIELD_ENABLED_LABEL="Enable" +PLG_TASK_PRIVACYCONSENT_FIELD_ERROR="Agreement to the site's Privacy Policy is required." +PLG_TASK_PRIVACYCONSENT_FIELD_LABEL="Privacy Policy" +PLG_TASK_PRIVACYCONSENT_FIELD_MENU_ITEM_LABEL="Privacy Menu Item" +PLG_TASK_PRIVACYCONSENT_FIELD_TYPE_ARTICLE="Article" +PLG_TASK_PRIVACYCONSENT_FIELD_TYPE_LABEL="Privacy Type" +PLG_TASK_PRIVACYCONSENT_FIELD_TYPE_MENU_ITEM="Menu Item" +PLG_TASK_PRIVACYCONSENT_LABEL="Website Privacy" +PLG_TASK_PRIVACYCONSENT_MAIL_REQUEST_REMINDER_DESC="Reminder to renew the privacy consent for this website." +PLG_TASK_PRIVACYCONSENT_MAIL_REQUEST_REMINDER_TITLE="System - Privacy Consent: Renew Consent" +PLG_TASK_PRIVACYCONSENT_NOTE_FIELD_DEFAULT="By signing up to this website and agreeing to the Privacy Policy you agree to this website storing your information." +PLG_TASK_PRIVACYCONSENT_NOTE_FIELD_DESC="A summary of the site's privacy policy. If left blank then the default message will be used." +PLG_TASK_PRIVACYCONSENT_NOTE_FIELD_LABEL="Short Privacy Policy" +PLG_TASK_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE="Privacy consent has expired for %1$s." +PLG_TASK_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT="Privacy Consent Expired" +PLG_TASK_PRIVACYCONSENT_OPTION_AGREE="I agree" +PLG_TASK_PRIVACYCONSENT_OPTION_DO_NOT_AGREE="I do not agree" +PLG_TASK_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT="Please confirm that you consent to this website storing your information by agreeing to the privacy policy." +PLG_TASK_PRIVACYCONSENT_REDIRECT_MESSAGE_DESC="Custom message to be displayed on redirect. If left blank then the default message will be used." +PLG_TASK_PRIVACYCONSENT_REDIRECT_MESSAGE_LABEL="Redirect Message" +PLG_TASK_PRIVACYCONSENT_REMINDBEFORE_DESC="Number of days to send a reminder before the expiration of the privacy consent." +PLG_TASK_PRIVACYCONSENT_REMINDBEFORE_LABEL="Remind" +PLG_TASK_PRIVACYCONSENT_SUBJECT="Privacy Policy" +PLG_TASK_PRIVACYCONSENT_XML_DESCRIPTION="Task for remind expired consents and delete expired consents" diff --git a/administrator/language/en-GB/plg_task_privacyconsent.sys.ini b/administrator/language/en-GB/plg_task_privacyconsent.sys.ini new file mode 100644 index 0000000000000..4893e9d29203a --- /dev/null +++ b/administrator/language/en-GB/plg_task_privacyconsent.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2023 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_TASK_PRIVACYCONSENT="Task - Privacy Consents" +PLG_TASK_PRIVACYCONSENT_XML_DESCRIPTION="Task for remind expired consents and delete expired consents." diff --git a/plugins/task/privacyconsent/forms/invalidateForm.xml b/plugins/task/privacyconsent/forms/invalidateForm.xml new file mode 100644 index 0000000000000..84f6ace055865 --- /dev/null +++ b/plugins/task/privacyconsent/forms/invalidateForm.xml @@ -0,0 +1,19 @@ + +
+ +
+ +
+
+
diff --git a/plugins/task/privacyconsent/forms/remindForm.xml b/plugins/task/privacyconsent/forms/remindForm.xml new file mode 100644 index 0000000000000..0055a426dd4ac --- /dev/null +++ b/plugins/task/privacyconsent/forms/remindForm.xml @@ -0,0 +1,31 @@ + +
+ +
+ + +
+
+
diff --git a/plugins/task/privacyconsent/privacyconsent.xml b/plugins/task/privacyconsent/privacyconsent.xml new file mode 100644 index 0000000000000..de5cb2add2f45 --- /dev/null +++ b/plugins/task/privacyconsent/privacyconsent.xml @@ -0,0 +1,22 @@ + + + plg_task_privacyconsent + Joomla! Project + 2023-07 + (C) 2023 Open Source Matters, Inc. + GNU General Public License version 2 or later; see LICENSE.txt + admin@joomla.org + www.joomla.org + 4.4 + PLG_TASK_PRIVACYCONSENT_XML_DESCRIPTION + Joomla\Plugin\Task\PrivacyConsent + + forms + services + src + + + language/en-GB/plg_task_privacyconsent.ini + language/en-GB/plg_task_privacyconsent.sys.ini + + diff --git a/plugins/task/privacyconsent/services/provider.php b/plugins/task/privacyconsent/services/provider.php new file mode 100644 index 0000000000000..a773c7c305f70 --- /dev/null +++ b/plugins/task/privacyconsent/services/provider.php @@ -0,0 +1,50 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Database\DatabaseInterface; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Task\PrivacyConsent\Extension\PrivacyConsent; + +return new class () implements ServiceProviderInterface { + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function register(Container $container) + { + $container->set( + PluginInterface::class, + function (Container $container) { + $dispatcher = $container->get(DispatcherInterface::class); + + $plugin = new PrivacyConsent( + $dispatcher, + (array) PluginHelper::getPlugin('task', 'privacyconsent') + ); + $plugin->setApplication(Factory::getApplication()); + $plugin->setDatabase($container->get(DatabaseInterface::class)); + + return $plugin; + } + ); + } +}; diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php new file mode 100644 index 0000000000000..6d084da5f3e01 --- /dev/null +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -0,0 +1,242 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Plugin\Task\PrivacyConsent\Extension; + +use Joomla\CMS\Application\ApplicationHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Mail\Exception\MailDisabledException; +use Joomla\CMS\Mail\MailTemplate; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\User\UserHelper; +use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; +use Joomla\Component\Scheduler\Administrator\Task\Status; +use Joomla\Component\Scheduler\Administrator\Task\Task; +use Joomla\Component\Scheduler\Administrator\Traits\TaskPluginTrait; +use Joomla\Database\DatabaseAwareTrait; +use Joomla\Database\ParameterType; +use Joomla\Event\SubscriberInterface; +use PHPMailer\PHPMailer\Exception as phpmailerException; + + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * A task plugin. Offers 2 task routines Invalidate Expired Consents and Remind Expired Consents + * {@see ExecuteTaskEvent}. + * + * @since __DEPLOY_VERSION__ + */ +final class PrivacyConsent extends CMSPlugin implements SubscriberInterface +{ + use DatabaseAwareTrait; + use TaskPluginTrait; + + /** + * @var string[] + * @since __DEPLOY_VERSION__ + */ + private const TASKS_MAP = [ + 'invalidate.expired' => [ + 'langConstPrefix' => 'PLG_TASK_PRIVACYCONSENT_INVALIDATE', + 'method' => 'invalidateExpiredConsents', + 'form' => 'invalidateForm', + ], + 'remind.expired' => [ + 'langConstPrefix' => 'PLG_TASK_PRIVACYCONSENT_REMIND', + 'method' => 'remindExpiringConsents', + 'form' => 'remindForm', + ], + ]; + + /** + * @var boolean + * @since __DEPLOY_VERSION__ + */ + protected $autoloadLanguage = true; + + /** + * @inheritDoc + * + * @return string[] + * + * @since __DEPLOY_VERSION__ + */ + public static function getSubscribedEvents(): array + { + return [ + 'onTaskOptionsList' => 'advertiseRoutines', + 'onExecuteTask' => 'standardRoutineHandler', + 'onContentPrepareForm' => 'enhanceTaskItemForm', + ]; + } + + /** + * Method to send the remind for privacy consents renew. + * + * @param ExecuteTaskEvent $event The `onExecuteTask` event. + * + * @return integer The routine exit code. + * + * @since __DEPLOY_VERSION__ + * @throws \Exception + */ + private function remindExpiringConsents(ExecuteTaskEvent $event): int + { + // Load the parameters. + $expire = (int) $event->getArgument('params')->consentexpiration ?? 365; + $remind = (int) $event->getArgument('params')->remind ?? 30; + + $now = Factory::getDate()->toSql(); + $period = '-' . ($expire - $remind); + $db = $this->getDatabase(); + $query = $db->getQuery(true); + + $query->select($db->quoteName(['r.id', 'r.user_id', 'u.email'])) + ->from($db->quoteName('#__privacy_consents', 'r')) + ->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('r.user_id')) + ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) + ->where($db->quoteName('remind') . ' = 0') + ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')); + + try { + $users = $db->setQuery($query)->loadObjectList(); + } catch (\RuntimeException $exception) { + return Status::KNOCKOUT; + } + + // Do not process further if no expired consents found + if (empty($users)) { + return Status::OK; + } + + $app = $this->getApplication(); + $linkMode = $app->get('force_ssl', 0) == 2 ? Route::TLS_FORCE : Route::TLS_IGNORE; + + foreach ($users as $user) { + $token = ApplicationHelper::getHash(UserHelper::genRandomPassword()); + $hashedToken = UserHelper::hashPassword($token); + + // The mail + try { + $templateData = [ + 'sitename' => $app->get('sitename'), + 'url' => Uri::root(), + 'tokenurl' => Route::link('site', 'index.php?option=com_privacy&view=remind&remind_token=' . $token, false, $linkMode, true), + 'formurl' => Route::link('site', 'index.php?option=com_privacy&view=remind', false, $linkMode, true), + 'token' => $token, + ]; + + $mailer = new MailTemplate('plg_system_privacyconsent.request.reminder', $app->getLanguage()->getTag()); + $mailer->addTemplateData($templateData); + $mailer->addRecipient($user->email); + + $mailResult = $mailer->send(); + + if ($mailResult === false) { + return Status::KNOCKOUT; + } + + $userId = (int) $user->id; + + // Update the privacy_consents item to not send the reminder again + $query->clear() + ->update($db->quoteName('#__privacy_consents')) + ->set($db->quoteName('remind') . ' = 1') + ->set($db->quoteName('token') . ' = :token') + ->where($db->quoteName('id') . ' = :userid') + ->bind(':token', $hashedToken) + ->bind(':userid', $userId, ParameterType::INTEGER); + $db->setQuery($query); + + try { + $db->execute(); + } catch (\RuntimeException $e) { + return Status::KNOCKOUT; + } + } catch (MailDisabledException | phpmailerException $exception) { + return Status::KNOCKOUT; + } + } + $this->logTask('Remind end'); + + return Status::OK; + } + + /** + * Method to delete the expired privacy consents. + * + * @param ExecuteTaskEvent $event The `onExecuteTask` event. + * + * @return integer The routine exit code. + * + * @since __DEPLOY_VERSION__ + * @throws \Exception + */ + private function invalidateExpiredConsents(ExecuteTaskEvent $event): int + { + $expire = (int) $event->getArgument('params')->consentexpiration ?? 365; + $now = Factory::getDate()->toSql(); + $period = '-' . $expire; + $db = $this->getDatabase(); + $query = $db->getQuery(true); + + $query->select($db->quoteName(['id', 'user_id'])) + ->from($db->quoteName('#__privacy_consents')) + ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')) + ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) + ->where($db->quoteName('state') . ' = 1'); + + $db->setQuery($query); + + try { + $users = $db->loadObjectList(); + } catch (\RuntimeException $e) { + return Status::KNOCKOUT; + } + + // Do not process further if no expired consents found + if (empty($users)) { + return Status::OK; + } + + // Push a notification to the site's super users + /** @var MessageModel $messageModel */ + $messageModel = $this->getApplication()->bootComponent('com_messages')->getMVCFactory()->createModel('Message', 'Administrator'); + + foreach ($users as $user) { + $userId = (int) $user->id; + $query = $db->getQuery(true) + ->update($db->quoteName('#__privacy_consents')) + ->set($db->quoteName('state') . ' = 0') + ->where($db->quoteName('id') . ' = :userid') + ->bind(':userid', $userId, ParameterType::INTEGER); + $db->setQuery($query); + + try { + $db->execute(); + } catch (\RuntimeException $e) { + return Status::KNOCKOUT; + } + + $messageModel->notifySuperUsers( + Text::_('PLG_TASK_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT'), + Text::sprintf('PLG_TASK_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE', Factory::getUser($user->user_id)->username) + ); + } + + return Status::OK; + } +} From dda25ac466d5073e8fb5d9e3bab52a5a8fcaf30a Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 7 May 2023 13:03:17 +0200 Subject: [PATCH 02/59] Update plugins/task/privacyconsent/privacyconsent.xml Co-authored-by: jsanders --- plugins/task/privacyconsent/privacyconsent.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/privacyconsent.xml b/plugins/task/privacyconsent/privacyconsent.xml index de5cb2add2f45..7122070503ce0 100644 --- a/plugins/task/privacyconsent/privacyconsent.xml +++ b/plugins/task/privacyconsent/privacyconsent.xml @@ -7,7 +7,7 @@ GNU General Public License version 2 or later; see LICENSE.txt admin@joomla.org www.joomla.org - 4.4 + 5.0 PLG_TASK_PRIVACYCONSENT_XML_DESCRIPTION Joomla\Plugin\Task\PrivacyConsent From 3c3c85e259081b2f9af7f720b46c117b9787a4f5 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 7 May 2023 13:03:27 +0200 Subject: [PATCH 03/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 6d084da5f3e01..a67c009fb33fd 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -27,8 +27,6 @@ use Joomla\Database\ParameterType; use Joomla\Event\SubscriberInterface; use PHPMailer\PHPMailer\Exception as phpmailerException; - - // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects From c1b9e8c2f97b329300b6d6d414294e6135549156 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 7 May 2023 13:03:40 +0200 Subject: [PATCH 04/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index a67c009fb33fd..409de4855693a 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -61,6 +61,7 @@ final class PrivacyConsent extends CMSPlugin implements SubscriberInterface /** * @var boolean + * @since __DEPLOY_VERSION__ */ protected $autoloadLanguage = true; From 8aef2ee8fd76332c68baa16ca0e7d03a8435c244 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 7 May 2023 13:14:10 +0200 Subject: [PATCH 05/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 409de4855693a..b00b745a60e38 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -27,6 +27,7 @@ use Joomla\Database\ParameterType; use Joomla\Event\SubscriberInterface; use PHPMailer\PHPMailer\Exception as phpmailerException; + // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects From e25641d52f74d43f5bc2de8ad6269d4c657bd716 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 7 May 2023 13:14:23 +0200 Subject: [PATCH 06/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index b00b745a60e38..2963e357584c5 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -100,7 +100,7 @@ private function remindExpiringConsents(ExecuteTaskEvent $event): int $remind = (int) $event->getArgument('params')->remind ?? 30; $now = Factory::getDate()->toSql(); - $period = '-' . ($expire - $remind); + $period = '-' . ($expire - $remind); $db = $this->getDatabase(); $query = $db->getQuery(true); From 11d80e8f9a1cedc73d8f2289d49c400e14971574 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 7 May 2023 13:26:01 +0200 Subject: [PATCH 07/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 2963e357584c5..dda4824d0f61d 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -207,8 +207,8 @@ private function invalidateExpiredConsents(ExecuteTaskEvent $event): int return Status::KNOCKOUT; } - // Do not process further if no expired consents found - if (empty($users)) { + // Do not process further if no expired consents found + if (empty($users)) { return Status::OK; } From a6eb6233ec0f51e0bb8934938359f0e7b9231875 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 7 May 2023 13:26:09 +0200 Subject: [PATCH 08/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index dda4824d0f61d..9ac53bfb6fe2b 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -85,7 +85,7 @@ public static function getSubscribedEvents(): array /** * Method to send the remind for privacy consents renew. - * + * * @param ExecuteTaskEvent $event The `onExecuteTask` event. * * @return integer The routine exit code. From 56aea53170db9e8cbdb2a577d409cca21e1524fe Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 14 May 2023 08:44:14 +0200 Subject: [PATCH 09/59] Update plugins/task/privacyconsent/privacyconsent.xml Co-authored-by: jsanders --- plugins/task/privacyconsent/privacyconsent.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/privacyconsent.xml b/plugins/task/privacyconsent/privacyconsent.xml index 7122070503ce0..c47fd75c4747d 100644 --- a/plugins/task/privacyconsent/privacyconsent.xml +++ b/plugins/task/privacyconsent/privacyconsent.xml @@ -7,7 +7,7 @@ GNU General Public License version 2 or later; see LICENSE.txt admin@joomla.org www.joomla.org - 5.0 + 5.0.0 PLG_TASK_PRIVACYCONSENT_XML_DESCRIPTION Joomla\Plugin\Task\PrivacyConsent From 26c23cffa98f512b84071c5053e8fa52da36c677 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Tue, 13 Jun 2023 10:01:04 +0200 Subject: [PATCH 10/59] remove plg_system_privacyconsents --- .../privacyconsent/forms/privacyconsent.xml | 21 - .../system/privacyconsent/privacyconsent.xml | 142 ---- .../privacyconsent/services/provider.php | 49 -- .../src/Extension/PrivacyConsent.php | 698 ------------------ .../privacyconsent/src/Field/PrivacyField.php | 133 ---- 5 files changed, 1043 deletions(-) delete mode 100644 plugins/system/privacyconsent/forms/privacyconsent.xml delete mode 100644 plugins/system/privacyconsent/privacyconsent.xml delete mode 100644 plugins/system/privacyconsent/services/provider.php delete mode 100644 plugins/system/privacyconsent/src/Extension/PrivacyConsent.php delete mode 100644 plugins/system/privacyconsent/src/Field/PrivacyField.php diff --git a/plugins/system/privacyconsent/forms/privacyconsent.xml b/plugins/system/privacyconsent/forms/privacyconsent.xml deleted file mode 100644 index f1c46a2f8c965..0000000000000 --- a/plugins/system/privacyconsent/forms/privacyconsent.xml +++ /dev/null @@ -1,21 +0,0 @@ - -
- -
- - - - -
-
-
diff --git a/plugins/system/privacyconsent/privacyconsent.xml b/plugins/system/privacyconsent/privacyconsent.xml deleted file mode 100644 index 9f7d31f020823..0000000000000 --- a/plugins/system/privacyconsent/privacyconsent.xml +++ /dev/null @@ -1,142 +0,0 @@ - - - plg_system_privacyconsent - Joomla! Project - 2018-04 - (C) 2018 Open Source Matters, Inc. - GNU General Public License version 2 or later; see LICENSE.txt - admin@joomla.org - www.joomla.org - 3.9.0 - PLG_SYSTEM_PRIVACYCONSENT_XML_DESCRIPTION - Joomla\Plugin\System\PrivacyConsent - - forms - services - src - - - language/en-GB/plg_system_privacyconsent.ini - language/en-GB/plg_system_privacyconsent.sys.ini - - - -
- - - - - - - - -
-
- - - - - - - - -
-
-
-
diff --git a/plugins/system/privacyconsent/services/provider.php b/plugins/system/privacyconsent/services/provider.php deleted file mode 100644 index e688786e06139..0000000000000 --- a/plugins/system/privacyconsent/services/provider.php +++ /dev/null @@ -1,49 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -defined('_JEXEC') or die; - -use Joomla\CMS\Extension\PluginInterface; -use Joomla\CMS\Factory; -use Joomla\CMS\Plugin\PluginHelper; -use Joomla\Database\DatabaseInterface; -use Joomla\DI\Container; -use Joomla\DI\ServiceProviderInterface; -use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\System\PrivacyConsent\Extension\PrivacyConsent; - -return new class () implements ServiceProviderInterface { - /** - * Registers the service provider with a DI container. - * - * @param Container $container The DI container. - * - * @return void - * - * @since 4.4.0 - */ - public function register(Container $container): void - { - $container->set( - PluginInterface::class, - function (Container $container) { - $dispatcher = $container->get(DispatcherInterface::class); - $plugin = new PrivacyConsent( - $dispatcher, - (array) PluginHelper::getPlugin('system', 'privacyconsent') - ); - $plugin->setApplication(Factory::getApplication()); - $plugin->setDatabase($container->get(DatabaseInterface::class)); - - return $plugin; - } - ); - } -}; diff --git a/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php deleted file mode 100644 index 0efffb6b201f4..0000000000000 --- a/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php +++ /dev/null @@ -1,698 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\Plugin\System\PrivacyConsent\Extension; - -use Exception; -use InvalidArgumentException; -use Joomla\CMS\Application\ApplicationHelper; -use Joomla\CMS\Cache\Cache; -use Joomla\CMS\Factory; -use Joomla\CMS\Form\Form; -use Joomla\CMS\Form\FormHelper; -use Joomla\CMS\Language\Associations; -use Joomla\CMS\Language\Text; -use Joomla\CMS\Mail\Exception\MailDisabledException; -use Joomla\CMS\Mail\MailTemplate; -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\CMS\Router\Route; -use Joomla\CMS\Uri\Uri; -use Joomla\CMS\User\UserHelper; -use Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel; -use Joomla\Component\Messages\Administrator\Model\MessageModel; -use Joomla\Database\DatabaseAwareTrait; -use Joomla\Database\Exception\ExecutionFailureException; -use Joomla\Database\ParameterType; -use Joomla\Utilities\ArrayHelper; -use PHPMailer\PHPMailer\Exception as phpmailerException; -use RuntimeException; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * An example custom privacyconsent plugin. - * - * @since 3.9.0 - */ -final class PrivacyConsent extends CMSPlugin -{ - use DatabaseAwareTrait; - - /** - * Load the language file on instantiation. - * - * @var boolean - * @since 3.9.0 - */ - protected $autoloadLanguage = true; - - /** - * Adds additional fields to the user editing form - * - * @param Form $form The form to be altered. - * @param mixed $data The associated data for the form. - * - * @return boolean - * - * @since 3.9.0 - */ - public function onContentPrepareForm(Form $form, $data) - { - // Check we are manipulating a valid form - we only display this on user registration form and user profile form. - $name = $form->getName(); - - if (!in_array($name, ['com_users.profile', 'com_users.registration'])) { - return true; - } - - // We only display this if user has not consented before - if (is_object($data)) { - $userId = $data->id ?? 0; - - if ($userId > 0 && $this->isUserConsented($userId)) { - return true; - } - } - - // Add the privacy policy fields to the form. - FormHelper::addFieldPrefix('Joomla\\Plugin\\System\\PrivacyConsent\\Field'); - FormHelper::addFormPath(JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name . '/forms'); - $form->loadFile('privacyconsent'); - - $privacyType = $this->params->get('privacy_type', 'article'); - $privacyId = ($privacyType == 'menu_item') ? $this->getPrivacyItemId() : $this->getPrivacyArticleId(); - $privacynote = $this->params->get('privacy_note'); - - // Push the privacy article ID into the privacy field. - $form->setFieldAttribute('privacy', $privacyType, $privacyId, 'privacyconsent'); - $form->setFieldAttribute('privacy', 'note', $privacynote, 'privacyconsent'); - } - - /** - * Method is called before user data is stored in the database - * - * @param array $user Holds the old user data. - * @param boolean $isNew True if a new user is stored. - * @param array $data Holds the new user data. - * - * @return boolean - * - * @since 3.9.0 - * @throws InvalidArgumentException on missing required data. - */ - public function onUserBeforeSave($user, $isNew, $data) - { - // // Only check for front-end user creation/update profile - if ($this->getApplication()->isClient('administrator')) { - return true; - } - - $userId = ArrayHelper::getValue($user, 'id', 0, 'int'); - - // User already consented before, no need to check it further - if ($userId > 0 && $this->isUserConsented($userId)) { - return true; - } - - // Check that the privacy is checked if required ie only in registration from frontend. - $input = $this->getApplication()->getInput(); - $option = $input->get('option'); - $task = $input->post->get('task'); - $form = $input->post->get('jform', [], 'array'); - - if ( - $option == 'com_users' && in_array($task, ['registration.register', 'profile.save']) - && empty($form['privacyconsent']['privacy']) - ) { - throw new InvalidArgumentException($this->getApplication()->getLanguage()->_('PLG_SYSTEM_PRIVACYCONSENT_FIELD_ERROR')); - } - - return true; - } - - /** - * Saves user privacy confirmation - * - * @param array $data entered user data - * @param boolean $isNew true if this is a new user - * @param boolean $result true if saving the user worked - * @param string $error error message - * - * @return void - * - * @since 3.9.0 - */ - public function onUserAfterSave($data, $isNew, $result, $error): void - { - // Only create an entry on front-end user creation/update profile - if ($this->getApplication()->isClient('administrator')) { - return; - } - - // Get the user's ID - $userId = ArrayHelper::getValue($data, 'id', 0, 'int'); - - // If user already consented before, no need to check it further - if ($userId > 0 && $this->isUserConsented($userId)) { - return; - } - - $input = $this->getApplication()->getInput(); - $option = $input->get('option'); - $task = $input->post->get('task'); - $form = $input->post->get('jform', [], 'array'); - - if ( - $option == 'com_users' - && in_array($task, ['registration.register', 'profile.save']) - && !empty($form['privacyconsent']['privacy']) - ) { - $userId = ArrayHelper::getValue($data, 'id', 0, 'int'); - - // Get the user's IP address - $ip = $input->server->get('REMOTE_ADDR', '', 'string'); - - // Get the user agent string - $userAgent = $input->server->get('HTTP_USER_AGENT', '', 'string'); - - // Create the user note - $userNote = (object) [ - 'user_id' => $userId, - 'subject' => 'PLG_SYSTEM_PRIVACYCONSENT_SUBJECT', - 'body' => Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_BODY', $ip, $userAgent), - 'created' => Factory::getDate()->toSql(), - ]; - - try { - $this->getDatabase()->insertObject('#__privacy_consents', $userNote); - } catch (Exception $e) { - // Do nothing if the save fails - } - - $userId = ArrayHelper::getValue($data, 'id', 0, 'int'); - - $message = [ - 'action' => 'consent', - 'id' => $userId, - 'title' => $data['name'], - 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $userId, - 'userid' => $userId, - 'username' => $data['username'], - 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $userId, - ]; - - /** @var ActionlogModel $model */ - $model = $this->getApplication()->bootComponent('com_actionlogs')->getMVCFactory()->createModel('Actionlog', 'Administrator'); - $model->addLog([$message], 'PLG_SYSTEM_PRIVACYCONSENT_CONSENT', 'plg_system_privacyconsent', $userId); - } - } - - /** - * Remove all user privacy consent information for the given user ID - * - * Method is called after user data is deleted from the database - * - * @param array $user Holds the user data - * @param boolean $success True if user was successfully stored in the database - * @param string $msg Message - * - * @return void - * - * @since 3.9.0 - */ - public function onUserAfterDelete($user, $success, $msg): void - { - if (!$success) { - return; - } - - $userId = ArrayHelper::getValue($user, 'id', 0, 'int'); - - if ($userId) { - // Remove user's consent - $query = $this->getDatabase()->getQuery(true) - ->delete($this->getDatabase()->quoteName('#__privacy_consents')) - ->where($this->getDatabase()->quoteName('user_id') . ' = :userid') - ->bind(':userid', $userId, ParameterType::INTEGER); - $this->getDatabase()->setQuery($query); - $this->getDatabase()->execute(); - } - } - - /** - * If logged in users haven't agreed to privacy consent, redirect them to profile edit page, ask them to agree to - * privacy consent before allowing access to any other pages - * - * @return void - * - * @since 3.9.0 - */ - public function onAfterRoute() - { - // Run this in frontend only - if (!$this->getApplication()->isClient('site')) { - return; - } - - $userId = $this->getApplication()->getIdentity()->id; - - // Check to see whether user already consented, if not, redirect to user profile page - if ($userId > 0) { - // If user consented before, no need to check it further - if ($this->isUserConsented($userId)) { - return; - } - - $input = $this->getApplication()->getInput(); - $option = $input->getCmd('option'); - $task = $input->get('task', ''); - $view = $input->getString('view', ''); - $layout = $input->getString('layout', ''); - $id = $input->getInt('id'); - - $privacyArticleId = $this->getPrivacyArticleId(); - - /* - * If user is already on edit profile screen or view privacy article - * or press update/apply button, or logout, do nothing to avoid infinite redirect - */ - $allowedUserTasks = [ - 'profile.save', 'profile.apply', 'user.logout', 'user.menulogout', - 'method', 'methods', 'captive', 'callback', - ]; - $isAllowedUserTask = in_array($task, $allowedUserTasks) - || substr($task, 0, 8) === 'captive.' - || substr($task, 0, 8) === 'methods.' - || substr($task, 0, 7) === 'method.' - || substr($task, 0, 9) === 'callback.'; - - if ( - ($option == 'com_users' && $isAllowedUserTask) - || ($option == 'com_content' && $view == 'article' && $id == $privacyArticleId) - || ($option == 'com_users' && $view == 'profile' && $layout == 'edit') - ) { - return; - } - - // Redirect to com_users profile edit - $this->getApplication()->enqueueMessage($this->getRedirectMessage(), 'notice'); - $link = 'index.php?option=com_users&view=profile&layout=edit'; - $this->getApplication()->redirect(Route::_($link, false)); - } - } - - /** - * Event to specify whether a privacy policy has been published. - * - * @param array &$policy The privacy policy status data, passed by reference, with keys "published", "editLink" and "articlePublished". - * - * @return void - * - * @since 3.9.0 - */ - public function onPrivacyCheckPrivacyPolicyPublished(&$policy) - { - // If another plugin has already indicated a policy is published, we won't change anything here - if ($policy['published']) { - return; - } - - $articleId = (int) $this->params->get('privacy_article'); - - if (!$articleId) { - return; - } - - // Check if the article exists in database and is published - $query = $this->getDatabase()->getQuery(true) - ->select($this->getDatabase()->quoteName(['id', 'state'])) - ->from($this->getDatabase()->quoteName('#__content')) - ->where($this->getDatabase()->quoteName('id') . ' = :id') - ->bind(':id', $articleId, ParameterType::INTEGER); - $this->getDatabase()->setQuery($query); - - $article = $this->getDatabase()->loadObject(); - - // Check if the article exists - if (!$article) { - return; - } - - // Check if the article is published - if ($article->state == 1) { - $policy['articlePublished'] = true; - } - - $policy['published'] = true; - $policy['editLink'] = Route::_('index.php?option=com_content&task=article.edit&id=' . $articleId); - } - - /** - * Returns the configured redirect message and falls back to the default version. - * - * @return string redirect message - * - * @since 3.9.0 - */ - private function getRedirectMessage() - { - $messageOnRedirect = trim($this->params->get('messageOnRedirect', '')); - - if (empty($messageOnRedirect)) { - return $this->getApplication()->getLanguage()->_('PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT'); - } - - return $messageOnRedirect; - } - - /** - * Method to check if the given user has consented yet - * - * @param integer $userId ID of uer to check - * - * @return boolean - * - * @since 3.9.0 - */ - private function isUserConsented($userId) - { - $userId = (int) $userId; - $db = $this->getDatabase(); - $query = $db->getQuery(true); - - $query->select('COUNT(*)') - ->from($db->quoteName('#__privacy_consents')) - ->where($db->quoteName('user_id') . ' = :userid') - ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) - ->where($db->quoteName('state') . ' = 1') - ->bind(':userid', $userId, ParameterType::INTEGER); - $db->setQuery($query); - - return (int) $db->loadResult() > 0; - } - - /** - * Get privacy article ID. If the site is a multilingual website and there is associated article for the - * current language, ID of the associated article will be returned - * - * @return integer - * - * @since 3.9.0 - */ - private function getPrivacyArticleId() - { - $privacyArticleId = $this->params->get('privacy_article'); - - if ($privacyArticleId > 0 && Associations::isEnabled()) { - $privacyAssociated = Associations::getAssociations('com_content', '#__content', 'com_content.item', $privacyArticleId); - $currentLang = $this->getApplication()->getLanguage()->getTag(); - - if (isset($privacyAssociated[$currentLang])) { - $privacyArticleId = $privacyAssociated[$currentLang]->id; - } - } - - return $privacyArticleId; - } - - /** - * Get privacy menu item ID. If the site is a multilingual website and there is associated menu item for the - * current language, ID of the associated menu item will be returned. - * - * @return integer - * - * @since 4.0.0 - */ - private function getPrivacyItemId() - { - $itemId = $this->params->get('privacy_menu_item'); - - if ($itemId > 0 && Associations::isEnabled()) { - $privacyAssociated = Associations::getAssociations('com_menus', '#__menu', 'com_menus.item', $itemId, 'id', '', ''); - $currentLang = $this->getApplication()->getTag(); - - if (isset($privacyAssociated[$currentLang])) { - $itemId = $privacyAssociated[$currentLang]->id; - } - } - - return $itemId; - } - - /** - * The privacy consent expiration check code is triggered after the page has fully rendered. - * - * @return void - * - * @since 3.9.0 - */ - public function onAfterRender() - { - if (!$this->params->get('enabled', 0)) { - return; - } - - $cacheTimeout = (int) $this->params->get('cachetimeout', 30); - $cacheTimeout = 24 * 3600 * $cacheTimeout; - - // Do we need to run? Compare the last run timestamp stored in the plugin's options with the current - // timestamp. If the difference is greater than the cache timeout we shall not execute again. - $now = time(); - $last = (int) $this->params->get('lastrun', 0); - - if ((abs($now - $last) < $cacheTimeout)) { - return; - } - - // Update last run status - $this->params->set('lastrun', $now); - - $paramsJson = $this->params->toString('JSON'); - $db = $this->getDatabase(); - $query = $db->getQuery(true) - ->update($db->quoteName('#__extensions')) - ->set($db->quoteName('params') . ' = :params') - ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) - ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) - ->where($db->quoteName('element') . ' = ' . $db->quote('privacyconsent')) - ->bind(':params', $paramsJson); - - try { - // Lock the tables to prevent multiple plugin executions causing a race condition - $db->lockTable('#__extensions'); - } catch (Exception $e) { - // If we can't lock the tables it's too risky to continue execution - return; - } - - try { - // Update the plugin parameters - $result = $db->setQuery($query)->execute(); - $this->clearCacheGroups(['com_plugins'], [0, 1]); - } catch (Exception $exc) { - // If we failed to execute - $db->unlockTables(); - $result = false; - } - - try { - // Unlock the tables after writing - $db->unlockTables(); - } catch (Exception $e) { - // If we can't lock the tables assume we have somehow failed - $result = false; - } - - // Abort on failure - if (!$result) { - return; - } - - // Delete the expired privacy consents - $this->invalidateExpiredConsents(); - - // Remind for privacy consents near to expire - $this->remindExpiringConsents(); - } - - /** - * Method to send the remind for privacy consents renew - * - * @return integer - * - * @since 3.9.0 - */ - private function remindExpiringConsents() - { - // Load the parameters. - $expire = (int) $this->params->get('consentexpiration', 365); - $remind = (int) $this->params->get('remind', 30); - $now = Factory::getDate()->toSql(); - $period = '-' . ($expire - $remind); - $db = $this->getDatabase(); - $query = $db->getQuery(true); - - $query->select($db->quoteName(['r.id', 'r.user_id', 'u.email'])) - ->from($db->quoteName('#__privacy_consents', 'r')) - ->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('r.user_id')) - ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) - ->where($db->quoteName('remind') . ' = 0') - ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')); - - try { - $users = $db->setQuery($query)->loadObjectList(); - } catch (ExecutionFailureException $exception) { - return false; - } - - $app = $this->getApplication(); - $linkMode = $app->get('force_ssl', 0) == 2 ? Route::TLS_FORCE : Route::TLS_IGNORE; - - foreach ($users as $user) { - $token = ApplicationHelper::getHash(UserHelper::genRandomPassword()); - $hashedToken = UserHelper::hashPassword($token); - - // The mail - try { - $templateData = [ - 'sitename' => $app->get('sitename'), - 'url' => Uri::root(), - 'tokenurl' => Route::link('site', 'index.php?option=com_privacy&view=remind&remind_token=' . $token, false, $linkMode, true), - 'formurl' => Route::link('site', 'index.php?option=com_privacy&view=remind', false, $linkMode, true), - 'token' => $token, - ]; - - $mailer = new MailTemplate('plg_system_privacyconsent.request.reminder', $app->getLanguage()->getTag()); - $mailer->addTemplateData($templateData); - $mailer->addRecipient($user->email); - - $mailResult = $mailer->send(); - - if ($mailResult === false) { - return false; - } - - $userId = (int) $user->id; - - // Update the privacy_consents item to not send the reminder again - $query->clear() - ->update($db->quoteName('#__privacy_consents')) - ->set($db->quoteName('remind') . ' = 1') - ->set($db->quoteName('token') . ' = :token') - ->where($db->quoteName('id') . ' = :userid') - ->bind(':token', $hashedToken) - ->bind(':userid', $userId, ParameterType::INTEGER); - $db->setQuery($query); - - try { - $db->execute(); - } catch (RuntimeException $e) { - return false; - } - } catch (MailDisabledException | phpmailerException $exception) { - return false; - } - } - } - - /** - * Method to delete the expired privacy consents - * - * @return boolean - * - * @since 3.9.0 - */ - private function invalidateExpiredConsents() - { - // Load the parameters. - $expire = (int) $this->params->get('consentexpiration', 365); - $now = Factory::getDate()->toSql(); - $period = '-' . $expire; - $db = $this->getDatabase(); - $query = $db->getQuery(true); - - $query->select($db->quoteName(['id', 'user_id'])) - ->from($db->quoteName('#__privacy_consents')) - ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')) - ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) - ->where($db->quoteName('state') . ' = 1'); - - $db->setQuery($query); - - try { - $users = $db->loadObjectList(); - } catch (RuntimeException $e) { - return false; - } - - // Do not process further if no expired consents found - if (empty($users)) { - return true; - } - - // Push a notification to the site's super users - /** @var MessageModel $messageModel */ - $messageModel = $this->getApplication()->bootComponent('com_messages')->getMVCFactory()->createModel('Message', 'Administrator'); - - foreach ($users as $user) { - $userId = (int) $user->id; - $query = $db->getQuery(true) - ->update($db->quoteName('#__privacy_consents')) - ->set($db->quoteName('state') . ' = 0') - ->where($db->quoteName('id') . ' = :userid') - ->bind(':userid', $userId, ParameterType::INTEGER); - $db->setQuery($query); - - try { - $db->execute(); - } catch (RuntimeException $e) { - return false; - } - - $messageModel->notifySuperUsers( - $this->getApplication()->getLanguage()->_('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT'), - Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE', Factory::getUser($user->user_id)->username) - ); - } - - return true; - } - /** - * Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp. - * - * @param array $clearGroups The cache groups to clean - * @param array $cacheClients The cache clients (site, admin) to clean - * - * @return void - * - * @since 3.9.0 - */ - private function clearCacheGroups(array $clearGroups, array $cacheClients = [0, 1]) - { - foreach ($clearGroups as $group) { - foreach ($cacheClients as $client_id) { - try { - $options = [ - 'defaultgroup' => $group, - 'cachebase' => $client_id ? JPATH_ADMINISTRATOR . '/cache' : - $this->getApplication()->get('cache_path', JPATH_SITE . '/cache'), - ]; - - $cache = Cache::getInstance('callback', $options); - $cache->clean(); - } catch (Exception $e) { - // Ignore it - } - } - } - } -} diff --git a/plugins/system/privacyconsent/src/Field/PrivacyField.php b/plugins/system/privacyconsent/src/Field/PrivacyField.php deleted file mode 100644 index 980fa37b5ced1..0000000000000 --- a/plugins/system/privacyconsent/src/Field/PrivacyField.php +++ /dev/null @@ -1,133 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\Plugin\System\PrivacyConsent\Field; - -use Joomla\CMS\Factory; -use Joomla\CMS\Form\Field\RadioField; -use Joomla\CMS\Language\Multilanguage; -use Joomla\CMS\Language\Text; -use Joomla\Component\Content\Site\Helper\RouteHelper; -use Joomla\Database\ParameterType; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * Provides input for privacy - * - * @since 3.9.0 - */ -class PrivacyField extends RadioField -{ - /** - * The form field type. - * - * @var string - * @since 3.9.0 - */ - protected $type = 'privacy'; - - /** - * Method to get the field input markup. - * - * @return string The field input markup. - * - * @since 3.9.0 - */ - protected function getInput() - { - // Display the message before the field - echo $this->getRenderer('plugins.system.privacyconsent.message')->render($this->getLayoutData()); - - return parent::getInput(); - } - - /** - * Method to get the field label markup. - * - * @return string The field label markup. - * - * @since 3.9.0 - */ - protected function getLabel() - { - if ($this->hidden) { - return ''; - } - - return $this->getRenderer('plugins.system.privacyconsent.label')->render($this->getLayoutData()); - } - - /** - * Method to get the data to be passed to the layout for rendering. - * - * @return array - * - * @since 3.9.4 - */ - protected function getLayoutData() - { - $data = parent::getLayoutData(); - - $article = false; - $link = false; - $privacyArticle = $this->element['article'] > 0 ? (int) $this->element['article'] : 0; - - if ($privacyArticle && Factory::getApplication()->isClient('site')) { - $db = $this->getDatabase(); - $query = $db->getQuery(true) - ->select($db->quoteName(['id', 'alias', 'catid', 'language'])) - ->from($db->quoteName('#__content')) - ->where($db->quoteName('id') . ' = :id') - ->bind(':id', $privacyArticle, ParameterType::INTEGER); - $db->setQuery($query); - $article = $db->loadObject(); - - $slug = $article->alias ? ($article->id . ':' . $article->alias) : $article->id; - $article->link = RouteHelper::getArticleRoute($slug, $article->catid, $article->language); - $link = $article->link; - } - - $privacyMenuItem = $this->element['menu_item'] > 0 ? (int) $this->element['menu_item'] : 0; - - if ($privacyMenuItem && Factory::getApplication()->isClient('site')) { - $link = 'index.php?Itemid=' . $privacyMenuItem; - - if (Multilanguage::isEnabled()) { - $db = $this->getDatabase(); - $query = $db->getQuery(true) - ->select($db->quoteName(['id', 'language'])) - ->from($db->quoteName('#__menu')) - ->where($db->quoteName('id') . ' = :id') - ->bind(':id', $privacyMenuItem, ParameterType::INTEGER); - $db->setQuery($query); - $menuItem = $db->loadObject(); - - $link .= '&lang=' . $menuItem->language; - } - } - - $extraData = [ - 'privacynote' => !empty($this->element['note']) ? $this->element['note'] : Text::_('PLG_SYSTEM_PRIVACYCONSENT_NOTE_FIELD_DEFAULT'), - 'options' => $this->getOptions(), - 'value' => (string) $this->value, - 'translateLabel' => $this->translateLabel, - 'translateDescription' => $this->translateDescription, - 'translateHint' => $this->translateHint, - 'privacyArticle' => $privacyArticle, - 'article' => $article, - 'privacyLink' => $link, - ]; - - return array_merge($data, $extraData); - } -} From 066990a6de4d28ad0bb72aaad34a5ad721708afa Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Tue, 13 Jun 2023 10:20:05 +0200 Subject: [PATCH 11/59] Update base.sql --- installation/sql/mysql/base.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation/sql/mysql/base.sql b/installation/sql/mysql/base.sql index d181db1fbcfe4..6a15feadc2cc6 100644 --- a/installation/sql/mysql/base.sql +++ b/installation/sql/mysql/base.sql @@ -344,7 +344,6 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_system_log', 'plugin', 'log', 'system', 0, 1, 1, 0, 1, '', '', '', 11, 0), (0, 'plg_system_logout', 'plugin', 'logout', 'system', 0, 1, 1, 0, 1, '', '', '', 12, 0), (0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), -(0, 'plg_system_privacyconsent', 'plugin', 'privacyconsent', 'system', 0, 0, 1, 0, 1, '', '{}', '', 14, 0), (0, 'plg_system_redirect', 'plugin', 'redirect', 'system', 0, 0, 1, 0, 1, '', '', '', 15, 0), (0, 'plg_system_remember', 'plugin', 'remember', 'system', 0, 1, 1, 0, 1, '', '', '', 16, 0), (0, 'plg_system_schedulerunner', 'plugin', 'schedulerunner', 'system', 0, 1, 1, 0, 1, '', '{}', '', 17, 0), @@ -360,6 +359,7 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_task_demotasks', 'plugin', 'demotasks', 'task', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), (0, 'plg_task_requests', 'plugin', 'requests', 'task', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), (0, 'plg_task_sitestatus', 'plugin', 'sitestatus', 'task', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), +(0, 'plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), (0, 'plg_multifactorauth_totp', 'plugin', 'totp', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 1, 0), (0, 'plg_multifactorauth_yubikey', 'plugin', 'yubikey', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_multifactorauth_webauthn', 'plugin', 'webauthn', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 3, 0), From eb7ca9548d4d632035355b5762235ad9ee7c6504 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Tue, 13 Jun 2023 10:25:51 +0200 Subject: [PATCH 12/59] Update base.sql --- installation/sql/postgresql/base.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation/sql/postgresql/base.sql b/installation/sql/postgresql/base.sql index 9bf300f290b23..1b61ffca41261 100644 --- a/installation/sql/postgresql/base.sql +++ b/installation/sql/postgresql/base.sql @@ -350,7 +350,6 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_system_log', 'plugin', 'log', 'system', 0, 1, 1, 0, 1, '', '', '', 11, 0), (0, 'plg_system_logout', 'plugin', 'logout', 'system', 0, 1, 1, 0, 1, '', '', '', 12, 0), (0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), -(0, 'plg_system_privacyconsent', 'plugin', 'privacyconsent', 'system', 0, 0, 1, 0, 1, '', '{}', '', 14, 0), (0, 'plg_system_redirect', 'plugin', 'redirect', 'system', 0, 0, 1, 0, 1, '', '', '', 15, 0), (0, 'plg_system_remember', 'plugin', 'remember', 'system', 0, 1, 1, 0, 1, '', '', '', 16, 0), (0, 'plg_system_schedulerunner', 'plugin', 'schedulerunner', 'system', 0, 1, 1, 0, 1, '', '{}', '', 17, 0), @@ -366,6 +365,7 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_task_demotasks', 'plugin', 'demotasks', 'task', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), (0, 'plg_task_requests', 'plugin', 'requests', 'task', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), (0, 'plg_task_sitestatus', 'plugin', 'sitestatus', 'task', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), +(0, 'plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), (0, 'plg_multifactorauth_totp', 'plugin', 'totp', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 1, 0), (0, 'plg_multifactorauth_yubikey', 'plugin', 'yubikey', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_multifactorauth_webauthn', 'plugin', 'webauthn', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 3, 0), From c08ca1aadafda61be5240fd275a1a32a83caac3e Mon Sep 17 00:00:00 2001 From: alikon Date: Tue, 13 Jun 2023 12:43:46 +0200 Subject: [PATCH 13/59] re-add system privacyconsent --- .../privacyconsent/forms/privacyconsent.xml | 21 + .../system/privacyconsent/privacyconsent.xml | 142 ++++ .../privacyconsent/services/provider.php | 49 ++ .../src/Extension/PrivacyConsent.php | 698 ++++++++++++++++++ .../privacyconsent/src/Field/PrivacyField.php | 133 ++++ 5 files changed, 1043 insertions(+) create mode 100644 plugins/system/privacyconsent/forms/privacyconsent.xml create mode 100644 plugins/system/privacyconsent/privacyconsent.xml create mode 100644 plugins/system/privacyconsent/services/provider.php create mode 100644 plugins/system/privacyconsent/src/Extension/PrivacyConsent.php create mode 100644 plugins/system/privacyconsent/src/Field/PrivacyField.php diff --git a/plugins/system/privacyconsent/forms/privacyconsent.xml b/plugins/system/privacyconsent/forms/privacyconsent.xml new file mode 100644 index 0000000000000..f1c46a2f8c965 --- /dev/null +++ b/plugins/system/privacyconsent/forms/privacyconsent.xml @@ -0,0 +1,21 @@ + +
+ +
+ + + + +
+
+
diff --git a/plugins/system/privacyconsent/privacyconsent.xml b/plugins/system/privacyconsent/privacyconsent.xml new file mode 100644 index 0000000000000..9f7d31f020823 --- /dev/null +++ b/plugins/system/privacyconsent/privacyconsent.xml @@ -0,0 +1,142 @@ + + + plg_system_privacyconsent + Joomla! Project + 2018-04 + (C) 2018 Open Source Matters, Inc. + GNU General Public License version 2 or later; see LICENSE.txt + admin@joomla.org + www.joomla.org + 3.9.0 + PLG_SYSTEM_PRIVACYCONSENT_XML_DESCRIPTION + Joomla\Plugin\System\PrivacyConsent + + forms + services + src + + + language/en-GB/plg_system_privacyconsent.ini + language/en-GB/plg_system_privacyconsent.sys.ini + + + +
+ + + + + + + + +
+
+ + + + + + + + +
+
+
+
diff --git a/plugins/system/privacyconsent/services/provider.php b/plugins/system/privacyconsent/services/provider.php new file mode 100644 index 0000000000000..e688786e06139 --- /dev/null +++ b/plugins/system/privacyconsent/services/provider.php @@ -0,0 +1,49 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Database\DatabaseInterface; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\System\PrivacyConsent\Extension\PrivacyConsent; + +return new class () implements ServiceProviderInterface { + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since 4.4.0 + */ + public function register(Container $container): void + { + $container->set( + PluginInterface::class, + function (Container $container) { + $dispatcher = $container->get(DispatcherInterface::class); + $plugin = new PrivacyConsent( + $dispatcher, + (array) PluginHelper::getPlugin('system', 'privacyconsent') + ); + $plugin->setApplication(Factory::getApplication()); + $plugin->setDatabase($container->get(DatabaseInterface::class)); + + return $plugin; + } + ); + } +}; diff --git a/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php new file mode 100644 index 0000000000000..0efffb6b201f4 --- /dev/null +++ b/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php @@ -0,0 +1,698 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Plugin\System\PrivacyConsent\Extension; + +use Exception; +use InvalidArgumentException; +use Joomla\CMS\Application\ApplicationHelper; +use Joomla\CMS\Cache\Cache; +use Joomla\CMS\Factory; +use Joomla\CMS\Form\Form; +use Joomla\CMS\Form\FormHelper; +use Joomla\CMS\Language\Associations; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Mail\Exception\MailDisabledException; +use Joomla\CMS\Mail\MailTemplate; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\User\UserHelper; +use Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel; +use Joomla\Component\Messages\Administrator\Model\MessageModel; +use Joomla\Database\DatabaseAwareTrait; +use Joomla\Database\Exception\ExecutionFailureException; +use Joomla\Database\ParameterType; +use Joomla\Utilities\ArrayHelper; +use PHPMailer\PHPMailer\Exception as phpmailerException; +use RuntimeException; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * An example custom privacyconsent plugin. + * + * @since 3.9.0 + */ +final class PrivacyConsent extends CMSPlugin +{ + use DatabaseAwareTrait; + + /** + * Load the language file on instantiation. + * + * @var boolean + * @since 3.9.0 + */ + protected $autoloadLanguage = true; + + /** + * Adds additional fields to the user editing form + * + * @param Form $form The form to be altered. + * @param mixed $data The associated data for the form. + * + * @return boolean + * + * @since 3.9.0 + */ + public function onContentPrepareForm(Form $form, $data) + { + // Check we are manipulating a valid form - we only display this on user registration form and user profile form. + $name = $form->getName(); + + if (!in_array($name, ['com_users.profile', 'com_users.registration'])) { + return true; + } + + // We only display this if user has not consented before + if (is_object($data)) { + $userId = $data->id ?? 0; + + if ($userId > 0 && $this->isUserConsented($userId)) { + return true; + } + } + + // Add the privacy policy fields to the form. + FormHelper::addFieldPrefix('Joomla\\Plugin\\System\\PrivacyConsent\\Field'); + FormHelper::addFormPath(JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name . '/forms'); + $form->loadFile('privacyconsent'); + + $privacyType = $this->params->get('privacy_type', 'article'); + $privacyId = ($privacyType == 'menu_item') ? $this->getPrivacyItemId() : $this->getPrivacyArticleId(); + $privacynote = $this->params->get('privacy_note'); + + // Push the privacy article ID into the privacy field. + $form->setFieldAttribute('privacy', $privacyType, $privacyId, 'privacyconsent'); + $form->setFieldAttribute('privacy', 'note', $privacynote, 'privacyconsent'); + } + + /** + * Method is called before user data is stored in the database + * + * @param array $user Holds the old user data. + * @param boolean $isNew True if a new user is stored. + * @param array $data Holds the new user data. + * + * @return boolean + * + * @since 3.9.0 + * @throws InvalidArgumentException on missing required data. + */ + public function onUserBeforeSave($user, $isNew, $data) + { + // // Only check for front-end user creation/update profile + if ($this->getApplication()->isClient('administrator')) { + return true; + } + + $userId = ArrayHelper::getValue($user, 'id', 0, 'int'); + + // User already consented before, no need to check it further + if ($userId > 0 && $this->isUserConsented($userId)) { + return true; + } + + // Check that the privacy is checked if required ie only in registration from frontend. + $input = $this->getApplication()->getInput(); + $option = $input->get('option'); + $task = $input->post->get('task'); + $form = $input->post->get('jform', [], 'array'); + + if ( + $option == 'com_users' && in_array($task, ['registration.register', 'profile.save']) + && empty($form['privacyconsent']['privacy']) + ) { + throw new InvalidArgumentException($this->getApplication()->getLanguage()->_('PLG_SYSTEM_PRIVACYCONSENT_FIELD_ERROR')); + } + + return true; + } + + /** + * Saves user privacy confirmation + * + * @param array $data entered user data + * @param boolean $isNew true if this is a new user + * @param boolean $result true if saving the user worked + * @param string $error error message + * + * @return void + * + * @since 3.9.0 + */ + public function onUserAfterSave($data, $isNew, $result, $error): void + { + // Only create an entry on front-end user creation/update profile + if ($this->getApplication()->isClient('administrator')) { + return; + } + + // Get the user's ID + $userId = ArrayHelper::getValue($data, 'id', 0, 'int'); + + // If user already consented before, no need to check it further + if ($userId > 0 && $this->isUserConsented($userId)) { + return; + } + + $input = $this->getApplication()->getInput(); + $option = $input->get('option'); + $task = $input->post->get('task'); + $form = $input->post->get('jform', [], 'array'); + + if ( + $option == 'com_users' + && in_array($task, ['registration.register', 'profile.save']) + && !empty($form['privacyconsent']['privacy']) + ) { + $userId = ArrayHelper::getValue($data, 'id', 0, 'int'); + + // Get the user's IP address + $ip = $input->server->get('REMOTE_ADDR', '', 'string'); + + // Get the user agent string + $userAgent = $input->server->get('HTTP_USER_AGENT', '', 'string'); + + // Create the user note + $userNote = (object) [ + 'user_id' => $userId, + 'subject' => 'PLG_SYSTEM_PRIVACYCONSENT_SUBJECT', + 'body' => Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_BODY', $ip, $userAgent), + 'created' => Factory::getDate()->toSql(), + ]; + + try { + $this->getDatabase()->insertObject('#__privacy_consents', $userNote); + } catch (Exception $e) { + // Do nothing if the save fails + } + + $userId = ArrayHelper::getValue($data, 'id', 0, 'int'); + + $message = [ + 'action' => 'consent', + 'id' => $userId, + 'title' => $data['name'], + 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $userId, + 'userid' => $userId, + 'username' => $data['username'], + 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $userId, + ]; + + /** @var ActionlogModel $model */ + $model = $this->getApplication()->bootComponent('com_actionlogs')->getMVCFactory()->createModel('Actionlog', 'Administrator'); + $model->addLog([$message], 'PLG_SYSTEM_PRIVACYCONSENT_CONSENT', 'plg_system_privacyconsent', $userId); + } + } + + /** + * Remove all user privacy consent information for the given user ID + * + * Method is called after user data is deleted from the database + * + * @param array $user Holds the user data + * @param boolean $success True if user was successfully stored in the database + * @param string $msg Message + * + * @return void + * + * @since 3.9.0 + */ + public function onUserAfterDelete($user, $success, $msg): void + { + if (!$success) { + return; + } + + $userId = ArrayHelper::getValue($user, 'id', 0, 'int'); + + if ($userId) { + // Remove user's consent + $query = $this->getDatabase()->getQuery(true) + ->delete($this->getDatabase()->quoteName('#__privacy_consents')) + ->where($this->getDatabase()->quoteName('user_id') . ' = :userid') + ->bind(':userid', $userId, ParameterType::INTEGER); + $this->getDatabase()->setQuery($query); + $this->getDatabase()->execute(); + } + } + + /** + * If logged in users haven't agreed to privacy consent, redirect them to profile edit page, ask them to agree to + * privacy consent before allowing access to any other pages + * + * @return void + * + * @since 3.9.0 + */ + public function onAfterRoute() + { + // Run this in frontend only + if (!$this->getApplication()->isClient('site')) { + return; + } + + $userId = $this->getApplication()->getIdentity()->id; + + // Check to see whether user already consented, if not, redirect to user profile page + if ($userId > 0) { + // If user consented before, no need to check it further + if ($this->isUserConsented($userId)) { + return; + } + + $input = $this->getApplication()->getInput(); + $option = $input->getCmd('option'); + $task = $input->get('task', ''); + $view = $input->getString('view', ''); + $layout = $input->getString('layout', ''); + $id = $input->getInt('id'); + + $privacyArticleId = $this->getPrivacyArticleId(); + + /* + * If user is already on edit profile screen or view privacy article + * or press update/apply button, or logout, do nothing to avoid infinite redirect + */ + $allowedUserTasks = [ + 'profile.save', 'profile.apply', 'user.logout', 'user.menulogout', + 'method', 'methods', 'captive', 'callback', + ]; + $isAllowedUserTask = in_array($task, $allowedUserTasks) + || substr($task, 0, 8) === 'captive.' + || substr($task, 0, 8) === 'methods.' + || substr($task, 0, 7) === 'method.' + || substr($task, 0, 9) === 'callback.'; + + if ( + ($option == 'com_users' && $isAllowedUserTask) + || ($option == 'com_content' && $view == 'article' && $id == $privacyArticleId) + || ($option == 'com_users' && $view == 'profile' && $layout == 'edit') + ) { + return; + } + + // Redirect to com_users profile edit + $this->getApplication()->enqueueMessage($this->getRedirectMessage(), 'notice'); + $link = 'index.php?option=com_users&view=profile&layout=edit'; + $this->getApplication()->redirect(Route::_($link, false)); + } + } + + /** + * Event to specify whether a privacy policy has been published. + * + * @param array &$policy The privacy policy status data, passed by reference, with keys "published", "editLink" and "articlePublished". + * + * @return void + * + * @since 3.9.0 + */ + public function onPrivacyCheckPrivacyPolicyPublished(&$policy) + { + // If another plugin has already indicated a policy is published, we won't change anything here + if ($policy['published']) { + return; + } + + $articleId = (int) $this->params->get('privacy_article'); + + if (!$articleId) { + return; + } + + // Check if the article exists in database and is published + $query = $this->getDatabase()->getQuery(true) + ->select($this->getDatabase()->quoteName(['id', 'state'])) + ->from($this->getDatabase()->quoteName('#__content')) + ->where($this->getDatabase()->quoteName('id') . ' = :id') + ->bind(':id', $articleId, ParameterType::INTEGER); + $this->getDatabase()->setQuery($query); + + $article = $this->getDatabase()->loadObject(); + + // Check if the article exists + if (!$article) { + return; + } + + // Check if the article is published + if ($article->state == 1) { + $policy['articlePublished'] = true; + } + + $policy['published'] = true; + $policy['editLink'] = Route::_('index.php?option=com_content&task=article.edit&id=' . $articleId); + } + + /** + * Returns the configured redirect message and falls back to the default version. + * + * @return string redirect message + * + * @since 3.9.0 + */ + private function getRedirectMessage() + { + $messageOnRedirect = trim($this->params->get('messageOnRedirect', '')); + + if (empty($messageOnRedirect)) { + return $this->getApplication()->getLanguage()->_('PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT'); + } + + return $messageOnRedirect; + } + + /** + * Method to check if the given user has consented yet + * + * @param integer $userId ID of uer to check + * + * @return boolean + * + * @since 3.9.0 + */ + private function isUserConsented($userId) + { + $userId = (int) $userId; + $db = $this->getDatabase(); + $query = $db->getQuery(true); + + $query->select('COUNT(*)') + ->from($db->quoteName('#__privacy_consents')) + ->where($db->quoteName('user_id') . ' = :userid') + ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) + ->where($db->quoteName('state') . ' = 1') + ->bind(':userid', $userId, ParameterType::INTEGER); + $db->setQuery($query); + + return (int) $db->loadResult() > 0; + } + + /** + * Get privacy article ID. If the site is a multilingual website and there is associated article for the + * current language, ID of the associated article will be returned + * + * @return integer + * + * @since 3.9.0 + */ + private function getPrivacyArticleId() + { + $privacyArticleId = $this->params->get('privacy_article'); + + if ($privacyArticleId > 0 && Associations::isEnabled()) { + $privacyAssociated = Associations::getAssociations('com_content', '#__content', 'com_content.item', $privacyArticleId); + $currentLang = $this->getApplication()->getLanguage()->getTag(); + + if (isset($privacyAssociated[$currentLang])) { + $privacyArticleId = $privacyAssociated[$currentLang]->id; + } + } + + return $privacyArticleId; + } + + /** + * Get privacy menu item ID. If the site is a multilingual website and there is associated menu item for the + * current language, ID of the associated menu item will be returned. + * + * @return integer + * + * @since 4.0.0 + */ + private function getPrivacyItemId() + { + $itemId = $this->params->get('privacy_menu_item'); + + if ($itemId > 0 && Associations::isEnabled()) { + $privacyAssociated = Associations::getAssociations('com_menus', '#__menu', 'com_menus.item', $itemId, 'id', '', ''); + $currentLang = $this->getApplication()->getTag(); + + if (isset($privacyAssociated[$currentLang])) { + $itemId = $privacyAssociated[$currentLang]->id; + } + } + + return $itemId; + } + + /** + * The privacy consent expiration check code is triggered after the page has fully rendered. + * + * @return void + * + * @since 3.9.0 + */ + public function onAfterRender() + { + if (!$this->params->get('enabled', 0)) { + return; + } + + $cacheTimeout = (int) $this->params->get('cachetimeout', 30); + $cacheTimeout = 24 * 3600 * $cacheTimeout; + + // Do we need to run? Compare the last run timestamp stored in the plugin's options with the current + // timestamp. If the difference is greater than the cache timeout we shall not execute again. + $now = time(); + $last = (int) $this->params->get('lastrun', 0); + + if ((abs($now - $last) < $cacheTimeout)) { + return; + } + + // Update last run status + $this->params->set('lastrun', $now); + + $paramsJson = $this->params->toString('JSON'); + $db = $this->getDatabase(); + $query = $db->getQuery(true) + ->update($db->quoteName('#__extensions')) + ->set($db->quoteName('params') . ' = :params') + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) + ->where($db->quoteName('element') . ' = ' . $db->quote('privacyconsent')) + ->bind(':params', $paramsJson); + + try { + // Lock the tables to prevent multiple plugin executions causing a race condition + $db->lockTable('#__extensions'); + } catch (Exception $e) { + // If we can't lock the tables it's too risky to continue execution + return; + } + + try { + // Update the plugin parameters + $result = $db->setQuery($query)->execute(); + $this->clearCacheGroups(['com_plugins'], [0, 1]); + } catch (Exception $exc) { + // If we failed to execute + $db->unlockTables(); + $result = false; + } + + try { + // Unlock the tables after writing + $db->unlockTables(); + } catch (Exception $e) { + // If we can't lock the tables assume we have somehow failed + $result = false; + } + + // Abort on failure + if (!$result) { + return; + } + + // Delete the expired privacy consents + $this->invalidateExpiredConsents(); + + // Remind for privacy consents near to expire + $this->remindExpiringConsents(); + } + + /** + * Method to send the remind for privacy consents renew + * + * @return integer + * + * @since 3.9.0 + */ + private function remindExpiringConsents() + { + // Load the parameters. + $expire = (int) $this->params->get('consentexpiration', 365); + $remind = (int) $this->params->get('remind', 30); + $now = Factory::getDate()->toSql(); + $period = '-' . ($expire - $remind); + $db = $this->getDatabase(); + $query = $db->getQuery(true); + + $query->select($db->quoteName(['r.id', 'r.user_id', 'u.email'])) + ->from($db->quoteName('#__privacy_consents', 'r')) + ->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('r.user_id')) + ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) + ->where($db->quoteName('remind') . ' = 0') + ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')); + + try { + $users = $db->setQuery($query)->loadObjectList(); + } catch (ExecutionFailureException $exception) { + return false; + } + + $app = $this->getApplication(); + $linkMode = $app->get('force_ssl', 0) == 2 ? Route::TLS_FORCE : Route::TLS_IGNORE; + + foreach ($users as $user) { + $token = ApplicationHelper::getHash(UserHelper::genRandomPassword()); + $hashedToken = UserHelper::hashPassword($token); + + // The mail + try { + $templateData = [ + 'sitename' => $app->get('sitename'), + 'url' => Uri::root(), + 'tokenurl' => Route::link('site', 'index.php?option=com_privacy&view=remind&remind_token=' . $token, false, $linkMode, true), + 'formurl' => Route::link('site', 'index.php?option=com_privacy&view=remind', false, $linkMode, true), + 'token' => $token, + ]; + + $mailer = new MailTemplate('plg_system_privacyconsent.request.reminder', $app->getLanguage()->getTag()); + $mailer->addTemplateData($templateData); + $mailer->addRecipient($user->email); + + $mailResult = $mailer->send(); + + if ($mailResult === false) { + return false; + } + + $userId = (int) $user->id; + + // Update the privacy_consents item to not send the reminder again + $query->clear() + ->update($db->quoteName('#__privacy_consents')) + ->set($db->quoteName('remind') . ' = 1') + ->set($db->quoteName('token') . ' = :token') + ->where($db->quoteName('id') . ' = :userid') + ->bind(':token', $hashedToken) + ->bind(':userid', $userId, ParameterType::INTEGER); + $db->setQuery($query); + + try { + $db->execute(); + } catch (RuntimeException $e) { + return false; + } + } catch (MailDisabledException | phpmailerException $exception) { + return false; + } + } + } + + /** + * Method to delete the expired privacy consents + * + * @return boolean + * + * @since 3.9.0 + */ + private function invalidateExpiredConsents() + { + // Load the parameters. + $expire = (int) $this->params->get('consentexpiration', 365); + $now = Factory::getDate()->toSql(); + $period = '-' . $expire; + $db = $this->getDatabase(); + $query = $db->getQuery(true); + + $query->select($db->quoteName(['id', 'user_id'])) + ->from($db->quoteName('#__privacy_consents')) + ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')) + ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) + ->where($db->quoteName('state') . ' = 1'); + + $db->setQuery($query); + + try { + $users = $db->loadObjectList(); + } catch (RuntimeException $e) { + return false; + } + + // Do not process further if no expired consents found + if (empty($users)) { + return true; + } + + // Push a notification to the site's super users + /** @var MessageModel $messageModel */ + $messageModel = $this->getApplication()->bootComponent('com_messages')->getMVCFactory()->createModel('Message', 'Administrator'); + + foreach ($users as $user) { + $userId = (int) $user->id; + $query = $db->getQuery(true) + ->update($db->quoteName('#__privacy_consents')) + ->set($db->quoteName('state') . ' = 0') + ->where($db->quoteName('id') . ' = :userid') + ->bind(':userid', $userId, ParameterType::INTEGER); + $db->setQuery($query); + + try { + $db->execute(); + } catch (RuntimeException $e) { + return false; + } + + $messageModel->notifySuperUsers( + $this->getApplication()->getLanguage()->_('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT'), + Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE', Factory::getUser($user->user_id)->username) + ); + } + + return true; + } + /** + * Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp. + * + * @param array $clearGroups The cache groups to clean + * @param array $cacheClients The cache clients (site, admin) to clean + * + * @return void + * + * @since 3.9.0 + */ + private function clearCacheGroups(array $clearGroups, array $cacheClients = [0, 1]) + { + foreach ($clearGroups as $group) { + foreach ($cacheClients as $client_id) { + try { + $options = [ + 'defaultgroup' => $group, + 'cachebase' => $client_id ? JPATH_ADMINISTRATOR . '/cache' : + $this->getApplication()->get('cache_path', JPATH_SITE . '/cache'), + ]; + + $cache = Cache::getInstance('callback', $options); + $cache->clean(); + } catch (Exception $e) { + // Ignore it + } + } + } + } +} diff --git a/plugins/system/privacyconsent/src/Field/PrivacyField.php b/plugins/system/privacyconsent/src/Field/PrivacyField.php new file mode 100644 index 0000000000000..980fa37b5ced1 --- /dev/null +++ b/plugins/system/privacyconsent/src/Field/PrivacyField.php @@ -0,0 +1,133 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Plugin\System\PrivacyConsent\Field; + +use Joomla\CMS\Factory; +use Joomla\CMS\Form\Field\RadioField; +use Joomla\CMS\Language\Multilanguage; +use Joomla\CMS\Language\Text; +use Joomla\Component\Content\Site\Helper\RouteHelper; +use Joomla\Database\ParameterType; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Provides input for privacy + * + * @since 3.9.0 + */ +class PrivacyField extends RadioField +{ + /** + * The form field type. + * + * @var string + * @since 3.9.0 + */ + protected $type = 'privacy'; + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 3.9.0 + */ + protected function getInput() + { + // Display the message before the field + echo $this->getRenderer('plugins.system.privacyconsent.message')->render($this->getLayoutData()); + + return parent::getInput(); + } + + /** + * Method to get the field label markup. + * + * @return string The field label markup. + * + * @since 3.9.0 + */ + protected function getLabel() + { + if ($this->hidden) { + return ''; + } + + return $this->getRenderer('plugins.system.privacyconsent.label')->render($this->getLayoutData()); + } + + /** + * Method to get the data to be passed to the layout for rendering. + * + * @return array + * + * @since 3.9.4 + */ + protected function getLayoutData() + { + $data = parent::getLayoutData(); + + $article = false; + $link = false; + $privacyArticle = $this->element['article'] > 0 ? (int) $this->element['article'] : 0; + + if ($privacyArticle && Factory::getApplication()->isClient('site')) { + $db = $this->getDatabase(); + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'alias', 'catid', 'language'])) + ->from($db->quoteName('#__content')) + ->where($db->quoteName('id') . ' = :id') + ->bind(':id', $privacyArticle, ParameterType::INTEGER); + $db->setQuery($query); + $article = $db->loadObject(); + + $slug = $article->alias ? ($article->id . ':' . $article->alias) : $article->id; + $article->link = RouteHelper::getArticleRoute($slug, $article->catid, $article->language); + $link = $article->link; + } + + $privacyMenuItem = $this->element['menu_item'] > 0 ? (int) $this->element['menu_item'] : 0; + + if ($privacyMenuItem && Factory::getApplication()->isClient('site')) { + $link = 'index.php?Itemid=' . $privacyMenuItem; + + if (Multilanguage::isEnabled()) { + $db = $this->getDatabase(); + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'language'])) + ->from($db->quoteName('#__menu')) + ->where($db->quoteName('id') . ' = :id') + ->bind(':id', $privacyMenuItem, ParameterType::INTEGER); + $db->setQuery($query); + $menuItem = $db->loadObject(); + + $link .= '&lang=' . $menuItem->language; + } + } + + $extraData = [ + 'privacynote' => !empty($this->element['note']) ? $this->element['note'] : Text::_('PLG_SYSTEM_PRIVACYCONSENT_NOTE_FIELD_DEFAULT'), + 'options' => $this->getOptions(), + 'value' => (string) $this->value, + 'translateLabel' => $this->translateLabel, + 'translateDescription' => $this->translateDescription, + 'translateHint' => $this->translateHint, + 'privacyArticle' => $privacyArticle, + 'article' => $article, + 'privacyLink' => $link, + ]; + + return array_merge($data, $extraData); + } +} From e87782c7c705abec59ea5f6b7b642a2a184f9815 Mon Sep 17 00:00:00 2001 From: alikon Date: Tue, 13 Jun 2023 12:49:32 +0200 Subject: [PATCH 14/59] consentexpirationdays --- administrator/language/en-GB/plg_task_privacyconsent.ini | 4 ++-- plugins/task/privacyconsent/forms/invalidateForm.xml | 6 +++--- plugins/task/privacyconsent/forms/remindForm.xml | 6 +++--- .../task/privacyconsent/src/Extension/PrivacyConsent.php | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/administrator/language/en-GB/plg_task_privacyconsent.ini b/administrator/language/en-GB/plg_task_privacyconsent.ini index c0b23c2d1e02f..5baf97d04fae5 100644 --- a/administrator/language/en-GB/plg_task_privacyconsent.ini +++ b/administrator/language/en-GB/plg_task_privacyconsent.ini @@ -4,8 +4,8 @@ ; Note : All ini files need to be saved as UTF-8 PLG_TASK_PRIVACYCONSENT="Task - Privacy Consents" -PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATION_DESC="Number of days after which the privacy consent shall expire." -PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATION_LABEL="Expiration" +PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATIONDAYS_DESC="Number of days after which the privacy consent shall expire." +PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATIONDAYS_LABEL="Expiration" PLG_TASK_PRIVACYCONSENT_INVALIDATE_TITLE ="Expiration of privacy consents" PLG_TASK_PRIVACYCONSENT_INVALIDATE_DESC ="Manage the expiration of privacy consents" PLG_TASK_PRIVACYCONSENT_REMIND_DESC="Manage the remind of expiration of privacy consents" diff --git a/plugins/task/privacyconsent/forms/invalidateForm.xml b/plugins/task/privacyconsent/forms/invalidateForm.xml index 84f6ace055865..50d52303c1cd1 100644 --- a/plugins/task/privacyconsent/forms/invalidateForm.xml +++ b/plugins/task/privacyconsent/forms/invalidateForm.xml @@ -3,10 +3,10 @@
getArgument('params')->consentexpiration ?? 365; + $expire = (int) $event->getArgument('params')->consentexpirationdays ?? 365; $remind = (int) $event->getArgument('params')->remind ?? 30; $now = Factory::getDate()->toSql(); @@ -187,7 +187,7 @@ private function remindExpiringConsents(ExecuteTaskEvent $event): int */ private function invalidateExpiredConsents(ExecuteTaskEvent $event): int { - $expire = (int) $event->getArgument('params')->consentexpiration ?? 365; + $expire = (int) $event->getArgument('params')->consentexpirationdays ?? 365; $now = Factory::getDate()->toSql(); $period = '-' . $expire; $db = $this->getDatabase(); From 3479301acc8bb7209ab54c017caf36e25d78df6d Mon Sep 17 00:00:00 2001 From: alikon Date: Tue, 13 Jun 2023 12:53:59 +0200 Subject: [PATCH 15/59] re-add system privacyconsent --- installation/sql/mysql/base.sql | 1 + installation/sql/postgresql/base.sql | 1 + 2 files changed, 2 insertions(+) diff --git a/installation/sql/mysql/base.sql b/installation/sql/mysql/base.sql index 6a15feadc2cc6..e26c2e0191bca 100644 --- a/installation/sql/mysql/base.sql +++ b/installation/sql/mysql/base.sql @@ -344,6 +344,7 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_system_log', 'plugin', 'log', 'system', 0, 1, 1, 0, 1, '', '', '', 11, 0), (0, 'plg_system_logout', 'plugin', 'logout', 'system', 0, 1, 1, 0, 1, '', '', '', 12, 0), (0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), +(0, 'plg_system_privacyconsent', 'plugin', 'privacyconsent', 'system', 0, 0, 1, 0, 1, '', '{}', '', 14, 0), (0, 'plg_system_redirect', 'plugin', 'redirect', 'system', 0, 0, 1, 0, 1, '', '', '', 15, 0), (0, 'plg_system_remember', 'plugin', 'remember', 'system', 0, 1, 1, 0, 1, '', '', '', 16, 0), (0, 'plg_system_schedulerunner', 'plugin', 'schedulerunner', 'system', 0, 1, 1, 0, 1, '', '{}', '', 17, 0), diff --git a/installation/sql/postgresql/base.sql b/installation/sql/postgresql/base.sql index 1b61ffca41261..e92fbac10f3e4 100644 --- a/installation/sql/postgresql/base.sql +++ b/installation/sql/postgresql/base.sql @@ -350,6 +350,7 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_system_log', 'plugin', 'log', 'system', 0, 1, 1, 0, 1, '', '', '', 11, 0), (0, 'plg_system_logout', 'plugin', 'logout', 'system', 0, 1, 1, 0, 1, '', '', '', 12, 0), (0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), +(0, 'plg_system_privacyconsent', 'plugin', 'privacyconsent', 'system', 0, 0, 1, 0, 1, '', '{}', '', 14, 0), (0, 'plg_system_redirect', 'plugin', 'redirect', 'system', 0, 0, 1, 0, 1, '', '', '', 15, 0), (0, 'plg_system_remember', 'plugin', 'remember', 'system', 0, 1, 1, 0, 1, '', '', '', 16, 0), (0, 'plg_system_schedulerunner', 'plugin', 'schedulerunner', 'system', 0, 1, 1, 0, 1, '', '{}', '', 17, 0), From 84c2b07c9f8a1b4719fec9f8056b9e578b794a7a Mon Sep 17 00:00:00 2001 From: alikon Date: Tue, 13 Jun 2023 14:07:04 +0200 Subject: [PATCH 16/59] move 2 task --- .../src/Extension/PrivacyConsent.php | 257 ------------------ .../src/Extension/PrivacyConsent.php | 2 +- 2 files changed, 1 insertion(+), 258 deletions(-) diff --git a/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php index 0efffb6b201f4..21a9a7010cf1b 100644 --- a/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php @@ -12,26 +12,17 @@ use Exception; use InvalidArgumentException; -use Joomla\CMS\Application\ApplicationHelper; -use Joomla\CMS\Cache\Cache; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormHelper; use Joomla\CMS\Language\Associations; use Joomla\CMS\Language\Text; -use Joomla\CMS\Mail\Exception\MailDisabledException; -use Joomla\CMS\Mail\MailTemplate; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Router\Route; -use Joomla\CMS\Uri\Uri; -use Joomla\CMS\User\UserHelper; use Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel; -use Joomla\Component\Messages\Administrator\Model\MessageModel; use Joomla\Database\DatabaseAwareTrait; -use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; -use PHPMailer\PHPMailer\Exception as phpmailerException; use RuntimeException; // phpcs:disable PSR1.Files.SideEffects @@ -447,252 +438,4 @@ private function getPrivacyItemId() return $itemId; } - - /** - * The privacy consent expiration check code is triggered after the page has fully rendered. - * - * @return void - * - * @since 3.9.0 - */ - public function onAfterRender() - { - if (!$this->params->get('enabled', 0)) { - return; - } - - $cacheTimeout = (int) $this->params->get('cachetimeout', 30); - $cacheTimeout = 24 * 3600 * $cacheTimeout; - - // Do we need to run? Compare the last run timestamp stored in the plugin's options with the current - // timestamp. If the difference is greater than the cache timeout we shall not execute again. - $now = time(); - $last = (int) $this->params->get('lastrun', 0); - - if ((abs($now - $last) < $cacheTimeout)) { - return; - } - - // Update last run status - $this->params->set('lastrun', $now); - - $paramsJson = $this->params->toString('JSON'); - $db = $this->getDatabase(); - $query = $db->getQuery(true) - ->update($db->quoteName('#__extensions')) - ->set($db->quoteName('params') . ' = :params') - ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) - ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) - ->where($db->quoteName('element') . ' = ' . $db->quote('privacyconsent')) - ->bind(':params', $paramsJson); - - try { - // Lock the tables to prevent multiple plugin executions causing a race condition - $db->lockTable('#__extensions'); - } catch (Exception $e) { - // If we can't lock the tables it's too risky to continue execution - return; - } - - try { - // Update the plugin parameters - $result = $db->setQuery($query)->execute(); - $this->clearCacheGroups(['com_plugins'], [0, 1]); - } catch (Exception $exc) { - // If we failed to execute - $db->unlockTables(); - $result = false; - } - - try { - // Unlock the tables after writing - $db->unlockTables(); - } catch (Exception $e) { - // If we can't lock the tables assume we have somehow failed - $result = false; - } - - // Abort on failure - if (!$result) { - return; - } - - // Delete the expired privacy consents - $this->invalidateExpiredConsents(); - - // Remind for privacy consents near to expire - $this->remindExpiringConsents(); - } - - /** - * Method to send the remind for privacy consents renew - * - * @return integer - * - * @since 3.9.0 - */ - private function remindExpiringConsents() - { - // Load the parameters. - $expire = (int) $this->params->get('consentexpiration', 365); - $remind = (int) $this->params->get('remind', 30); - $now = Factory::getDate()->toSql(); - $period = '-' . ($expire - $remind); - $db = $this->getDatabase(); - $query = $db->getQuery(true); - - $query->select($db->quoteName(['r.id', 'r.user_id', 'u.email'])) - ->from($db->quoteName('#__privacy_consents', 'r')) - ->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('r.user_id')) - ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) - ->where($db->quoteName('remind') . ' = 0') - ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')); - - try { - $users = $db->setQuery($query)->loadObjectList(); - } catch (ExecutionFailureException $exception) { - return false; - } - - $app = $this->getApplication(); - $linkMode = $app->get('force_ssl', 0) == 2 ? Route::TLS_FORCE : Route::TLS_IGNORE; - - foreach ($users as $user) { - $token = ApplicationHelper::getHash(UserHelper::genRandomPassword()); - $hashedToken = UserHelper::hashPassword($token); - - // The mail - try { - $templateData = [ - 'sitename' => $app->get('sitename'), - 'url' => Uri::root(), - 'tokenurl' => Route::link('site', 'index.php?option=com_privacy&view=remind&remind_token=' . $token, false, $linkMode, true), - 'formurl' => Route::link('site', 'index.php?option=com_privacy&view=remind', false, $linkMode, true), - 'token' => $token, - ]; - - $mailer = new MailTemplate('plg_system_privacyconsent.request.reminder', $app->getLanguage()->getTag()); - $mailer->addTemplateData($templateData); - $mailer->addRecipient($user->email); - - $mailResult = $mailer->send(); - - if ($mailResult === false) { - return false; - } - - $userId = (int) $user->id; - - // Update the privacy_consents item to not send the reminder again - $query->clear() - ->update($db->quoteName('#__privacy_consents')) - ->set($db->quoteName('remind') . ' = 1') - ->set($db->quoteName('token') . ' = :token') - ->where($db->quoteName('id') . ' = :userid') - ->bind(':token', $hashedToken) - ->bind(':userid', $userId, ParameterType::INTEGER); - $db->setQuery($query); - - try { - $db->execute(); - } catch (RuntimeException $e) { - return false; - } - } catch (MailDisabledException | phpmailerException $exception) { - return false; - } - } - } - - /** - * Method to delete the expired privacy consents - * - * @return boolean - * - * @since 3.9.0 - */ - private function invalidateExpiredConsents() - { - // Load the parameters. - $expire = (int) $this->params->get('consentexpiration', 365); - $now = Factory::getDate()->toSql(); - $period = '-' . $expire; - $db = $this->getDatabase(); - $query = $db->getQuery(true); - - $query->select($db->quoteName(['id', 'user_id'])) - ->from($db->quoteName('#__privacy_consents')) - ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')) - ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) - ->where($db->quoteName('state') . ' = 1'); - - $db->setQuery($query); - - try { - $users = $db->loadObjectList(); - } catch (RuntimeException $e) { - return false; - } - - // Do not process further if no expired consents found - if (empty($users)) { - return true; - } - - // Push a notification to the site's super users - /** @var MessageModel $messageModel */ - $messageModel = $this->getApplication()->bootComponent('com_messages')->getMVCFactory()->createModel('Message', 'Administrator'); - - foreach ($users as $user) { - $userId = (int) $user->id; - $query = $db->getQuery(true) - ->update($db->quoteName('#__privacy_consents')) - ->set($db->quoteName('state') . ' = 0') - ->where($db->quoteName('id') . ' = :userid') - ->bind(':userid', $userId, ParameterType::INTEGER); - $db->setQuery($query); - - try { - $db->execute(); - } catch (RuntimeException $e) { - return false; - } - - $messageModel->notifySuperUsers( - $this->getApplication()->getLanguage()->_('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT'), - Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE', Factory::getUser($user->user_id)->username) - ); - } - - return true; - } - /** - * Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp. - * - * @param array $clearGroups The cache groups to clean - * @param array $cacheClients The cache clients (site, admin) to clean - * - * @return void - * - * @since 3.9.0 - */ - private function clearCacheGroups(array $clearGroups, array $cacheClients = [0, 1]) - { - foreach ($clearGroups as $group) { - foreach ($cacheClients as $client_id) { - try { - $options = [ - 'defaultgroup' => $group, - 'cachebase' => $client_id ? JPATH_ADMINISTRATOR . '/cache' : - $this->getApplication()->get('cache_path', JPATH_SITE . '/cache'), - ]; - - $cache = Cache::getInstance('callback', $options); - $cache->clean(); - } catch (Exception $e) { - // Ignore it - } - } - } - } } diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index a4da776627c7b..b98e316d32c91 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -2,7 +2,7 @@ /** * @package Joomla.Plugin - * @subpackage Task.PrivacyConsent + * @subpackage Task.privacyconsent * * @copyright (C) 2023 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt From 6543c1432c7ffa4b75a1fb21d23cdeebc0ec3565 Mon Sep 17 00:00:00 2001 From: alikon Date: Tue, 13 Jun 2023 14:24:06 +0200 Subject: [PATCH 17/59] update & core plugin --- .../components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql | 2 ++ .../com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql | 2 ++ libraries/src/Extension/ExtensionHelper.php | 1 + 3 files changed, 5 insertions(+) create mode 100644 administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql create mode 100644 administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql new file mode 100644 index 0000000000000..000a6015c17da --- /dev/null +++ b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql @@ -0,0 +1,2 @@ +INSERT INTO `#__extensions` (`name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES +('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); \ No newline at end of file diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql new file mode 100644 index 0000000000000..4fc340a838c56 --- /dev/null +++ b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql @@ -0,0 +1,2 @@ +INSERT INTO "#__extensions" ("name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "custom_data", "checked_out", "checked_out_time", "ordering", "state") VALUES +('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); \ No newline at end of file diff --git a/libraries/src/Extension/ExtensionHelper.php b/libraries/src/Extension/ExtensionHelper.php index 9677624181e2c..836bb66d1be54 100644 --- a/libraries/src/Extension/ExtensionHelper.php +++ b/libraries/src/Extension/ExtensionHelper.php @@ -312,6 +312,7 @@ class ExtensionHelper ['plugin', 'demotasks', 'task', 0], ['plugin', 'requests', 'task', 0], ['plugin', 'sitestatus', 'task', 0], + ['plugin', 'privacyconsent', 'task', 0], // Core plugin extensions - user ['plugin', 'contactcreator', 'user', 0], From 4e7318b14ad6c1cc43bf3d58ec552c9dfd18e7c4 Mon Sep 17 00:00:00 2001 From: alikon Date: Tue, 13 Jun 2023 15:45:27 +0200 Subject: [PATCH 18/59] nomore expiration fieldset --- .../system/privacyconsent/privacyconsent.xml | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/plugins/system/privacyconsent/privacyconsent.xml b/plugins/system/privacyconsent/privacyconsent.xml index 9f7d31f020823..6c47d949ef517 100644 --- a/plugins/system/privacyconsent/privacyconsent.xml +++ b/plugins/system/privacyconsent/privacyconsent.xml @@ -78,65 +78,6 @@ filter="html" />
-
- - - - - - - - -
From d3784e9a4d965c68bb7b829586db58c0d99a2aba Mon Sep 17 00:00:00 2001 From: alikon Date: Sat, 24 Jun 2023 09:40:16 +0200 Subject: [PATCH 19/59] redesign --- .../privacyconsent/forms/invalidateForm.xml | 19 ------- ...{remindForm.xml => privacyconsentForm.xml} | 10 ++-- .../src/Extension/PrivacyConsent.php | 54 ++++++++++++------- 3 files changed, 40 insertions(+), 43 deletions(-) delete mode 100644 plugins/task/privacyconsent/forms/invalidateForm.xml rename plugins/task/privacyconsent/forms/{remindForm.xml => privacyconsentForm.xml} (72%) diff --git a/plugins/task/privacyconsent/forms/invalidateForm.xml b/plugins/task/privacyconsent/forms/invalidateForm.xml deleted file mode 100644 index 50d52303c1cd1..0000000000000 --- a/plugins/task/privacyconsent/forms/invalidateForm.xml +++ /dev/null @@ -1,19 +0,0 @@ - -
- -
- -
-
-
diff --git a/plugins/task/privacyconsent/forms/remindForm.xml b/plugins/task/privacyconsent/forms/privacyconsentForm.xml similarity index 72% rename from plugins/task/privacyconsent/forms/remindForm.xml rename to plugins/task/privacyconsent/forms/privacyconsentForm.xml index 389fb039c1295..0573b30d4c7c2 100644 --- a/plugins/task/privacyconsent/forms/remindForm.xml +++ b/plugins/task/privacyconsent/forms/privacyconsentForm.xml @@ -3,11 +3,11 @@
* @license GNU General Public License version 2 or later; see LICENSE.txt @@ -48,21 +48,16 @@ final class PrivacyConsent extends CMSPlugin implements SubscriberInterface * @since __DEPLOY_VERSION__ */ private const TASKS_MAP = [ - 'invalidate.expired' => [ + 'privacy.consent' => [ 'langConstPrefix' => 'PLG_TASK_PRIVACYCONSENT_INVALIDATE', - 'method' => 'invalidateExpiredConsents', - 'form' => 'invalidateForm', - ], - 'remind.expired' => [ - 'langConstPrefix' => 'PLG_TASK_PRIVACYCONSENT_REMIND', - 'method' => 'remindExpiringConsents', - 'form' => 'remindForm', + 'method' => 'privacyConsents', + 'form' => 'privacyconsentForm', ], ]; /** * @var boolean - + * * @since __DEPLOY_VERSION__ */ protected $autoloadLanguage = true; @@ -85,7 +80,7 @@ public static function getSubscribedEvents(): array /** * Method to send the remind for privacy consents renew. - * + * * @param ExecuteTaskEvent $event The `onExecuteTask` event. * * @return integer The routine exit code. @@ -93,14 +88,36 @@ public static function getSubscribedEvents(): array * @since __DEPLOY_VERSION__ * @throws \Exception */ - private function remindExpiringConsents(ExecuteTaskEvent $event): int + private function privacyConsents(ExecuteTaskEvent $event): int { // Load the parameters. - $expire = (int) $event->getArgument('params')->consentexpirationdays ?? 365; + $expire = (int) $event->getArgument('params')->consentexpiration ?? 365; $remind = (int) $event->getArgument('params')->remind ?? 30; + if (($this->invalidateExpiredConsents($expire) === Status::OK) && + ($this->remindExpiringConsents($expire, $remind) === Status::OK)) { + + return Status::OK; + } + + return Status::KNOCKOUT; + } + + /** + * Method to send the remind for privacy consents renew. + * + * @param integer $expire + * @param integer $remind + * + * @return integer The routine exit code. + * + * @since __DEPLOY_VERSION__ + * @throws \Exception + */ + private function remindExpiringConsents($expire, $remind): int + { $now = Factory::getDate()->toSql(); - $period = '-' . ($expire - $remind); + $period = '-' . ($expire - $remind); $db = $this->getDatabase(); $query = $db->getQuery(true); @@ -178,16 +195,15 @@ private function remindExpiringConsents(ExecuteTaskEvent $event): int /** * Method to delete the expired privacy consents. * - * @param ExecuteTaskEvent $event The `onExecuteTask` event. + * @param integer $expire * * @return integer The routine exit code. * * @since __DEPLOY_VERSION__ * @throws \Exception */ - private function invalidateExpiredConsents(ExecuteTaskEvent $event): int + private function invalidateExpiredConsents($expire): int { - $expire = (int) $event->getArgument('params')->consentexpirationdays ?? 365; $now = Factory::getDate()->toSql(); $period = '-' . $expire; $db = $this->getDatabase(); @@ -207,8 +223,8 @@ private function invalidateExpiredConsents(ExecuteTaskEvent $event): int return Status::KNOCKOUT; } - // Do not process further if no expired consents found - if (empty($users)) { + // Do not process further if no expired consents found + if (empty($users)) { return Status::OK; } From 445aa2c485e65a304f6c087036cd6b35a0dd5262 Mon Sep 17 00:00:00 2001 From: alikon Date: Sat, 24 Jun 2023 10:22:19 +0200 Subject: [PATCH 20/59] mail --- .../com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql | 4 +++- .../com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql | 4 +++- .../task/privacyconsent/src/Extension/PrivacyConsent.php | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql index 000a6015c17da..0f1572267d513 100644 --- a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql +++ b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql @@ -1,2 +1,4 @@ INSERT INTO `#__extensions` (`name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES -('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); \ No newline at end of file +('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); +INSERT INTO `#__mail_templates` (`template_id`, `extension`, `language`, `subject`, `body`, `htmlbody`, `attachments`, `params`) VALUES +('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); \ No newline at end of file diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql index 4fc340a838c56..cc07c9e7ecf1e 100644 --- a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql +++ b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql @@ -1,2 +1,4 @@ INSERT INTO "#__extensions" ("name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "custom_data", "checked_out", "checked_out_time", "ordering", "state") VALUES -('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); \ No newline at end of file +('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); +INSERT INTO "#__mail_templates" ("template_id", "language", "subject", "body", "htmlbody", "attachments", "params") VALUES +('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); \ No newline at end of file diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 36351deb5a96c..bd6d2b32120e3 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -124,7 +124,7 @@ private function remindExpiringConsents($expire, $remind): int $query->select($db->quoteName(['r.id', 'r.user_id', 'u.email'])) ->from($db->quoteName('#__privacy_consents', 'r')) ->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('r.user_id')) - ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) + ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_TASK_PRIVACYCONSENT_SUBJECT')) ->where($db->quoteName('remind') . ' = 0') ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')); @@ -156,7 +156,7 @@ private function remindExpiringConsents($expire, $remind): int 'token' => $token, ]; - $mailer = new MailTemplate('plg_system_privacyconsent.request.reminder', $app->getLanguage()->getTag()); + $mailer = new MailTemplate('plg_task_privacyconsent.request.reminder', $app->getLanguage()->getTag()); $mailer->addTemplateData($templateData); $mailer->addRecipient($user->email); @@ -212,7 +212,7 @@ private function invalidateExpiredConsents($expire): int $query->select($db->quoteName(['id', 'user_id'])) ->from($db->quoteName('#__privacy_consents')) ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created')) - ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT')) + ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_TASK_PRIVACYCONSENT_SUBJECT')) ->where($db->quoteName('state') . ' = 1'); $db->setQuery($query); From 52517e0580acc04d270ca2cdb5d1f1e40fca30dc Mon Sep 17 00:00:00 2001 From: alikon Date: Sun, 25 Jun 2023 07:59:55 +0200 Subject: [PATCH 21/59] mail_templates --- installation/sql/mysql/supports.sql | 2 +- installation/sql/postgresql/supports.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/installation/sql/mysql/supports.sql b/installation/sql/mysql/supports.sql index 034682c5a2bfc..6247d1f457808 100644 --- a/installation/sql/mysql/supports.sql +++ b/installation/sql/mysql/supports.sql @@ -433,7 +433,7 @@ INSERT INTO `#__mail_templates` (`template_id`, `extension`, `language`, `subjec ('com_users.registration.admin.new_notification', 'com_users', '', 'COM_USERS_EMAIL_ACCOUNT_DETAILS', 'COM_USERS_EMAIL_REGISTERED_NOTIFICATION_TO_ADMIN_BODY', '', '', '{"tags":["name","sitename","siteurl","username"]}'), ('com_users.registration.user.admin_activated', 'com_users', '', 'COM_USERS_EMAIL_ACTIVATED_BY_ADMIN_ACTIVATION_SUBJECT', 'COM_USERS_EMAIL_ACTIVATED_BY_ADMIN_ACTIVATION_BODY', '', '', '{"tags":["name","sitename","siteurl","username"]}'), ('com_users.registration.admin.verification_request', 'com_users', '', 'COM_USERS_EMAIL_ACTIVATE_WITH_ADMIN_ACTIVATION_SUBJECT', 'COM_USERS_EMAIL_ACTIVATE_WITH_ADMIN_ACTIVATION_BODY', '', '', '{"tags":["name","sitename","email","username","activate"]}'), -('plg_system_privacyconsent.request.reminder', 'plg_system_privacyconsent', '', 'PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), +('plg_task_privacyconsent.request.reminder', 'plg_task_privacyconsent', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), ('com_messages.new_message', 'com_messages', '', 'COM_MESSAGES_NEW_MESSAGE', 'COM_MESSAGES_NEW_MESSAGE_BODY', '', '', '{"tags":["subject","message","fromname","sitename","siteurl","fromemail","toname","toemail"]}'), ('plg_system_tasknotification.failure_mail', 'plg_system_tasknotification', '', 'PLG_SYSTEM_TASK_NOTIFICATION_FAILURE_MAIL_SUBJECT', 'PLG_SYSTEM_TASK_NOTIFICATION_FAILURE_MAIL_BODY', '', '', '{"tags": ["task_id", "task_title", "exit_code", "exec_data_time", "task_output"]}'), ('plg_system_tasknotification.fatal_recovery_mail', 'plg_system_tasknotification', '', 'PLG_SYSTEM_TASK_NOTIFICATION_FATAL_MAIL_SUBJECT', 'PLG_SYSTEM_TASK_NOTIFICATION_FATAL_MAIL_BODY', '', '', '{"tags": ["task_id", "task_title"]}'), diff --git a/installation/sql/postgresql/supports.sql b/installation/sql/postgresql/supports.sql index 8a00c79fff6c7..6e9390e9eff4e 100644 --- a/installation/sql/postgresql/supports.sql +++ b/installation/sql/postgresql/supports.sql @@ -444,7 +444,7 @@ INSERT INTO "#__mail_templates" ("template_id", "extension", "language", "subjec ('com_users.registration.admin.new_notification', 'com_users', '', 'COM_USERS_EMAIL_ACCOUNT_DETAILS', 'COM_USERS_EMAIL_REGISTERED_NOTIFICATION_TO_ADMIN_BODY', '', '', '{"tags":["name","sitename","siteurl","username"]}'), ('com_users.registration.user.admin_activated', 'com_users', '', 'COM_USERS_EMAIL_ACTIVATED_BY_ADMIN_ACTIVATION_SUBJECT', 'COM_USERS_EMAIL_ACTIVATED_BY_ADMIN_ACTIVATION_BODY', '', '', '{"tags":["name","sitename","siteurl","username"]}'), ('com_users.registration.admin.verification_request', 'com_users', '', 'COM_USERS_EMAIL_ACTIVATE_WITH_ADMIN_ACTIVATION_SUBJECT', 'COM_USERS_EMAIL_ACTIVATE_WITH_ADMIN_ACTIVATION_BODY', '', '', '{"tags":["name","sitename","email","username","activate"]}'), -('plg_system_privacyconsent.request.reminder', 'plg_system_privacyconsent', '', 'PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), +('plg_task_privacyconsent.request.reminder', 'plg_task_privacyconsent', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), ('com_messages.new_message', 'com_messages', '', 'COM_MESSAGES_NEW_MESSAGE', 'COM_MESSAGES_NEW_MESSAGE_BODY', '', '', '{"tags":["subject","message","fromname","sitename","siteurl","fromemail","toname","toemail"]}'), ('plg_system_tasknotification.failure_mail', 'plg_system_tasknotification', '', 'PLG_SYSTEM_TASK_NOTIFICATION_FAILURE_MAIL_SUBJECT', 'PLG_SYSTEM_TASK_NOTIFICATION_FAILURE_MAIL_BODY', '', '', '{"tags": ["task_id", "task_title", "exit_code", "exec_data_time", "task_output"]}'), ('plg_system_tasknotification.fatal_recovery_mail', 'plg_system_tasknotification', '', 'PLG_SYSTEM_TASK_NOTIFICATION_FATAL_MAIL_SUBJECT', 'PLG_SYSTEM_TASK_NOTIFICATION_FATAL_MAIL_BODY', '', '', '{"tags": ["task_id", "task_title"]}'), From 24eb7611db1059e16d07b2acf0884537ef92e0fe Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 10:22:01 +0200 Subject: [PATCH 22/59] Update libraries/src/Extension/ExtensionHelper.php Co-authored-by: Richard Fath --- libraries/src/Extension/ExtensionHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/src/Extension/ExtensionHelper.php b/libraries/src/Extension/ExtensionHelper.php index 634c77e0c7d6a..fc62e0b06ed40 100644 --- a/libraries/src/Extension/ExtensionHelper.php +++ b/libraries/src/Extension/ExtensionHelper.php @@ -309,9 +309,9 @@ class ExtensionHelper // Core plugin extensions - task scheduler ['plugin', 'checkfiles', 'task', 0], + ['plugin', 'privacyconsent', 'task', 0], ['plugin', 'requests', 'task', 0], ['plugin', 'sitestatus', 'task', 0], - ['plugin', 'privacyconsent', 'task', 0], // Core plugin extensions - user ['plugin', 'contactcreator', 'user', 0], From 765dc0444abdc446ffa7937f5cc54befd0e2cad9 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 12:29:18 +0200 Subject: [PATCH 23/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index bd6d2b32120e3..8d13fe2712deb 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -57,7 +57,7 @@ final class PrivacyConsent extends CMSPlugin implements SubscriberInterface /** * @var boolean - * + * * @since __DEPLOY_VERSION__ */ protected $autoloadLanguage = true; From c46c0b5c796529337771d1fd5d338b6496e15d9a Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 12:29:31 +0200 Subject: [PATCH 24/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 8d13fe2712deb..43c896c55327f 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -80,7 +80,7 @@ public static function getSubscribedEvents(): array /** * Method to send the remind for privacy consents renew. - * + * * @param ExecuteTaskEvent $event The `onExecuteTask` event. * * @return integer The routine exit code. From 084212065b1f6df10adb144282df03a39bb25162 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 12:29:53 +0200 Subject: [PATCH 25/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 43c896c55327f..059b2068e5a69 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -96,7 +96,6 @@ private function privacyConsents(ExecuteTaskEvent $event): int if (($this->invalidateExpiredConsents($expire) === Status::OK) && ($this->remindExpiringConsents($expire, $remind) === Status::OK)) { - return Status::OK; } From 1fd0c2fc092b08e00edc5a139b2e0a3ee01483d2 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 12:30:20 +0200 Subject: [PATCH 26/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 059b2068e5a69..75c3f02987895 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -94,7 +94,7 @@ private function privacyConsents(ExecuteTaskEvent $event): int $expire = (int) $event->getArgument('params')->consentexpiration ?? 365; $remind = (int) $event->getArgument('params')->remind ?? 30; - if (($this->invalidateExpiredConsents($expire) === Status::OK) && + if (($this->invalidateExpiredConsents($expire) === Status::OK) && ($this->remindExpiringConsents($expire, $remind) === Status::OK)) { return Status::OK; } From c4e744dcaecce0299d9233e1a6979fe6a76af356 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 12:30:32 +0200 Subject: [PATCH 27/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- .../task/privacyconsent/src/Extension/PrivacyConsent.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 75c3f02987895..b37c7abfd877c 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -115,10 +115,10 @@ private function privacyConsents(ExecuteTaskEvent $event): int */ private function remindExpiringConsents($expire, $remind): int { - $now = Factory::getDate()->toSql(); + $now = Factory::getDate()->toSql(); $period = '-' . ($expire - $remind); - $db = $this->getDatabase(); - $query = $db->getQuery(true); + $db = $this->getDatabase(); + $query = $db->getQuery(true); $query->select($db->quoteName(['r.id', 'r.user_id', 'u.email'])) ->from($db->quoteName('#__privacy_consents', 'r')) From dd31eff21af3b59fe4053ea91aa5b1ed20de77c5 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 12:30:45 +0200 Subject: [PATCH 28/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index b37c7abfd877c..8a090b2ebfd1f 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -222,8 +222,8 @@ private function invalidateExpiredConsents($expire): int return Status::KNOCKOUT; } - // Do not process further if no expired consents found - if (empty($users)) { + // Do not process further if no expired consents found + if (empty($users)) { return Status::OK; } From 78277b1ce60fcaefbe8ef15d17ce7fc3e43873c8 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 25 Jun 2023 12:30:59 +0200 Subject: [PATCH 29/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 8a090b2ebfd1f..48f0ac67d0b43 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -104,7 +104,7 @@ private function privacyConsents(ExecuteTaskEvent $event): int /** * Method to send the remind for privacy consents renew. - * + * * @param integer $expire * @param integer $remind * From 9523b4d619abcf3d4e6b229e6683846acb5180bf Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Mon, 26 Jun 2023 08:19:47 +0200 Subject: [PATCH 30/59] Update plugins/task/privacyconsent/src/Extension/PrivacyConsent.php Co-authored-by: Richard Fath --- .../task/privacyconsent/src/Extension/PrivacyConsent.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 48f0ac67d0b43..87d909d2fc927 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -94,8 +94,10 @@ private function privacyConsents(ExecuteTaskEvent $event): int $expire = (int) $event->getArgument('params')->consentexpiration ?? 365; $remind = (int) $event->getArgument('params')->remind ?? 30; - if (($this->invalidateExpiredConsents($expire) === Status::OK) && - ($this->remindExpiringConsents($expire, $remind) === Status::OK)) { + if ( + $this->invalidateExpiredConsents($expire) === Status::OK + && $this->remindExpiringConsents($expire, $remind) === Status::OK + ) { return Status::OK; } From 28d0a4c5580a0a82016ac6b0c7004db0cb1324c9 Mon Sep 17 00:00:00 2001 From: alikon Date: Mon, 26 Jun 2023 09:22:02 +0200 Subject: [PATCH 31/59] migratePrivacyconsentConfiguration --- administrator/components/com_admin/script.php | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 4958a2109643c..b038e8b38153f 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -946,6 +946,74 @@ public function postflight($action, $installer) return false; } + if (!$this->migratePrivacyconsentConfiguration()) { + return false; + } + + return true; + } + + /** + * Migrate privacyconsents system plugin configuration + * + * @return boolean True on success + * + * @since 5.0.0 + */ + private function migratePrivacyconsentConfiguration(): bool + { + $db = Factory::getDbo(); + + try { + // Get the PrivacyConsent system plugin's parameters + $row = $db->setQuery( + $db->getQuery(true) + ->select($db->quotename('enabled'), $db->quoteName('params')) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) + ->where($db->quoteName('element') . ' = ' . $db->quote('privacyconsent')) + )->loadObject(); + } catch (Exception $e) { + echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '
'; + + return false; + } + // If disbled there is nothing to migrate + if (!$row->enabled) { + return true; + } + + $params = json_decode($row->params, true); + + /** @var SchedulerComponent $component */ + $component = Factory::getApplication()->bootComponent('com_scheduler'); + + /** @var TaskModel $model */ + $model = $component->getMVCFactory()->createModel('Task', 'Administrator', ['ignore_request' => true]); + $task = [ + 'title' => 'UpdateNotification', + 'type' => 'update.notification', + 'execution_rules' => [ + 'rule-type' => 'interval-hours', + 'interval-hours' => $params->get('cachetimeout', 6), + 'exec-time' => gmdate("H:i", $params->get('lastrun', 0)), + ], + 'state' => 1, + 'params' => [ + 'consentexpiration' => $params->get('consentexpiration', 360), + 'remind' => $params->get('remind', 30), + ], + ]; + + try { + $model->save($task); + } catch (Exception $e) { + echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '
'; + + return false; + } + return true; } From 39ceb62e0d88c994e268b91b10a4a4a1aeed4ea9 Mon Sep 17 00:00:00 2001 From: alikon Date: Mon, 26 Jun 2023 12:22:43 +0200 Subject: [PATCH 32/59] PrivacyConsent migr --- administrator/components/com_admin/script.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 30b103992f454..5e5b233cf150c 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -1474,8 +1474,8 @@ private function migratePrivacyconsentConfiguration(): bool /** @var TaskModel $model */ $model = $component->getMVCFactory()->createModel('Task', 'Administrator', ['ignore_request' => true]); $task = [ - 'title' => 'UpdateNotification', - 'type' => 'update.notification', + 'title' => 'PrivacyConsent', + 'type' => 'privacy.consent', 'execution_rules' => [ 'rule-type' => 'interval-hours', 'interval-hours' => $params->get('cachetimeout', 6), From f4129cbdb9efa9414825a6aef73367697d5bbd72 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Tue, 1 Aug 2023 19:19:34 +0200 Subject: [PATCH 33/59] Rename 5.0.0-2023-06-13.sql to 5.0.0-2023-08-03.sql shift --- .../mysql/{5.0.0-2023-06-13.sql => 5.0.0-2023-08-03.sql} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename administrator/components/com_admin/sql/updates/mysql/{5.0.0-2023-06-13.sql => 5.0.0-2023-08-03.sql} (97%) diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-03.sql similarity index 97% rename from administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql rename to administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-03.sql index 0f1572267d513..d6ab2f74b7af6 100644 --- a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-06-13.sql +++ b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-03.sql @@ -1,4 +1,4 @@ INSERT INTO `#__extensions` (`name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES ('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); INSERT INTO `#__mail_templates` (`template_id`, `extension`, `language`, `subject`, `body`, `htmlbody`, `attachments`, `params`) VALUES -('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); \ No newline at end of file +('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); From 7a2bb3296fb730471bbf78678a60608c1e502e59 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Tue, 1 Aug 2023 19:20:05 +0200 Subject: [PATCH 34/59] Rename 5.0.0-2023-03-13.sql to 5.0.0-2023-08-03.sql shift --- .../postgresql/{5.0.0-2023-03-13.sql => 5.0.0-2023-08-03.sql} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename administrator/components/com_admin/sql/updates/postgresql/{5.0.0-2023-03-13.sql => 5.0.0-2023-08-03.sql} (97%) diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-03.sql similarity index 97% rename from administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql rename to administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-03.sql index cc07c9e7ecf1e..262be27ca4e79 100644 --- a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-03-13.sql +++ b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-03.sql @@ -1,4 +1,4 @@ INSERT INTO "#__extensions" ("name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "custom_data", "checked_out", "checked_out_time", "ordering", "state") VALUES ('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); INSERT INTO "#__mail_templates" ("template_id", "language", "subject", "body", "htmlbody", "attachments", "params") VALUES -('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); \ No newline at end of file +('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); From 76fe19757bf221acbe5c33ba452f64c40ef5359d Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sat, 26 Aug 2023 07:43:43 +0200 Subject: [PATCH 35/59] Rename 5.0.0-2023-08-03.sql to 5.0.0-2023-08-23.sql --- .../updates/mysql/{5.0.0-2023-08-03.sql => 5.0.0-2023-08-23.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename administrator/components/com_admin/sql/updates/mysql/{5.0.0-2023-08-03.sql => 5.0.0-2023-08-23.sql} (100%) diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-03.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-03.sql rename to administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql From eedbdf889821e4fd7c8e65096a22e245488bceb2 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sat, 26 Aug 2023 07:44:04 +0200 Subject: [PATCH 36/59] Rename 5.0.0-2023-08-03.sql to 5.0.0-2023-08-23.sql --- .../postgresql/{5.0.0-2023-08-03.sql => 5.0.0-2023-08-23.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename administrator/components/com_admin/sql/updates/postgresql/{5.0.0-2023-08-03.sql => 5.0.0-2023-08-23.sql} (100%) diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-03.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-03.sql rename to administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql From a349b6e2dbfa875491a55e3d2f8515b07bb5aeee Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Mon, 28 Aug 2023 08:55:24 +0200 Subject: [PATCH 37/59] userfactory --- plugins/task/privacyconsent/src/Extension/PrivacyConsent.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php index 87d909d2fc927..d79a595e4ee8b 100644 --- a/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -18,6 +18,7 @@ use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; +use Joomla\CMS\User\UserFactoryAwareTrait; use Joomla\CMS\User\UserHelper; use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; use Joomla\Component\Scheduler\Administrator\Task\Status; @@ -42,6 +43,7 @@ final class PrivacyConsent extends CMSPlugin implements SubscriberInterface { use DatabaseAwareTrait; use TaskPluginTrait; + use UserFactoryAwareTrait; /** * @var string[] @@ -250,7 +252,7 @@ private function invalidateExpiredConsents($expire): int $messageModel->notifySuperUsers( Text::_('PLG_TASK_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT'), - Text::sprintf('PLG_TASK_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE', Factory::getUser($user->user_id)->username) + Text::sprintf('PLG_TASK_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE', $this->getUserFactory()->loadUserById($user->user_id)->username) ); } From 0daca7d81f88a3876d047aad5edcd4594ac55b80 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Mon, 28 Aug 2023 08:56:26 +0200 Subject: [PATCH 38/59] userfactory --- plugins/task/privacyconsent/services/provider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/task/privacyconsent/services/provider.php b/plugins/task/privacyconsent/services/provider.php index a773c7c305f70..587ff225a7ff9 100644 --- a/plugins/task/privacyconsent/services/provider.php +++ b/plugins/task/privacyconsent/services/provider.php @@ -13,6 +13,7 @@ use Joomla\CMS\Extension\PluginInterface; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; +use Joomla\CMS\User\UserFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; @@ -42,6 +43,7 @@ function (Container $container) { ); $plugin->setApplication(Factory::getApplication()); $plugin->setDatabase($container->get(DatabaseInterface::class)); + $plugin->setUserFactory($container->get(UserFactoryInterface::class)); return $plugin; } From 32d3c7b2cdfd03b8b47fe370da7a8ba0d9eea235 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Mon, 28 Aug 2023 09:00:45 +0200 Subject: [PATCH 39/59] delete mail_template --- .../components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql index d6ab2f74b7af6..565ca49b5096e 100644 --- a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql +++ b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql @@ -2,3 +2,4 @@ INSERT INTO `#__extensions` (`name`, `type`, `element`, `folder`, `client_id`, ` ('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); INSERT INTO `#__mail_templates` (`template_id`, `extension`, `language`, `subject`, `body`, `htmlbody`, `attachments`, `params`) VALUES ('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); +DELETE FROM `#__mail_templates` WHERE `template_id` = 'plg_system_privacyconsent.request.reminder'; From 8079f5b4da5b681e5b5499c7ddcac129e11db09b Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Mon, 28 Aug 2023 09:01:54 +0200 Subject: [PATCH 40/59] delete mail_template --- .../com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql index 262be27ca4e79..4da63139c896d 100644 --- a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql +++ b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql @@ -2,3 +2,4 @@ INSERT INTO "#__extensions" ("name", "type", "element", "folder", "client_id", " ('plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); INSERT INTO "#__mail_templates" ("template_id", "language", "subject", "body", "htmlbody", "attachments", "params") VALUES ('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'); +DELETE FROM "#__mail_templates" WHERE "template_id" = 'plg_system_privacyconsent.request.reminder'; From da8a9c17d76f1d37d2ec4b53ffa6b137314c99fb Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 13:50:24 +0200 Subject: [PATCH 41/59] Rename 5.0.0-2023-08-23.sql to 5.0.0-2023-08-30.sql --- .../updates/mysql/{5.0.0-2023-08-23.sql => 5.0.0-2023-08-30.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename administrator/components/com_admin/sql/updates/mysql/{5.0.0-2023-08-23.sql => 5.0.0-2023-08-30.sql} (100%) diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-30.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-23.sql rename to administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-30.sql From 969d8e400c39190696eadce7b5981cd21151aaa9 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 13:51:15 +0200 Subject: [PATCH 42/59] Rename 5.0.0-2023-08-23.sql to 5.0.0-2023-08-30.sql --- .../postgresql/{5.0.0-2023-08-23.sql => 5.0.0-2023-08-30.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename administrator/components/com_admin/sql/updates/postgresql/{5.0.0-2023-08-23.sql => 5.0.0-2023-08-30.sql} (100%) diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-30.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-23.sql rename to administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-30.sql From 87940ee73c380b3b11a0b57988ef25b42cc020dc Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 15:29:36 +0200 Subject: [PATCH 43/59] Deprecate language strings --- .../en-GB/plg_system_privacyconsent.ini | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/administrator/language/en-GB/plg_system_privacyconsent.ini b/administrator/language/en-GB/plg_system_privacyconsent.ini index 30ef0294554d5..2e424fd6ce9a4 100644 --- a/administrator/language/en-GB/plg_system_privacyconsent.ini +++ b/administrator/language/en-GB/plg_system_privacyconsent.ini @@ -5,18 +5,12 @@ PLG_SYSTEM_PRIVACYCONSENT="System - Privacy Consent" PLG_SYSTEM_PRIVACYCONSENT_BODY="

The user consented to storing their user information using the IP address %s

The user agent string of the user's browser was:
%s

This information was automatically recorded when the user submitted their details on the website and checked the confirm box

" -PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_DESC="How often the check is performed." -PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_LABEL="Periodic check (days)" PLG_SYSTEM_PRIVACYCONSENT_CONSENT="User {username} consented to the privacy policy." -PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_DESC="Number of days after which the privacy consent shall expire." -PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_LABEL="Expiration" PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_BODY="Your Privacy Consent given at {URL} will expire in few days, you can renew the privacy consent for this website.\n\nIn order to do this, you can complete one of the following tasks:\n\n1. Visit the following URL: {TOKENURL}\n\n2. Copy your token from this email, visit the referenced URL, and paste your token into the form.\nURL: {FORMURL}\nToken: {TOKEN}\n\nPlease note that this token is only valid for this account." PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT="Privacy Consent at {SITENAME}" PLG_SYSTEM_PRIVACYCONSENT_EXPIRATION_FIELDSET_LABEL="Expiration" PLG_SYSTEM_PRIVACYCONSENT_FIELD_ARTICLE_DESC="Select the article from the list or create a new one." PLG_SYSTEM_PRIVACYCONSENT_FIELD_ARTICLE_LABEL="Privacy Article" -PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_DESC="When enabled it performs checks for consent expiration." -PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_LABEL="Enable" PLG_SYSTEM_PRIVACYCONSENT_FIELD_ERROR="Agreement to the site's Privacy Policy is required." PLG_SYSTEM_PRIVACYCONSENT_FIELD_LABEL="Privacy Policy" PLG_SYSTEM_PRIVACYCONSENT_FIELD_MENU_ITEM_LABEL="Privacy Menu Item" @@ -36,7 +30,15 @@ PLG_SYSTEM_PRIVACYCONSENT_OPTION_DO_NOT_AGREE="I do not agree" PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT="Please confirm that you consent to this website storing your information by agreeing to the privacy policy." PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DESC="Custom message to be displayed on redirect. If left blank then the default message will be used." PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_LABEL="Redirect Message" -PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_DESC="Number of days to send a reminder before the expiration of the privacy consent." -PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_LABEL="Remind" PLG_SYSTEM_PRIVACYCONSENT_SUBJECT="Privacy Policy" PLG_SYSTEM_PRIVACYCONSENT_XML_DESCRIPTION="Basic plugin to request user's consent to the site's privacy policy. Existing users who have not consented yet will be redirected on login to update their profile." + +; All the following strings are deprecated and will be removed with 6.0 +PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_DESC="How often the check is performed." +PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_LABEL="Periodic check (days)" +PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_DESC="Number of days after which the privacy consent shall expire." +PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_LABEL="Expiration" +PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_DESC="When enabled it performs checks for consent expiration." +PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_LABEL="Enable" +PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_DESC="Number of days to send a reminder before the expiration of the privacy consent." +PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_LABEL="Remind" From 78e615103f8ede5d96677efba43a4ea5a3f46882 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 16:03:50 +0200 Subject: [PATCH 44/59] One more deprecated language string --- administrator/language/en-GB/plg_system_privacyconsent.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/language/en-GB/plg_system_privacyconsent.ini b/administrator/language/en-GB/plg_system_privacyconsent.ini index 2e424fd6ce9a4..2abb7a81a4ec4 100644 --- a/administrator/language/en-GB/plg_system_privacyconsent.ini +++ b/administrator/language/en-GB/plg_system_privacyconsent.ini @@ -8,7 +8,6 @@ PLG_SYSTEM_PRIVACYCONSENT_BODY="

The user consented to storing their user info PLG_SYSTEM_PRIVACYCONSENT_CONSENT="User {username} consented to the privacy policy." PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_BODY="Your Privacy Consent given at {URL} will expire in few days, you can renew the privacy consent for this website.\n\nIn order to do this, you can complete one of the following tasks:\n\n1. Visit the following URL: {TOKENURL}\n\n2. Copy your token from this email, visit the referenced URL, and paste your token into the form.\nURL: {FORMURL}\nToken: {TOKEN}\n\nPlease note that this token is only valid for this account." PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT="Privacy Consent at {SITENAME}" -PLG_SYSTEM_PRIVACYCONSENT_EXPIRATION_FIELDSET_LABEL="Expiration" PLG_SYSTEM_PRIVACYCONSENT_FIELD_ARTICLE_DESC="Select the article from the list or create a new one." PLG_SYSTEM_PRIVACYCONSENT_FIELD_ARTICLE_LABEL="Privacy Article" PLG_SYSTEM_PRIVACYCONSENT_FIELD_ERROR="Agreement to the site's Privacy Policy is required." @@ -38,6 +37,7 @@ PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_DESC="How often the check is performed." PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_LABEL="Periodic check (days)" PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_DESC="Number of days after which the privacy consent shall expire." PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_LABEL="Expiration" +PLG_SYSTEM_PRIVACYCONSENT_EXPIRATION_FIELDSET_LABEL="Expiration" PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_DESC="When enabled it performs checks for consent expiration." PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_LABEL="Enable" PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_DESC="Number of days to send a reminder before the expiration of the privacy consent." From 94396dde7496977e3f45556dc4368da491c1e730 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 16:23:28 +0200 Subject: [PATCH 45/59] Do not add task when consent expiration was disabled --- administrator/components/com_admin/script.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 59b167b2713d8..782de2d370ecf 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2291,6 +2291,11 @@ private function migratePrivacyconsentConfiguration(): bool $params = json_decode($row->params, true); + // If consent expiration was disbled there is nothing to migrate + if (!$params->get('enabled', 0)) { + return; + } + /** @var SchedulerComponent $component */ $component = Factory::getApplication()->bootComponent('com_scheduler'); From 75b0dbc4a1a02342d76d52497c836bbe023180ec Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 16:29:32 +0200 Subject: [PATCH 46/59] Use Registry when using get method later. --- administrator/components/com_admin/script.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 782de2d370ecf..6f350abcd61f4 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2289,7 +2289,7 @@ private function migratePrivacyconsentConfiguration(): bool return true; } - $params = json_decode($row->params, true); + $params = new Registry($row->params); // If consent expiration was disbled there is nothing to migrate if (!$params->get('enabled', 0)) { From 3a98c210c733e153bf548ebf9210a1c268e2824a Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 16:35:12 +0200 Subject: [PATCH 47/59] My mistake from previous commit --- administrator/components/com_admin/script.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 6f350abcd61f4..727f98caf9aa3 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2293,7 +2293,7 @@ private function migratePrivacyconsentConfiguration(): bool // If consent expiration was disbled there is nothing to migrate if (!$params->get('enabled', 0)) { - return; + return true; } /** @var SchedulerComponent $component */ From d39745d37852269cbc0f8419ad93247dbee59057 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 16:37:55 +0200 Subject: [PATCH 48/59] Add missing use statement for my previos commits --- administrator/components/com_admin/script.php | 1 + 1 file changed, 1 insertion(+) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 727f98caf9aa3..0a397371dabea 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -19,6 +19,7 @@ use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\Database\ParameterType; +use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; From 0cee5fe0ae48c467a4f9784b29653ec87b108723 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 17:13:48 +0200 Subject: [PATCH 49/59] Cachetimeout was given in days in the system plugin, and default was 30. --- administrator/components/com_admin/script.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 0a397371dabea..6854b2536c93d 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2306,9 +2306,10 @@ private function migratePrivacyconsentConfiguration(): bool 'title' => 'PrivacyConsent', 'type' => 'privacy.consent', 'execution_rules' => [ - 'rule-type' => 'interval-hours', - 'interval-hours' => $params->get('cachetimeout', 6), - 'exec-time' => gmdate("H:i", $params->get('lastrun', 0)), + 'rule-type' => 'interval-days', + 'interval-days' => $params->get('cachetimeout', 30), + 'exec-time' => gmdate("H:i", $params->get('lastrun', 0)), + 'exec-day' => gmdate('d'), ], 'state' => 1, 'params' => [ From 50bf3d68dd8872baba2e734084645a368be52a0f Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Fri, 1 Sep 2023 19:26:37 +0200 Subject: [PATCH 50/59] Don't migrate if the system plugin was not found --- administrator/components/com_admin/script.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 6854b2536c93d..679babf02cea3 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2285,8 +2285,9 @@ private function migratePrivacyconsentConfiguration(): bool return false; } - // If disbled there is nothing to migrate - if (!$row->enabled) { + + // If not existing or disbled there is nothing to migrate + if (!$row || !$row->enabled) { return true; } From 5a818386b564b064a213784151c8fa05732a4f89 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sat, 2 Sep 2023 11:27:39 +0200 Subject: [PATCH 51/59] Single quotes --- administrator/components/com_admin/script.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 679babf02cea3..2338d8c31cfe0 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2309,7 +2309,7 @@ private function migratePrivacyconsentConfiguration(): bool 'execution_rules' => [ 'rule-type' => 'interval-days', 'interval-days' => $params->get('cachetimeout', 30), - 'exec-time' => gmdate("H:i", $params->get('lastrun', 0)), + 'exec-time' => gmdate('H:i', $params->get('lastrun', 0)), 'exec-day' => gmdate('d'), ], 'state' => 1, From 0cf9f732c60b1df09d30af3c836f36d00ee898cf Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sat, 2 Sep 2023 12:44:25 +0200 Subject: [PATCH 52/59] Remove extra reference assignment for dispatcher argument --- plugins/task/privacyconsent/services/provider.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/task/privacyconsent/services/provider.php b/plugins/task/privacyconsent/services/provider.php index 587ff225a7ff9..4bd20fa1c74c3 100644 --- a/plugins/task/privacyconsent/services/provider.php +++ b/plugins/task/privacyconsent/services/provider.php @@ -35,10 +35,8 @@ public function register(Container $container) $container->set( PluginInterface::class, function (Container $container) { - $dispatcher = $container->get(DispatcherInterface::class); - $plugin = new PrivacyConsent( - $dispatcher, + $container->get(DispatcherInterface::class), (array) PluginHelper::getPlugin('task', 'privacyconsent') ); $plugin->setApplication(Factory::getApplication()); From e267a3f49560bab3f60bf0aa8780a1662edb7310 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sat, 2 Sep 2023 16:44:47 +0200 Subject: [PATCH 53/59] Rename 5.0.0-2023-08-30.sql to 5.0.0-2023-09-02.sql --- .../updates/mysql/{5.0.0-2023-08-30.sql => 5.0.0-2023-09-02.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename administrator/components/com_admin/sql/updates/mysql/{5.0.0-2023-08-30.sql => 5.0.0-2023-09-02.sql} (100%) diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-30.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-09-02.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-08-30.sql rename to administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-09-02.sql From 11d82768bae2f760afd8f96e2898c857089e42c1 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sat, 2 Sep 2023 16:45:08 +0200 Subject: [PATCH 54/59] Rename 5.0.0-2023-08-30.sql to 5.0.0-2023-09-02.sql --- .../postgresql/{5.0.0-2023-08-30.sql => 5.0.0-2023-09-02.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename administrator/components/com_admin/sql/updates/postgresql/{5.0.0-2023-08-30.sql => 5.0.0-2023-09-02.sql} (100%) diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-30.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-09-02.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-08-30.sql rename to administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-09-02.sql From 5441152c57dc6b7d216f3e45c21047ad1c7ce290 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sun, 3 Sep 2023 12:12:03 +0200 Subject: [PATCH 55/59] Fix lastrun default on update --- administrator/components/com_admin/script.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 01232413eb057..b404f3d01b645 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2310,7 +2310,7 @@ private function migratePrivacyconsentConfiguration(): bool 'execution_rules' => [ 'rule-type' => 'interval-days', 'interval-days' => $params->get('cachetimeout', 30), - 'exec-time' => gmdate('H:i', $params->get('lastrun', 0)), + 'exec-time' => gmdate('H:i', $params->get('lastrun', time())), 'exec-day' => gmdate('d'), ], 'state' => 1, From 8ddf28d117794ff863aa2a825ee529792f49f57c Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sun, 3 Sep 2023 15:08:16 +0200 Subject: [PATCH 56/59] Fix previous conflict resolution --- installation/sql/postgresql/base.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation/sql/postgresql/base.sql b/installation/sql/postgresql/base.sql index 66a423378c707..883ad28bf8ff5 100644 --- a/installation/sql/postgresql/base.sql +++ b/installation/sql/postgresql/base.sql @@ -375,7 +375,7 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_task_rotatelogs', 'plugin', 'rotatelogs', 'task', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), (0, 'plg_task_sitestatus', 'plugin', 'sitestatus', 'task', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), -(0, 'plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(0, 'plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), (0, 'plg_multifactorauth_totp', 'plugin', 'totp', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 1, 0), (0, 'plg_multifactorauth_yubikey', 'plugin', 'yubikey', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_multifactorauth_webauthn', 'plugin', 'webauthn', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 3, 0), From e35d7f07ee973c502d547f84ef4ad1bd61151a63 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sun, 3 Sep 2023 15:09:06 +0200 Subject: [PATCH 57/59] Forgotten fix --- installation/sql/mysql/base.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation/sql/mysql/base.sql b/installation/sql/mysql/base.sql index cc162436fe2c0..0c0b6a33700aa 100644 --- a/installation/sql/mysql/base.sql +++ b/installation/sql/mysql/base.sql @@ -369,7 +369,7 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_task_privacyconsent', 'plugin', 'privacyconsent', 'task', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_task_rotatelogs', 'plugin', 'rotatelogs', 'task', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), (0, 'plg_task_sitestatus', 'plugin', 'sitestatus', 'task', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), -(0, 'plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(0, 'plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), (0, 'plg_multifactorauth_totp', 'plugin', 'totp', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 1, 0), (0, 'plg_multifactorauth_yubikey', 'plugin', 'yubikey', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_multifactorauth_webauthn', 'plugin', 'webauthn', 'multifactorauth', 0, 1, 1, 0, 1, '', '', '', 3, 0), From d827f717176d3850de42b485e06a43f91f11df5a Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sun, 3 Sep 2023 15:40:59 +0200 Subject: [PATCH 58/59] Use the right language strings in the form --- plugins/task/privacyconsent/forms/privacyconsentForm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/task/privacyconsent/forms/privacyconsentForm.xml b/plugins/task/privacyconsent/forms/privacyconsentForm.xml index 0573b30d4c7c2..dd6f12b71ff64 100644 --- a/plugins/task/privacyconsent/forms/privacyconsentForm.xml +++ b/plugins/task/privacyconsent/forms/privacyconsentForm.xml @@ -5,8 +5,8 @@ Date: Sun, 3 Sep 2023 15:56:44 +0200 Subject: [PATCH 59/59] Fix update SQL scripts missing column value --- .../components/com_admin/sql/updates/mysql/5.0.0-2023-09-02.sql | 2 +- .../com_admin/sql/updates/postgresql/5.0.0-2023-09-02.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-09-02.sql b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-09-02.sql index 8814104018654..27c020cd2fb52 100644 --- a/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-09-02.sql +++ b/administrator/components/com_admin/sql/updates/mysql/5.0.0-2023-09-02.sql @@ -4,7 +4,7 @@ INSERT INTO `#__extensions` (`name`, `type`, `element`, `folder`, `client_id`, ` ('plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); INSERT INTO `#__mail_templates` (`template_id`, `extension`, `language`, `subject`, `body`, `htmlbody`, `attachments`, `params`) VALUES -('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), +('plg_task_privacyconsent.request.reminder', 'plg_task_privacyconsent', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), ('plg_task_updatenotification.mail', 'plg_task_updatenotification', '', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_SUBJECT', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_BODY', '', '', '{"tags":["newversion","curversion","sitename","url","link","releasenews"]}'); DELETE FROM `#__mail_templates` WHERE `template_id` IN ('plg_system_privacyconsent.request.reminder', 'plg_system_updatenotification.mail'); diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-09-02.sql b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-09-02.sql index d429316a247f4..931f4acb15b6e 100644 --- a/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-09-02.sql +++ b/administrator/components/com_admin/sql/updates/postgresql/5.0.0-2023-09-02.sql @@ -4,7 +4,7 @@ INSERT INTO "#__extensions" ("name", "type", "element", "folder", "client_id", " ('plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0); INSERT INTO "#__mail_templates" ("template_id", "extension", "language", "subject", "body", "htmlbody", "attachments", "params") VALUES -('plg_task_privacyconsent.request.reminder', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), +('plg_task_privacyconsent.request.reminder', 'plg_task_privacyconsent', '', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT', 'PLG_TASK_PRIVACYCONSENT_EMAIL_REMIND_BODY', '', '', '{"tags":["sitename","url","tokenurl","formurl","token"]}'), ('plg_task_updatenotification.mail', 'plg_task_updatenotification', '', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_SUBJECT', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_BODY', '', '', '{"tags":["newversion","curversion","sitename","url","link","releasenews"]}'); DELETE FROM "#__mail_templates" WHERE "template_id" IN ('plg_system_privacyconsent.request.reminder', 'plg_system_updatenotification.mail');