We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New language relevant PR in upstream repo: joomla/joomla-cms#40553 Here are the upstream changes:
diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 1eefc688d162..768a04aa6df0 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2346,11 +2346,86 @@ public function postflight($action, $installer) return false; } + if (!$this->migratePrivacyconsentConfiguration()) { + return false; + } + $this->setGuidedToursUid(); 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()) . '<br>'; + + return false; + } + + // If not existing or disbled there is nothing to migrate + if (!$row || !$row->enabled) { + return true; + } + + $params = new Registry($row->params); + + // If consent expiration was disbled there is nothing to migrate + if (!$params->get('enabled', 0)) { + return true; + } + + /** @var SchedulerComponent $component */ + $component = Factory::getApplication()->bootComponent('com_scheduler'); + + /** @var TaskModel $model */ + $model = $component->getMVCFactory()->createModel('Task', 'Administrator', ['ignore_request' => true]); + $task = [ + 'title' => 'PrivacyConsent', + 'type' => 'privacy.consent', + 'execution_rules' => [ + 'rule-type' => 'interval-days', + 'interval-days' => $params->get('cachetimeout', 30), + 'exec-time' => gmdate('H:i', $params->get('lastrun', time())), + 'exec-day' => gmdate('d'), + ], + '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()) . '<br>'; + + return false; + } + + return true; + } + /** * Migrate TinyMCE editor plugin configuration * 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 5552693d22c2..27c020cd2fb5 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 @@ -1,9 +1,12 @@ 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), ('plg_task_rotatelogs', 'plugin', 'rotatelogs', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0), ('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', '', '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` = 'plg_system_updatenotification.mail'; +DELETE FROM `#__mail_templates` WHERE `template_id` IN ('plg_system_privacyconsent.request.reminder', 'plg_system_updatenotification.mail'); + DELETE FROM `#__postinstall_messages` WHERE `condition_file` = 'site://plugins/system/updatenotification/postinstall/updatecachetime.php'; 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 fc99290a3d62..931f4acb15b6 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 @@ -1,9 +1,12 @@ 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), ('plg_task_rotatelogs', 'plugin', 'rotatelogs', 'task', 0, 1, 1, 0, 1, '', '{}', '', NULL, NULL, 0, 0), ('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', '', '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" = 'plg_system_updatenotification.mail'; +DELETE FROM "#__mail_templates" WHERE "template_id" IN ('plg_system_privacyconsent.request.reminder', 'plg_system_updatenotification.mail'); + DELETE FROM "#__postinstall_messages" WHERE "condition_file" = 'site://plugins/system/updatenotification/postinstall/updatecachetime.php'; diff --git a/administrator/language/en-GB/plg_system_privacyconsent.ini b/administrator/language/en-GB/plg_system_privacyconsent.ini index 30ef0294554d..2abb7a81a4ec 100644 --- a/administrator/language/en-GB/plg_system_privacyconsent.ini +++ b/administrator/language/en-GB/plg_system_privacyconsent.ini @@ -5,18 +5,11 @@ PLG_SYSTEM_PRIVACYCONSENT="System - Privacy Consent" PLG_SYSTEM_PRIVACYCONSENT_BODY="<p>The user consented to storing their user information using the IP address <strong>%s</strong></p><p>The user agent string of the user's browser was:<br>%s</p><p>This information was automatically recorded when the user submitted their details on the website and checked the confirm box</p>" -PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_DESC="How often the check is performed." -PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_LABEL="Periodic check (days)" PLG_SYSTEM_PRIVACYCONSENT_CONSENT="User <a href='{accountlink}'>{username}</a> 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 +29,16 @@ 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_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." +PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_LABEL="Remind" 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 000000000000..5baf97d04fae --- /dev/null +++ b/administrator/language/en-GB/plg_task_privacyconsent.ini @@ -0,0 +1,47 @@ +; Joomla! Project +; (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> +; 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_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" +PLG_TASK_PRIVACYCONSENT_REMIND_TITLE="Remind" +PLG_TASK_PRIVACYCONSENT_BODY="<p>The user consented to storing their user information using the IP address <strong>%s</strong></p><p>The user agent string of the user's browser was:<br>%s</p><p>This information was automatically recorded when the user submitted their details on the website and checked the confirm box</p>" +PLG_TASK_PRIVACYCONSENT_CACHETIMEOUT_DESC="How often the check is performed." +PLG_TASK_PRIVACYCONSENT_CACHETIMEOUT_LABEL="Periodic check (days)" +PLG_TASK_PRIVACYCONSENT_CONSENT="User <a href='{accountlink}'>{username}</a> 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 000000000000..4893e9d29203 --- /dev/null +++ b/administrator/language/en-GB/plg_task_privacyconsent.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> +; 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/installation/sql/mysql/base.sql b/installation/sql/mysql/base.sql index d5ed86c6dd52..0c0b6a33700a 100644 --- a/installation/sql/mysql/base.sql +++ b/installation/sql/mysql/base.sql @@ -366,9 +366,10 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_task_checkfiles', 'plugin', 'checkfiles', 'task', 0, 1, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_task_globalcheckin', 'plugin', 'globalcheckin', 'task', 0, 1, 1, 0, 0, '', '{}', '', 2, 0), (0, 'plg_task_requests', 'plugin', 'requests', 'task', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), -(0, 'plg_task_rotatelogs', 'plugin', 'rotatelogs', 'task', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), -(0, 'plg_task_sitestatus', 'plugin', 'sitestatus', 'task', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), -(0, 'plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(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, '', '{}', '', 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), diff --git a/installation/sql/mysql/supports.sql b/installation/sql/mysql/supports.sql index f7252db7465a..2829bad359b0 100644 --- a/installation/sql/mysql/supports.sql +++ b/installation/sql/mysql/supports.sql @@ -431,7 +431,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/base.sql b/installation/sql/postgresql/base.sql index 58314488cbde..883ad28bf8ff 100644 --- a/installation/sql/postgresql/base.sql +++ b/installation/sql/postgresql/base.sql @@ -372,9 +372,10 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_task_checkfiles', 'plugin', 'checkfiles', 'task', 0, 1, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_task_globalcheckin', 'plugin', 'globalcheckin', 'task', 0, 1, 1, 0, 0, '', '{}', '', 2, 0), (0, 'plg_task_requests', 'plugin', 'requests', 'task', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), -(0, 'plg_task_rotatelogs', 'plugin', 'rotatelogs', 'task', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), -(0, 'plg_task_sitestatus', 'plugin', 'sitestatus', 'task', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), -(0, 'plg_task_updatenotification', 'plugin', 'updatenotification', 'task', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(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, '', '{}', '', 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), diff --git a/installation/sql/postgresql/supports.sql b/installation/sql/postgresql/supports.sql index 1ad64316c3c2..fb6571f6f951 100644 --- a/installation/sql/postgresql/supports.sql +++ b/installation/sql/postgresql/supports.sql @@ -442,7 +442,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/libraries/src/Extension/ExtensionHelper.php b/libraries/src/Extension/ExtensionHelper.php index e52390c4cb9a..bde784432c9d 100644 --- a/libraries/src/Extension/ExtensionHelper.php +++ b/libraries/src/Extension/ExtensionHelper.php @@ -319,6 +319,7 @@ class ExtensionHelper // Core plugin extensions - task scheduler ['plugin', 'checkfiles', 'task', 0], ['plugin', 'globalcheckin', 'task', 0], + ['plugin', 'privacyconsent', 'task', 0], ['plugin', 'requests', 'task', 0], ['plugin', 'rotatelogs', 'task', 0], ['plugin', 'sitestatus', 'task', 0], diff --git a/plugins/system/privacyconsent/privacyconsent.xml b/plugins/system/privacyconsent/privacyconsent.xml index 9f7d31f02082..6c47d949ef51 100644 --- a/plugins/system/privacyconsent/privacyconsent.xml +++ b/plugins/system/privacyconsent/privacyconsent.xml @@ -78,65 +78,6 @@ filter="html" /> </fieldset> - <fieldset - name="expiration" - label="PLG_SYSTEM_PRIVACYCONSENT_EXPIRATION_FIELDSET_LABEL" - > - <field - name="enabled" - type="radio" - label="PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_LABEL" - description="PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_DESC" - layout="joomla.form.field.radio.switcher" - default="0" - filter="integer" - > - <option value="0">JNO</option> - <option value="1">JYES</option> - </field> - <field - name="cachetimeout" - type="integer" - label="PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_LABEL" - description="PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_DESC" - first="0" - last="120" - step="1" - default="30" - filter="int" - validate="number" - /> - <field - name="consentexpiration" - type="integer" - label="PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_LABEL" - description="PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_DESC" - first="180" - last="720" - step="30" - default="360" - filter="int" - validate="number" - /> - <field - name="remind" - type="integer" - label="PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_LABEL" - description="PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_DESC" - first="0" - last="120" - step="1" - default="30" - filter="int" - validate="number" - /> - <field - name="lastrun" - type="hidden" - default="0" - filter="integer" - /> - </fieldset> </fields> </config> </extension> diff --git a/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php b/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php index 3376b260f548..639aa20cff5a 100644 --- a/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php +++ b/plugins/system/privacyconsent/src/Extension/PrivacyConsent.php @@ -10,28 +10,18 @@ namespace Joomla\Plugin\System\PrivacyConsent\Extension; -use Joomla\CMS\Application\ApplicationHelper; -use Joomla\CMS\Cache\Cache; use Joomla\CMS\Event\Privacy\CheckPrivacyPolicyPublishedEvent; 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\UserFactoryAwareTrait; -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; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -450,252 +440,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; - } - - // Stop 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', $this->getUserFactory()->loadUserById($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/forms/privacyconsentForm.xml b/plugins/task/privacyconsent/forms/privacyconsentForm.xml new file mode 100644 index 000000000000..dd6f12b71ff6 --- /dev/null +++ b/plugins/task/privacyconsent/forms/privacyconsentForm.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<form> + <fields name="params"> + <fieldset name="task_params"> + <field + name="consentexpiration" + type="integer" + label="PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATIONDAYS_LABEL" + description="PLG_TASK_PRIVACYCONSENT_CONSENTEXPIRATIONDAYS_DESC" + first="0" + last="720" + step="30" + default="360" + filter="int" + validate="number" + /> + <field + name="remind" + type="integer" + label="PLG_TASK_PRIVACYCONSENT_REMINDBEFORE_LABEL" + description="PLG_TASK_PRIVACYCONSENT_REMINDBEFORE_DESC" + first="0" + last="120" + step="1" + default="30" + filter="int" + validate="number" + /> + </fieldset> + </fields> +</form> diff --git a/plugins/task/privacyconsent/privacyconsent.xml b/plugins/task/privacyconsent/privacyconsent.xml new file mode 100644 index 000000000000..c47fd75c4747 --- /dev/null +++ b/plugins/task/privacyconsent/privacyconsent.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<extension type="plugin" group="task" method="upgrade"> + <name>plg_task_privacyconsent</name> + <author>Joomla! Project</author> + <creationDate>2023-07</creationDate> + <copyright>(C) 2023 Open Source Matters, Inc.</copyright> + <license>GNU General Public License version 2 or later; see LICENSE.txt</license> + <authorEmail>admin@joomla.org</authorEmail> + <authorUrl>www.joomla.org</authorUrl> + <version>5.0.0</version> + <description>PLG_TASK_PRIVACYCONSENT_XML_DESCRIPTION</description> + <namespace path="src">Joomla\Plugin\Task\PrivacyConsent</namespace> + <files> + <folder>forms</folder> + <folder plugin="privacyconsent">services</folder> + <folder>src</folder> + </files> + <languages> + <language tag="en-GB">language/en-GB/plg_task_privacyconsent.ini</language> + <language tag="en-GB">language/en-GB/plg_task_privacyconsent.sys.ini</language> + </languages> +</extension> diff --git a/plugins/task/privacyconsent/services/provider.php b/plugins/task/privacyconsent/services/provider.php new file mode 100644 index 000000000000..4bd20fa1c74c --- /dev/null +++ b/plugins/task/privacyconsent/services/provider.php @@ -0,0 +1,50 @@ +<?php + +/** + * @package Joomla.Plugin + * @subpackage Task.PrivacyConsent + * + * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> + * @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\CMS\User\UserFactoryInterface; +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) { + $plugin = new PrivacyConsent( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('task', 'privacyconsent') + ); + $plugin->setApplication(Factory::getApplication()); + $plugin->setDatabase($container->get(DatabaseInterface::class)); + $plugin->setUserFactory($container->get(UserFactoryInterface::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 000000000000..d79a595e4ee8 --- /dev/null +++ b/plugins/task/privacyconsent/src/Extension/PrivacyConsent.php @@ -0,0 +1,261 @@ +<?php + +/** + * @package Joomla.Plugin + * @subpackage Task.PrivacyConsent + * + * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> + * @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\UserFactoryAwareTrait; +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; + use UserFactoryAwareTrait; + + /** + * @var string[] + * @since __DEPLOY_VERSION__ + */ + private const TASKS_MAP = [ + 'privacy.consent' => [ + 'langConstPrefix' => 'PLG_TASK_PRIVACYCONSENT_INVALIDATE', + 'method' => 'privacyConsents', + 'form' => 'privacyconsentForm', + ], + ]; + + /** + * @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 privacyConsents(ExecuteTaskEvent $event): int + { + // Load the parameters. + $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); + $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_TASK_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_task_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 integer $expire + * + * @return integer The routine exit code. + * + * @since __DEPLOY_VERSION__ + * @throws \Exception + */ + private function invalidateExpiredConsents($expire): int + { + $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_TASK_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', $this->getUserFactory()->loadUserById($user->user_id)->username) + ); + } + + return Status::OK; + } +}
The text was updated successfully, but these errors were encountered:
tecpromotion
Successfully merging a pull request may close this issue.
New language relevant PR in upstream repo: joomla/joomla-cms#40553 Here are the upstream changes:
Click to expand the diff!
The text was updated successfully, but these errors were encountered: