From bf7381b30afdc2347d162ed89357b8f2b336b0ab Mon Sep 17 00:00:00 2001 From: Taslan Graham <gtaslan1@gmail.com> Date: Wed, 6 Nov 2024 15:08:01 -0500 Subject: [PATCH] pkp/pkp-lib#10571 WIP: Allow admins and managers to assign user groups to email templates within a mailable --- .../PKPEmailTemplateController.php | 2 +- .../FieldEmailTemplateUserGroupSettings.php | 33 +++++++++++++++++++ .../forms/emailTemplate/EmailTemplateForm.php | 4 +++ classes/emailTemplate/DAO.php | 29 ++++++++++++++++ classes/emailTemplate/Repository.php | 23 ++++++++++++- classes/emailTemplate/maps/Schema.php | 19 +---------- classes/mail/Repository.php | 16 +++++++++ locale/en/api.po | 3 ++ locale/en/common.po | 3 ++ 9 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 classes/components/forms/FieldEmailTemplateUserGroupSettings.php diff --git a/api/v1/emailTemplates/PKPEmailTemplateController.php b/api/v1/emailTemplates/PKPEmailTemplateController.php index 726c8ca64c1..b0bfce34567 100644 --- a/api/v1/emailTemplates/PKPEmailTemplateController.php +++ b/api/v1/emailTemplates/PKPEmailTemplateController.php @@ -248,7 +248,7 @@ public function edit(Request $illuminateRequest): JsonResponse return response()->json($errors, Response::HTTP_BAD_REQUEST); } - Repo::emailTemplate()->edit($emailTemplate, $params); + Repo::emailTemplate()->edit($emailTemplate, $params, $requestContext->getId()); $emailTemplate = Repo::emailTemplate()->getByKey( // context ID is null if edited for the first time diff --git a/classes/components/forms/FieldEmailTemplateUserGroupSettings.php b/classes/components/forms/FieldEmailTemplateUserGroupSettings.php new file mode 100644 index 00000000000..a2609dd632f --- /dev/null +++ b/classes/components/forms/FieldEmailTemplateUserGroupSettings.php @@ -0,0 +1,33 @@ +<?php + +/** + * @file classes/components/form/FieldEmailTemplateUserGroupSettings.php + * + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky + * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. + * + * @class FieldEmailTemplateUserGroupSettings + * + * @ingroup classes_controllers_form + * + * @brief A component managing user groups assignable to an email template + */ + +namespace PKP\components\forms; + +class FieldEmailTemplateUserGroupSettings extends Field +{ + /** @copydoc Field::$component */ + public $component = 'field-email-template-user-group-settings'; + + /** + * @copydoc Field::getConfig() + */ + public function getConfig() + { + $config = parent::getConfig(); + + return $config; + } +} diff --git a/classes/components/forms/emailTemplate/EmailTemplateForm.php b/classes/components/forms/emailTemplate/EmailTemplateForm.php index 8a3c90f5b88..7e42bd2c211 100644 --- a/classes/components/forms/emailTemplate/EmailTemplateForm.php +++ b/classes/components/forms/emailTemplate/EmailTemplateForm.php @@ -15,6 +15,7 @@ namespace PKP\components\forms\emailTemplate; +use PKP\components\forms\FieldEmailTemplateUserGroupSettings; use PKP\components\forms\FieldPreparedContent; use PKP\components\forms\FieldText; use PKP\components\forms\FormComponent; @@ -46,6 +47,9 @@ public function __construct(string $action, array $locales) 'isMultilingual' => true, 'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist', 'plugins' => 'paste,link,lists', + ]))->addField(new FieldEmailTemplateUserGroupSettings('userGroupIds', [ + 'label' => __('workflow.userGroup.allowed'), + 'type' => 'checkbox' ])); } } diff --git a/classes/emailTemplate/DAO.php b/classes/emailTemplate/DAO.php index ff388308a58..66e1587c990 100644 --- a/classes/emailTemplate/DAO.php +++ b/classes/emailTemplate/DAO.php @@ -448,4 +448,33 @@ protected function getUniqueKey(EmailTemplate $emailTemplate): string return $key; } + + + public function updateTemplateAccessGroups(EmailTemplate $emailTemplate, array $newUserGroupIds, $contextId) + { + + // Delete old entries for user groups IDs not found in new list of user group IDs + DB::table('email_template_role_access') + ->where('email_key', $emailTemplate->getData('key')) + ->where('context_id', $contextId) + ->whereNotIn('user_group_id', $newUserGroupIds) + ->delete(); + + foreach ($newUserGroupIds as $id) { + DB::table('email_template_role_access') + ->updateOrInsert( + [ // The where conditions (keys that should match) + 'email_key' => $emailTemplate->getData('key'), + 'user_group_id' => $id, + 'context_id' => $contextId + ], + [ // The data to insert or update (values to set) + 'email_key' => $emailTemplate->getData('key'), + 'user_group_id' => $id, + 'context_id' => $contextId, + ] + ); + } + + } } diff --git a/classes/emailTemplate/Repository.php b/classes/emailTemplate/Repository.php index 1b8470fce32..41f1a2790c3 100644 --- a/classes/emailTemplate/Repository.php +++ b/classes/emailTemplate/Repository.php @@ -129,6 +129,20 @@ public function validate(?EmailTemplate $object, array $props, Context $context) }); } + // If groupIds were passed to limit email access, check that groups exists within the context + if (isset($props['userGroupIds'])) { + $validator->after(function () use ($validator, $props, $context) { + $existingGroupIds = Repo::userGroup()->getCollector() + ->filterByContextIds([$context->getId()]) + ->filterByUserGroupIds($props['userGroupIds'])->getIds()->toArray(); + + if (!empty(array_diff($existingGroupIds, $props['userGroupIds']))) { + $validator->errors()->add('userGroupIds', __('api.emailTemplates.404.userGroupIds')); + } + }); + + } + // Check for input from disallowed locales ValidatorFactory::allowedLocales($validator, $this->schemaService->getMultilingualProps($this->dao->schema), $allowedLocales); @@ -156,11 +170,14 @@ public function add(EmailTemplate $emailTemplate): string } /** @copydoc DAO::update() */ - public function edit(EmailTemplate $emailTemplate, array $params) + public function edit(EmailTemplate $emailTemplate, array $params, $contextId) { $newEmailTemplate = clone $emailTemplate; $newEmailTemplate->setAllData(array_merge($newEmailTemplate->_data, $params)); + $userGroupIds = $params['userGroupIds']; + unset($params['userGroupIds']); + Hook::call('EmailTemplate::edit', [$newEmailTemplate, $emailTemplate, $params]); if ($newEmailTemplate->getId()) { @@ -168,6 +185,10 @@ public function edit(EmailTemplate $emailTemplate, array $params) } else { $this->dao->insert($newEmailTemplate); } + + if($userGroupIds) { + $this->dao->updateTemplateAccessGroups($emailTemplate, $userGroupIds, $contextId); + } } /** @copydoc DAO::delete() */ diff --git a/classes/emailTemplate/maps/Schema.php b/classes/emailTemplate/maps/Schema.php index 52d1fe4571d..299dcb292c0 100644 --- a/classes/emailTemplate/maps/Schema.php +++ b/classes/emailTemplate/maps/Schema.php @@ -85,18 +85,11 @@ protected function mapByProperties(array $props, EmailTemplate $item, string $ma $mailableClass = $mailableClass ?? Repo::mailable()->getMailableByEmailTemplate($item); - if(!$mailableClass) { - error_log('TEMPLATE NAME ' . $item->getData('key')); - error_log('TEMPLATE ALTERNATE TO ' . $item->getData('alternateTo') ?? ''); - } - - // some mailable are not found during some operations such as performing a search for templates. So ensure mailable exist before using if($mailableClass) { $isUserGroupsAssignable = Repo::mailable()->isGroupsAssignableToTemplates($mailableClass); - if ($isUserGroupsAssignable) { - $output['assignableUserGroups'] = []; + if (!$isUserGroupsAssignable) { $output['assignedUserGroupIds'] = []; } else { // get roles for mailable @@ -105,16 +98,6 @@ protected function mapByProperties(array $props, EmailTemplate $item, string $ma $userGroups = []; $roleNames = Application::get()->getRoleNames(); - foreach (Repo::userGroup()->getByRoleIds($roles, $this->context->getId())->all() as $group) { - $roleId = $group->getRoleId(); - $userGroups[] = [ - 'id' => $group->getId(), - 'name' => $group->getLocalizedName(), - 'roleId' => $roleId, - 'roleName' => $roleNames[$roleId]]; - } - - $output['assignableUserGroups'] = $userGroups; // Get the current user groups assigned to the template $output['assignedUserGroupIds'] = Repo::emailTemplate()->getGroupsAssignedToTemplate($item->getData('key'), Application::get()->getRequest()->getContext()->getId()); } diff --git a/classes/mail/Repository.php b/classes/mail/Repository.php index b50c37ca634..4f69043a989 100644 --- a/classes/mail/Repository.php +++ b/classes/mail/Repository.php @@ -102,6 +102,21 @@ public function summarizeMailable(string $class): array $dataDescriptions = $class::getDataDescriptions(); ksort($dataDescriptions); + // get roles for mailable + $roles = $class::getFromRoleIds(); + // Get the groups for each role + $userGroups = []; + $roleNames = Application::get()->getRoleNames(); + + foreach (Repo::userGroup()->getByRoleIds($roles, Application::get()->getRequest()->getContext()->getId())->all() as $group) { + $roleId = $group->getRoleId(); + $userGroups[] = [ + 'id' => $group->getId(), + 'name' => $group->getLocalizedName(), + 'roleId' => $roleId, + 'roleName' => $roleNames[$roleId]]; + } + return [ '_href' => Application::get()->getRequest()->getDispatcher()->url( Application::get()->getRequest(), @@ -118,6 +133,7 @@ public function summarizeMailable(string $class): array 'supportsTemplates' => $class::getSupportsTemplates(), 'toRoleIds' => $class::getToRoleIds(), 'canAssignUserGroupToTemplates' => $this->isGroupsAssignableToTemplates($class), + 'assignableTemplateUserGroups' => $userGroups ]; } diff --git a/locale/en/api.po b/locale/en/api.po index e53bce54eed..d9f9f51f62d 100644 --- a/locale/en/api.po +++ b/locale/en/api.po @@ -343,3 +343,6 @@ msgstr "The provided section does not exist." msgid "api.publications.403.noEnabledIdentifiers" msgstr "Publication identifiers form is unavailable as there are no enabled Identifiers." + +msgid "api.emailTemplates.404.userGroupIds" +msgstr "One or more of the provided user groups does not exist." diff --git a/locale/en/common.po b/locale/en/common.po index e2f967f80eb..0c26a08a3c9 100644 --- a/locale/en/common.po +++ b/locale/en/common.po @@ -2343,3 +2343,6 @@ msgstr "Application running in sandbox mode." msgid "common.fromUntil" msgstr "{$from} – {$until}" + +msgid "workflow.userGroup.allowed" +msgstr "Allowed User Groups"