From 405e376643b23a2471cb3c400ae4d1650d689734 Mon Sep 17 00:00:00 2001 From: Taslan Graham Date: Mon, 18 Nov 2024 11:53:48 -0500 Subject: [PATCH] Code cleanup --- .../forms/FieldEmailTemplateUnrestricted.php | 5 ++- .../FieldEmailTemplateUserGroupSettings.php | 2 +- .../forms/emailTemplate/EmailTemplateForm.php | 12 +++--- .../users/reviewer/PKPReviewerGridHandler.php | 2 +- classes/decision/steps/Email.php | 3 +- classes/emailTemplate/DAO.php | 19 ++++++--- classes/emailTemplate/Repository.php | 26 ++++++------ classes/emailTemplate/maps/Schema.php | 21 +++++++++- classes/invitation/sections/Email.php | 12 ++++-- classes/mail/Repository.php | 40 ++----------------- .../EmailTemplateUserGroupAccessMigration.php | 4 +- .../I10403_EmailTemplateUserGroupAccess.php | 29 +++++++------- classes/plugins/Plugin.php | 2 +- .../grid/queries/QueriesGridHandler.php | 2 +- controllers/grid/queries/form/QueryForm.php | 1 - .../grid/users/reviewer/form/ReviewerForm.php | 12 +++++- .../StageParticipantGridHandler.php | 2 +- dtd/emailTemplates.dtd | 2 +- locale/en/admin.po | 2 +- schemas/emailTemplate.json | 18 +++++++++ tools/installEmailTemplate.php | 4 +- 21 files changed, 118 insertions(+), 102 deletions(-) diff --git a/classes/components/forms/FieldEmailTemplateUnrestricted.php b/classes/components/forms/FieldEmailTemplateUnrestricted.php index fe035b48d13..f16a2a65e3c 100644 --- a/classes/components/forms/FieldEmailTemplateUnrestricted.php +++ b/classes/components/forms/FieldEmailTemplateUnrestricted.php @@ -11,7 +11,7 @@ * * @ingroup classes_controllers_form * - * @brief A component to indicate if an email template is unrestricted, i.e accessible to all user groups within the associated mailable + * @brief A component to indicate if an email template is unrestricted, i.e accessible to all user groups. */ namespace PKP\components\forms; @@ -20,13 +20,14 @@ class FieldEmailTemplateUnrestricted extends Field { /** @copydoc Field::$component */ public $component = 'field-email-template-unrestricted'; - + public string $subNote = ''; /** * @copydoc Field::getConfig() */ public function getConfig() { $config = parent::getConfig(); + $config['subNote'] = $this->subNote; return $config; } } diff --git a/classes/components/forms/FieldEmailTemplateUserGroupSettings.php b/classes/components/forms/FieldEmailTemplateUserGroupSettings.php index a2609dd632f..e68cc07d5f4 100644 --- a/classes/components/forms/FieldEmailTemplateUserGroupSettings.php +++ b/classes/components/forms/FieldEmailTemplateUserGroupSettings.php @@ -11,7 +11,7 @@ * * @ingroup classes_controllers_form * - * @brief A component managing user groups assignable to an email template + * @brief A component managing user groups assigned to an email template */ namespace PKP\components\forms; diff --git a/classes/components/forms/emailTemplate/EmailTemplateForm.php b/classes/components/forms/emailTemplate/EmailTemplateForm.php index 02b8b96ba7f..9e6568599e0 100644 --- a/classes/components/forms/emailTemplate/EmailTemplateForm.php +++ b/classes/components/forms/emailTemplate/EmailTemplateForm.php @@ -48,16 +48,14 @@ public function __construct(string $action, array $locales) 'isMultilingual' => true, 'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist', 'plugins' => 'paste,link,lists', - ]))->addField( - new FieldEmailTemplateUnrestricted('isUnrestricted'), - [ - 'type' => 'checkbox' - ] - ) + ]))->addField(new FieldEmailTemplateUnrestricted('isUnrestricted', [ + 'type' => 'checkbox', + 'label' => __('admin.workflow.email.userGroup.assign.unrestricted'), + 'subNote' => __('admin.workflow.email.userGroup.unrestricted.template.note') + ])) ->addField(new FieldEmailTemplateUserGroupSettings('userGroupIds', [ 'type' => 'checkbox', 'label' => __('admin.workflow.email.userGroup.allowed'), - ])); } } diff --git a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php index 884784633fd..ecbb60fbb45 100644 --- a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php +++ b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php @@ -1048,7 +1048,7 @@ public function fetchTemplateBody(array $args, PKPRequest $request): ?JSONMessag }; $template = Repo::emailTemplate()->getByKey($context->getId(), $request->getUserVar('template')); - if (!$template) { + if (!$template || ! Repo::emailTemplate()->isTemplateAccessibleToUser($request->getUser(), $template, $context->getId())) { return null; } diff --git a/classes/decision/steps/Email.php b/classes/decision/steps/Email.php index ab3200d6a9d..aee87ada299 100644 --- a/classes/decision/steps/Email.php +++ b/classes/decision/steps/Email.php @@ -128,7 +128,7 @@ protected function getEmailTemplates(): array $emailTemplates = collect(); if ($this->mailable::getEmailTemplateKey()) { $emailTemplate = Repo::emailTemplate()->getByKey($context->getId(), $this->mailable::getEmailTemplateKey()); - if (Repo::emailTemplate()->isTemplateAccessibleToUser($request->getUser(), $emailTemplate, $context->getId())) { + if ($emailTemplate && Repo::emailTemplate()->isTemplateAccessibleToUser($request->getUser(), $emailTemplate, $context->getId())) { $emailTemplates->add($emailTemplate); } Repo::emailTemplate() @@ -142,7 +142,6 @@ protected function getEmailTemplates(): array }); } - return Repo::emailTemplate()->getSchemaMap()->mapMany($emailTemplates)->toArray(); } diff --git a/classes/emailTemplate/DAO.php b/classes/emailTemplate/DAO.php index d96373744d9..2b1f08c3971 100644 --- a/classes/emailTemplate/DAO.php +++ b/classes/emailTemplate/DAO.php @@ -235,13 +235,17 @@ public function getMainEmailTemplatesFilename() * skipping others * @param bool $skipExisting If true, do not install email templates * that already exist in the database + * @param bool $recordTemplateGroupAccess - If true, records the templates as unrestricted. + * By default, it is set to false to ensure compatibility with older processes (e.g., migrations) + * where the `email_template_user_group_access` table may not exist at the time of execution. * */ public function installEmailTemplates( string $templatesFile, array $locales = [], ?string $emailKey = null, - bool $skipExisting = false + bool $skipExisting = false, + $recordTemplateGroupAccess = false ): bool { $xmlDao = new XMLDAO(); $data = $xmlDao->parseStruct($templatesFile, ['email']); @@ -282,14 +286,17 @@ public function installEmailTemplates( } } - if (isset($attrs['isUnrestricted'])) { - if ($attrs['isUnrestricted'] !== '1' && $attrs['isUnrestricted'] !== '0') { - throw new Exception('Invalid value given for the `isUnrestricted` attribute on the ' . $attrs['key'] . ' template'); + if ($recordTemplateGroupAccess) { + // Default to true if `isUnrestricted` is not set. + $isUnrestricted = $attrs['isUnrestricted'] ?? '1'; + + if ($isUnrestricted !== '1' && $isUnrestricted !== '0') { + throw new Exception('Invalid value given for the `isUnrestricted` attribute on the ' . $attrs['key'] . ' template.'); } $contextIds = app()->get('context')->getIds(); foreach ($contextIds as $contextId) { - Repo::emailTemplate()->markTemplateAsUnrestricted($attrs['key'], (bool)$attrs['isUnrestricted'], $contextId); + Repo::emailTemplate()->markTemplateAsUnrestricted($attrs['key'], (bool)$isUnrestricted, $contextId); } } } @@ -420,7 +427,7 @@ public function installAlternateEmailTemplates(int $contextId, ?string $emailKey 'Tried to install email template as an alternate to `' . $alternateTo . '`, but no default template exists with this key. Installing ' . $alternateTo . ' email template first', E_USER_WARNING ); - $this->installEmailTemplates(Repo::emailTemplate()->dao->getMainEmailTemplatesFilename(), [], $alternateTo); + $this->installEmailTemplates(Repo::emailTemplate()->dao->getMainEmailTemplatesFilename(), [], $alternateTo, false, true); } DB::table($this->table)->insert([ diff --git a/classes/emailTemplate/Repository.php b/classes/emailTemplate/Repository.php index 81a9123d8ff..22fa421f533 100644 --- a/classes/emailTemplate/Repository.php +++ b/classes/emailTemplate/Repository.php @@ -130,7 +130,7 @@ 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 groupIds were passed to limit email access, check that the user groups exists within the context if (isset($props['userGroupIds'])) { $validator->after(function () use ($validator, $props, $context) { $existingGroupIds = Repo::userGroup()->getCollector() @@ -141,7 +141,6 @@ public function validate(?EmailTemplate $object, array $props, Context $context) $validator->errors()->add('userGroupIds', __('api.emailTemplates.404.userGroupIds')); } }); - } // Check for input from disallowed locales @@ -231,7 +230,7 @@ public function restoreDefaults($contextId): array /*** * Gets the IDs of the user groups assigned to an email template */ - public function getUserGroupsIdsAssignedToTemplate(string $templateKey, int $contextId): array + public function getAssignedGroupsIds(string $templateKey, int $contextId): array { return EmailTemplateAccessGroup::withEmailKey([$templateKey]) ->withContextId($contextId) @@ -248,7 +247,7 @@ public function isTemplateUnrestricted(string $templateKey, int $contextId): boo { return !!EmailTemplateAccessGroup::withEmailKey([$templateKey]) ->withContextId($contextId) - ->where('user_group_id', null) + ->whereNull('user_group_id') ->first(); } @@ -263,7 +262,7 @@ public function isTemplateAccessibleToUser(User $user, EmailTemplate $template, } $userUserGroups = Repo::userGroup()->userUserGroups($user->getId(), $contextId)->all(); - $templateUserGroups = $this->getUserGroupsIdsAssignedToTemplate($template->getData('key'), $contextId); + $templateUserGroups = $this->getAssignedGroupsIds($template->getData('key'), $contextId); foreach ($userUserGroups as $userGroup) { if (in_array($userGroup->getId(), $templateUserGroups)) { @@ -280,7 +279,7 @@ public function isTemplateAccessibleToUser(User $user, EmailTemplate $template, * @param Enumerable $templates List of EmailTemplates to filter. * @param User $user The user whose access level is used for filtering. * - * @return Collection Filtered list of EmailTemplate objects accessible to the user. + * @return Collection Filtered list of EmailTemplates accessible to the user. */ public function filterTemplatesByUserAccess(Enumerable $templates, User $user, int $contextId): Collection { @@ -298,7 +297,7 @@ public function filterTemplatesByUserAccess(Enumerable $templates, User $user, i /*** * Internal method used to assign user group IDs to an email template */ - private function _updateTemplateAccessGroups(EmailTemplate $emailTemplate, array $newUserGroupIds, int $contextId): void + private function updateTemplateAccessGroups(EmailTemplate $emailTemplate, array $newUserGroupIds, int $contextId): void { EmailTemplateAccessGroup::withEmailKey([$emailTemplate->getData('key')]) ->withContextId($contextId) @@ -307,13 +306,13 @@ private function _updateTemplateAccessGroups(EmailTemplate $emailTemplate, array foreach ($newUserGroupIds as $id) { EmailTemplateAccessGroup::updateOrCreate( [ - // The where conditions (keys that should match) + // The where conditions 'email_key' => $emailTemplate->getData('key'), 'user_group_id' => $id, 'context_id' => $contextId, ], [ - // The data to insert or update (values to set) + // The data to insert or update 'emailKey' => $emailTemplate->getData('key'), 'userGroupId' => $id, 'contextId' => $contextId, @@ -328,7 +327,7 @@ private function _updateTemplateAccessGroups(EmailTemplate $emailTemplate, array public function setEmailTemplateAccess(EmailTemplate $emailTemplate, int $contextId, ?array $userGroupIds, ?bool $isUnrestricted): void { if($userGroupIds !== null) { - $this->_updateTemplateAccessGroups($emailTemplate, $userGroupIds, $contextId); + $this->updateTemplateAccessGroups($emailTemplate, $userGroupIds, $contextId); } if($isUnrestricted !== null) { @@ -339,8 +338,7 @@ public function setEmailTemplateAccess(EmailTemplate $emailTemplate, int $contex /** * Mark an email template as unrestricted or not. - * An unrestricted email template is available to all user groups associated with the Roles linked to the mailable that the template belongs to. - * Mailable roles are stored in the $fromRoleIds property of a mailable + * An unrestricted email template is available to all user groups. */ public function markTemplateAsUnrestricted(string $emailKey, bool $isUnrestricted, int $contextId): void { @@ -348,13 +346,13 @@ public function markTemplateAsUnrestricted(string $emailKey, bool $isUnrestricte if ($isUnrestricted) { EmailTemplateAccessGroup::updateOrCreate( [ - // The where conditions (keys that should match) + // The where conditions 'email_key' => $emailKey, 'user_group_id' => null, 'context_id' => $contextId, ], [ - // The data to insert or update (values to set) + // The data to insert or update 'emailKey' => $emailKey, 'userGroupId' => null, 'contextId' => $contextId, diff --git a/classes/emailTemplate/maps/Schema.php b/classes/emailTemplate/maps/Schema.php index d4e7688cf83..a250048ceda 100644 --- a/classes/emailTemplate/maps/Schema.php +++ b/classes/emailTemplate/maps/Schema.php @@ -19,6 +19,7 @@ use PKP\core\PKPApplication; use PKP\emailTemplate\EmailTemplate; use PKP\services\PKPSchemaService; +use PKP\userGroup\UserGroup; class Schema extends \PKP\core\maps\Schema { @@ -82,8 +83,8 @@ public function summarizeMany(Enumerable $collection, string $mailableClass = nu protected function mapByProperties(array $props, EmailTemplate $item, string $mailableClass = null): array { $output = []; + $mailableClass = $mailableClass ?? Repo::mailable()->get($item->getData('key'), Application::get()->getRequest()->getContext()); - $mailableClass = $mailableClass ?? Repo::mailable()->getMailableByEmailTemplate($item); foreach ($props as $prop) { switch ($prop) { case '_href': @@ -99,11 +100,27 @@ protected function mapByProperties(array $props, EmailTemplate $item, string $ma break; case 'assignedUserGroupIds': if ($mailableClass && Repo::mailable()->isGroupsAssignableToTemplates($mailableClass)) { - $output['assignedUserGroupIds'] = Repo::emailTemplate()->getUserGroupsIdsAssignedToTemplate($item->getData('key'), Application::get()->getRequest()->getContext()->getId()); + $output['assignedUserGroupIds'] = Repo::emailTemplate()->getAssignedGroupsIds($item->getData('key'), Application::get()->getRequest()->getContext()->getId()); } else { $output['assignedUserGroupIds'] = []; } break; + case 'assignableTemplateUserGroups': + if($mailableClass && Repo::mailable()->isGroupsAssignableToTemplates($mailableClass)) { + $userGroups = collect(); + + Repo::userGroup()->getCollector() + ->filterByContextIds([Application::get()->getRequest()->getContext()->getId()]) + ->getMany()->each(fn (UserGroup $group) => $userGroups->add([ + 'id' => $group->getId(), + 'name' => $group->getLocalizedName() + ])); + + $output['assignableTemplateUserGroups'] = $userGroups; + } else { + $output['assignableTemplateUserGroups'] = []; + } + break; default: $output[$prop] = $item->getData($prop); break; diff --git a/classes/invitation/sections/Email.php b/classes/invitation/sections/Email.php index 614b1c05a8e..317336edfc3 100644 --- a/classes/invitation/sections/Email.php +++ b/classes/invitation/sections/Email.php @@ -64,7 +64,6 @@ public function getState(): stdClass /** * Get all email recipients for email composer - * @return array */ protected function getRecipientOptions(): array { @@ -84,7 +83,6 @@ protected function getRecipientOptions(): array /** * Get all email templates for email composer - * @return array */ protected function getEmailTemplates(): array { @@ -93,15 +91,21 @@ protected function getEmailTemplates(): array $emailTemplates = collect(); if ($this->mailable::getEmailTemplateKey()) { + $user = $request->getUser(); + $emailTemplate = Repo::emailTemplate()->getByKey($context->getId(), $this->mailable::getEmailTemplateKey()); - if ($emailTemplate) { + if ($emailTemplate && Repo::emailTemplate()->isTemplateAccessibleToUser($user, $emailTemplate, $context->getId())) { $emailTemplates->add($emailTemplate); } Repo::emailTemplate() ->getCollector($context->getId()) ->alternateTo([$this->mailable::getEmailTemplateKey()]) ->getMany() - ->each(fn (EmailTemplate $e) => $emailTemplates->add($e)); + ->each(function (EmailTemplate $e) use ($context, $user, $emailTemplates) { + if(Repo::emailTemplate()->isTemplateAccessibleToUser($user, $e, $context->getId())) { + $emailTemplates->add($e); + } + }); } return Repo::emailTemplate()->getSchemaMap()->mapMany($emailTemplates)->toArray(); diff --git a/classes/mail/Repository.php b/classes/mail/Repository.php index 2b0322a2553..c6b199e6c1e 100644 --- a/classes/mail/Repository.php +++ b/classes/mail/Repository.php @@ -18,7 +18,6 @@ use Illuminate\Support\Collection; use Illuminate\Support\Str; use PKP\context\Context; -use PKP\emailTemplate\EmailTemplate; use PKP\mail\mailables\DecisionNotifyOtherAuthors; use PKP\mail\mailables\EditReviewNotify; use PKP\mail\mailables\ReviewCompleteNotifyEditors; @@ -29,7 +28,6 @@ use PKP\mail\mailables\SubmissionSavedForLater; use PKP\mail\traits\Configurable; use PKP\plugins\Hook; -use PKP\userGroup\UserGroup; class Repository { @@ -103,23 +101,6 @@ 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(); - - $userGroups = collect(); - - Repo::userGroup()->getCollector() - ->filterByContextIds([Application::get()->getRequest()->getContext()->getId()]) - ->getMany()->each(fn (UserGroup $group) => $userGroups->add([ - 'id' => $group->getId(), - 'name' => $group->getLocalizedName(), - 'roleId' => $group->getRoleId(), - 'roleName' => $roleNames[$group->getRoleId()] - ])); - return [ '_href' => Application::get()->getRequest()->getDispatcher()->url( Application::get()->getRequest(), @@ -136,7 +117,6 @@ public function summarizeMailable(string $class): array 'supportsTemplates' => $class::getSupportsTemplates(), 'toRoleIds' => $class::getToRoleIds(), 'canAssignUserGroupToTemplates' => $this->isGroupsAssignableToTemplates($class), - 'assignableTemplateUserGroups' => $userGroups ]; } @@ -226,10 +206,11 @@ protected function isMailableConfigurable(string $class, Context $context): bool } - // Check if the templates of a given mailable can be assigned to specific groups /** + * Check if the templates of a given mailable can be assigned to specific groups + * * @param Mailable|string $mailable - Mailable class or qualified string referencing the class - */ + */ public function isGroupsAssignableToTemplates(Mailable|string $mailable): bool { return !in_array(Mailable::FROM_SYSTEM, $mailable::getFromRoleIds()); @@ -294,19 +275,4 @@ public function map(): Collection mailables\ValidateEmailSite::class, ]); } - - /** - * Gets the mailable for a given email template - * - * @param EmailTemplate $template - * - * Note: This does not discover/find mailbles defined within plugins - * - * @return string|null - Fully Qualified Class Name of a mailable - */ - public function getMailableByEmailTemplate(EmailTemplate $template): ?string - { - $emailKey = $template->getData('alternateTo') ?? $template->getData('key'); - return $this->map()->first(fn ($class) => $class::getEmailTemplateKey() === $emailKey); - } } diff --git a/classes/migration/install/EmailTemplateUserGroupAccessMigration.php b/classes/migration/install/EmailTemplateUserGroupAccessMigration.php index 3ee6533fcd7..cd6498eea3c 100644 --- a/classes/migration/install/EmailTemplateUserGroupAccessMigration.php +++ b/classes/migration/install/EmailTemplateUserGroupAccessMigration.php @@ -28,8 +28,8 @@ public function up(): void Schema::create('email_template_user_group_access', function (Blueprint $table) use ($contextDao) { $table->bigInteger('email_template_user_group_access_id')->autoIncrement()->comment('Primary key'); $table->string('email_key', 255)->comment("The email template's unique key"); - $table->bigInteger('context_id')->comment('Identifier for the context for which the user group assignment occurs.'); - $table->bigInteger('user_group_id')->nullable()->comment('The user group ID.'); + $table->bigInteger('context_id')->comment('The ID of the context for which the user group assignment is defined.'); + $table->bigInteger('user_group_id')->nullable()->comment('The user group ID. A null value indicates that the email template is accessible to all user groups.'); $table->foreign('context_id')->references($contextDao->primaryKeyColumn)->on($contextDao->tableName)->onDelete('cascade')->onDelete('cascade'); $table->foreign('user_group_id')->references('user_group_id')->on('user_groups')->onDelete('cascade')->onDelete('cascade'); diff --git a/classes/migration/upgrade/V3_6_0/I10403_EmailTemplateUserGroupAccess.php b/classes/migration/upgrade/V3_6_0/I10403_EmailTemplateUserGroupAccess.php index 860ebb26833..74b2c8faa7a 100644 --- a/classes/migration/upgrade/V3_6_0/I10403_EmailTemplateUserGroupAccess.php +++ b/classes/migration/upgrade/V3_6_0/I10403_EmailTemplateUserGroupAccess.php @@ -5,7 +5,6 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; -use PKP\context\Context; use PKP\emailTemplate\EmailTemplateAccessGroup; use PKP\migration\Migration; @@ -19,20 +18,18 @@ public function up(): void $contextDao = \APP\core\Application::getContextDAO(); Schema::create('email_template_user_group_access', function (Blueprint $table) use ($contextDao) { $table->bigInteger('email_template_user_group_access_id')->autoIncrement()->comment('Primary key'); - $table->string('email_key', 255)->comment("The email template's unique key."); - $table->bigInteger('context_id')->comment('Identifier for the context for which the user group assignment occurs.'); - $table->bigInteger('user_group_id')->nullable()->comment('The user group ID.'); + $table->string('email_key', 255)->comment("The email template's unique key"); + $table->bigInteger('context_id')->comment('The ID of the context for which the user group assignment is defined.'); + $table->bigInteger('user_group_id')->nullable()->comment('The user group ID. A null value indicates that the email template is accessible to all user groups.'); $table->foreign('context_id')->references($contextDao->primaryKeyColumn)->on($contextDao->tableName)->onDelete('cascade')->onDelete('cascade'); $table->foreign('user_group_id')->references('user_group_id')->on('user_groups')->onDelete('cascade')->onDelete('cascade'); }); - $contextIds = array_map(fn (Context $context) => $context->getId(), $contextDao->getAll()->toArray()); + $contextIds = app()->get('context')->getIds(); if (!empty($contextIds)) { - DB::table('email_template_user_group_access')->select('*')->get(); - - $defaultTemplateKeys = DB::table('email_templates_default_data')->select()->pluck('email_key')->all(); + $defaultTemplateKeys = DB::table('email_templates_default_data')->distinct()->select('email_key')->pluck('email_key')->all(); $alternateTemplates = DB::table('email_templates')->select(['context_id', 'email_key'])->get()->toArray(); $data = []; @@ -40,11 +37,11 @@ public function up(): void // Record any existing default templates as unrestricted for existing Contexts foreach ($defaultTemplateKeys as $defaultTemplateKey) { foreach ($contextIds as $contextId) { - $data[] = [ - 'email_key' => $defaultTemplateKey, - 'context_id' => $contextId, - 'user_group_id' => null - ]; + $data[$defaultTemplateKey] = + ['email_key' => $defaultTemplateKey, + 'context_id' => $contextId, + 'user_group_id' => null + ]; } } @@ -52,16 +49,18 @@ public function up(): void foreach ($alternateTemplates as $template) { foreach ($contextIds as $contextId) { if ($contextId === $template->context_id) { - $data[] = [ + $data[$template->email_key] = [ 'email_key' => $template->email_key, 'context_id' => $contextId, 'user_group_id' => null ]; + + break; } } } - EmailTemplateAccessGroup::insert($data); + EmailTemplateAccessGroup::insert(array_values($data)); } } diff --git a/classes/plugins/Plugin.php b/classes/plugins/Plugin.php index 62818b997c5..2bc36d07dd2 100644 --- a/classes/plugins/Plugin.php +++ b/classes/plugins/Plugin.php @@ -579,7 +579,7 @@ public function installEmailTemplates($hookName, $args) } // Localized data is needed by the email installation $this->addLocaleData(); - $status = Repo::emailTemplate()->dao->installEmailTemplates($this->getInstallEmailTemplatesFile(), $locales, null, true); + $status = Repo::emailTemplate()->dao->installEmailTemplates($this->getInstallEmailTemplatesFile(), $locales, null, true, true); if ($status === false) { // The template file seems to be invalid. diff --git a/controllers/grid/queries/QueriesGridHandler.php b/controllers/grid/queries/QueriesGridHandler.php index 37d2c15f2bf..4fc8adc3ad6 100644 --- a/controllers/grid/queries/QueriesGridHandler.php +++ b/controllers/grid/queries/QueriesGridHandler.php @@ -764,7 +764,7 @@ public function fetchTemplateBody(array $args, PKPRequest $request): JSONMessage $templateId = $request->getUserVar('template'); $context = $request->getContext(); $template = Repo::emailTemplate()->getByKey($context->getId(), $templateId); - if ($template) { + if ($template && Repo::emailTemplate()->isTemplateAccessibleToUser($request->getUser(), $template, $context->getId())) { $mailable = $this->getStageMailable($context, $this->getSubmission()); $mailable->sender($request->getUser()); $data = $mailable->getData(); diff --git a/controllers/grid/queries/form/QueryForm.php b/controllers/grid/queries/form/QueryForm.php index d4c64e652b2..6ae889e0e9a 100644 --- a/controllers/grid/queries/form/QueryForm.php +++ b/controllers/grid/queries/form/QueryForm.php @@ -274,7 +274,6 @@ public function fetch($request, $template = null, $display = false, $actionArgs $data = $mailable->getData(); $defaultTemplate = Repo::emailTemplate()->getByKey($context->getId(), $mailable::getEmailTemplateKey()); - // check to ensure user's user group has access to the templates for the mailable if(Repo::emailTemplate()->isTemplateAccessibleToUser($user, $defaultTemplate, $context->getId())) { $templateKeySubjectPairs[$mailable::getEmailTemplateKey()] = $defaultTemplate->getLocalizedData('name'); } diff --git a/controllers/grid/users/reviewer/form/ReviewerForm.php b/controllers/grid/users/reviewer/form/ReviewerForm.php index b677a3d2d7d..c695385ff25 100644 --- a/controllers/grid/users/reviewer/form/ReviewerForm.php +++ b/controllers/grid/users/reviewer/form/ReviewerForm.php @@ -486,18 +486,26 @@ protected function getMailable(): ReviewRequest */ protected function getEmailTemplates(): array { + $contextId = Application::get()->getRequest()->getContext()->getId(); + $defaultTemplate = Repo::emailTemplate()->getByKey( Application::get()->getRequest()->getContext()->getId(), ReviewRequest::getEmailTemplateKey() ); - $templateKeys = [ReviewRequest::getEmailTemplateKey() => $defaultTemplate->getLocalizedData('name')]; + + $user = Application::get()->getRequest()->getUser(); + if($defaultTemplate && Repo::emailTemplate()->isTemplateAccessibleToUser($user, $defaultTemplate, $contextId)) { + $templateKeys = [ReviewRequest::getEmailTemplateKey() => $defaultTemplate->getLocalizedData('name')]; + } $alternateTemplates = Repo::emailTemplate()->getCollector(Application::get()->getRequest()->getContext()->getId()) ->alternateTo([ReviewRequest::getEmailTemplateKey()]) ->getMany(); foreach ($alternateTemplates as $alternateTemplate) { - $templateKeys[$alternateTemplate->getData('key')] = $alternateTemplate->getLocalizedData('name'); + if (Repo::emailTemplate()->isTemplateAccessibleToUser($user, $alternateTemplate, $contextId)) { + $templateKeys[$alternateTemplate->getData('key')] = $alternateTemplate->getLocalizedData('name'); + } } return $templateKeys; diff --git a/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php b/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php index 5f026a935c3..d7afdd76a5c 100644 --- a/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php +++ b/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php @@ -582,7 +582,7 @@ public function fetchTemplateBody($args, $request) $templateKey = $request->getUserVar('template'); $context = $request->getContext(); $template = Repo::emailTemplate()->getByKey($context->getId(), $templateKey); - if ($template) { + if ($template && Repo::emailTemplate()->isTemplateAccessibleToUser($request->getUser(), $template, $context->getId())) { $submission = $this->getSubmission(); $mailable = $this->getStageMailable($context, $submission); $mailable->sender($request->getUser()); diff --git a/dtd/emailTemplates.dtd b/dtd/emailTemplates.dtd index 1c54a17fc2b..b1e4bce4afd 100644 --- a/dtd/emailTemplates.dtd +++ b/dtd/emailTemplates.dtd @@ -17,4 +17,4 @@ subject CDATA #REQUIRED body CDATA #REQUIRED alternateTo CDATA #IMPLIED - isUnrestricted (1 | 0) CDATA #IMPLIED> + isUnrestricted (1 | 0) #IMPLIED> diff --git a/locale/en/admin.po b/locale/en/admin.po index 9e758598aa2..043be9a3543 100644 --- a/locale/en/admin.po +++ b/locale/en/admin.po @@ -1022,4 +1022,4 @@ msgid "admin.workflow.email.userGroup.assign.unrestricted" msgstr "Mark as unrestricted" msgid "admin.workflow.email.userGroup.unrestricted.template.note" -msgstr "Unrestricted templates will be accessible to all user groups(listed below) associated with this template." +msgstr "Unrestricted templates will be accessible to all user groups." diff --git a/schemas/emailTemplate.json b/schemas/emailTemplate.json index 9681ab37fdd..d0140565b8b 100644 --- a/schemas/emailTemplate.json +++ b/schemas/emailTemplate.json @@ -81,6 +81,24 @@ "items": { "type": "integer" } + }, + "assignableTemplateUserGroups": { + "type": "array", + "description": "List of User Groups that can be assigned to the template", + "apiSummary": true, + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "apiSummary": true + }, + "name": { + "type": "integer", + "apiSummary": true + } + } + } } } } diff --git a/tools/installEmailTemplate.php b/tools/installEmailTemplate.php index 633af8971fa..02e03b10188 100644 --- a/tools/installEmailTemplate.php +++ b/tools/installEmailTemplate.php @@ -70,7 +70,9 @@ public function execute() Repo::emailTemplate()->dao->installEmailTemplates( Repo::emailTemplate()->dao->getMainEmailTemplatesFilename(), $locales, - $this->_emailKey + $this->_emailKey, + false, + true ); } }