Adding new Email type

Hi,

I am trying to customize application to my needs and while customizing existing templates are straightforward I encountered issues while trying to add new email notification for users.

There is currently ‘html/LU/user_setup_complete.php’
Which informs admins about newly registered users, but I would like to improve that and send email to user himself when he finishes the registration process.

I have found part responsible for sending email for admins:

private function createEmailCollection(User $userWhoCompletedSetup)
under AdminUserSetupCompleteEmailRedactor

Could you advise on the flow how current sending works as currently I can’t understand how the call to in this case html/LU/user_setup_complete.php happens.

I would appreciate help as I would have some reference to customize emails for company needs.

Actually wrote quite quick solution for myself like so:

<?php
declare(strict_types=1);

namespace App\Notification\Email\Redactor;

use App\Controller\Setup\SetupCompleteController;
use App\Model\Entity\User;
use App\Model\Table\UsersTable;
use App\Notification\Email\Email;
use App\Notification\Email\EmailCollection;
use App\Notification\Email\SubscribedEmailRedactorInterface;
use App\Notification\Email\SubscribedEmailRedactorTrait;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\I18n\FrozenTime;
use Cake\ORM\Query;
use Cake\ORM\TableRegistry;
use Passbolt\Locale\Service\LocaleService;
use Passbolt\Log\Model\Entity\EntityHistory;
use RuntimeException;

class AdminUserSetupCompleteEmailRedactor implements SubscribedEmailRedactorInterface
{
    use SubscribedEmailRedactorTrait;

    public const TEMPLATE_ADMIN = 'LU/user_setup_complete';
    public const TEMPLATE_USER = 'AN/user_self_message';

    private $usersTable;

    public function __construct(?UsersTable $usersTable = null)
    {
        $this->usersTable = $usersTable ?? TableRegistry::getTableLocator()->get('Users');
        if (!Configure::read('passbolt.plugins.log.enabled')) {
            throw new RuntimeException(sprintf('%s requires Passbolt/Log plugin', self::class));
        }
    }

    public function getSubscribedEvents(): array
    {
        return [
            SetupCompleteController::COMPLETE_SUCCESS_EVENT_NAME,
        ];
    }

    public function onSubscribedEvent(Event $event): EmailCollection
    {
        return $this->createEmailCollection($event->getData()['user']);
    }

    private function createEmailCollection(User $userWhoCompletedSetup)
    {
        $emailCollection = new EmailCollection();

        $userWhoCompletedSetup = $this->usersTable->loadInto(
            $userWhoCompletedSetup,
            [
                'Profiles',
                'EntitiesHistory' => function (Query $q) {
                    return $q->where(['crud' => EntityHistory::CRUD_CREATE]);
                },
                'EntitiesHistory.ActionLogs',
                'EntitiesHistory.ActionLogs.Users',
                'EntitiesHistory.ActionLogs.Users.Profiles',
            ]
        );

        if (!isset($userWhoCompletedSetup->entities_history) || !isset($userWhoCompletedSetup->entities_history[0])) {
            $invitedWhen = FrozenTime::now();
            $invitedBy = $userWhoCompletedSetup;
        } else {
            $createdHistory = $userWhoCompletedSetup->entities_history[0];
            $invitedBy = $createdHistory->action_log->user;
            $invitedWhen = $createdHistory->action_log->created;
            if (!isset($invitedBy)) {
                $invitedBy = $userWhoCompletedSetup;
            }
        }

        // Add user completion setup email
        $emailCollection->addEmail(
            $this->createEmail($userWhoCompletedSetup, $userWhoCompletedSetup, $invitedBy, $invitedWhen, self::TEMPLATE_USER)
        );

        // Create an email for every admin
        $admins = $this->usersTable->findAdmins()->find('locale');
        foreach ($admins as $admin) {
            $emailCollection->addEmail(
                $this->createEmail($admin, $userWhoCompletedSetup, $invitedBy, $invitedWhen, self::TEMPLATE_ADMIN)
            );
        }

        return $emailCollection;
    }

    private function createEmail(User $recipient, User $userCompletedSetup, User $invitedBy, FrozenTime $invitedWhen, string $template)
    {
        $profile = $userCompletedSetup->profile;

        if ($template == self::TEMPLATE_ADMIN) {
            $subject = (new LocaleService())->translateString(
                $recipient->locale,
                function () use ($profile) {
                    return __('{0} just activated their account on passbolt', $profile->first_name);
                }
            );
        } else {
            $subject = (new LocaleService())->translateString(
                $recipient->locale,
                function () {
                    return __('You just activated your account on passbolt');
                }
            );
        }

        return new Email(
            $recipient->username,
            $subject,
            [
                'title' => $subject,
                'body' => [
                    'user' => $userCompletedSetup,
                    'admin' => $recipient,
                    'invitedBy' => $invitedBy,
                    'invitedWhen' => $invitedWhen->timeAgoInWords(['accuracy' => 'day']),
                    'invitedByYou' => $invitedBy->id === $recipient->id,
                ],
            ],
            $template
        );
    }
}

Now my users get other email template for the same action, that they did successfully signed up
Hope it will be helpful for someone later.

On the other hand as controllers for such events exist it would be great if admins would be able to specify who should receive notifications on specific events happening, even on user per user level… And customizing content for emails from UI.

It would introduce more control for users and admins using application as main users of tool are companies sometimes they would want to add additional info in emails or links to specific documentations, etc.