chamilo/chamilo-lms

View on GitHub
src/CoreBundle/Command/ProcessUserDataRequestsCommand.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

/* For licensing terms, see /license.txt */

declare(strict_types=1);

namespace Chamilo\CoreBundle\Command;

use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper;
use Chamilo\CoreBundle\Settings\SettingsManager;
use Database;
use DateInterval;
use DateTime;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManager;
use MessageManager;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use UserManager;

class ProcessUserDataRequestsCommand extends Command
{
    /**
     * @var string
     */
    protected static $defaultName = 'app:process-user-data-requests';

    public function __construct(
        private readonly Connection $connection,
        private readonly AccessUrlHelper $accessUrlHelper,
        private readonly SettingsManager $settingsManager,
        private readonly MailerInterface $mailer,
        private readonly EntityManager $em,
        private readonly TranslatorInterface $translator
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->setDescription('Process user data requests for personal data actions.')
            ->addOption('debug', null, InputOption::VALUE_NONE, 'Enable debug mode')
            ->setHelp('This command processes user data requests that require administrative action.')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        Database::setManager($this->em);

        $container = $this->getApplication()->getKernel()->getContainer();
        Container::setContainer($container);

        $io = new SymfonyStyle($input, $output);
        $debug = $input->getOption('debug');

        if ($debug) {
            $io->note('Debug mode activated');
        }

        $defaultSenderId = 1;
        $accessUrl = $this->accessUrlHelper->getCurrent();
        $numberOfDays = 7;
        $date = new DateTime();
        $date->sub(new DateInterval('P'.$numberOfDays.'D'));
        $dateToString = $date->format('Y-m-d H:i:s');

        if ($accessUrl) {
            $message = $this->processUrlData($accessUrl->getId(), $defaultSenderId, $dateToString, $io, $debug);
            if ($debug) {
                $io->success($message);
            }
        }

        return Command::SUCCESS;
    }

    private function processUrlData(
        int $accessUrlId,
        int $defaultSenderId,
        string $dateToString,
        SymfonyStyle $io,
        bool $debug
    ): string {
        $sql = '
            SELECT u.id, v.updated_at
            FROM user AS u
            INNER JOIN extra_field_values AS v ON u.id = v.item_id
            WHERE (v.field_id IN (:deleteLegal, :deleteAccount))
            AND v.field_value = 1
            AND u.active <> :userSoftDeleted
            AND v.updated_at < :dateToString
        ';

        if ($this->accessUrlHelper->isMultiple()) {
            $sql .= ' AND EXISTS (
                        SELECT 1 FROM access_url_rel_user rel
                        WHERE u.id = rel.user_id
                        AND rel.access_url_id = :accessUrlId)';
        }

        $extraFields = UserManager::createDataPrivacyExtraFields();
        $params = [
            'deleteLegal' => $extraFields['delete_legal'],
            'deleteAccount' => $extraFields['delete_account_extra_field'],
            'userSoftDeleted' => User::SOFT_DELETED,
            'dateToString' => $dateToString,
            'accessUrlId' => $accessUrlId,
        ];

        $result = $this->connection->fetchAllAssociative($sql, $params);
        $usersToBeProcessed = [];

        foreach ($result as $user) {
            $usersToBeProcessed[] = $user;
        }

        if (empty($usersToBeProcessed)) {
            return "No users waiting for data actions for Access URL ID: {$accessUrlId}";
        }

        return $this->processUsers($usersToBeProcessed, $defaultSenderId, $io, $debug);
    }

    private function processUsers(
        array $users,
        int $defaultSenderId,
        SymfonyStyle $io,
        bool $debug
    ): string {
        $administrator = [
            'completeName' => $this->settingsManager->getSetting('admin.administrator_name'),
            'email' => $this->settingsManager->getSetting('admin.administrator_email'),
        ];

        $rootweb = $this->settingsManager->getSetting('platform.institution_url');
        $link = $rootweb.'/main/admin/user_list_consent.php';
        $subject = $this->translator->trans('A user is waiting for an action about his/her personal data request');
        $email = $this->settingsManager->getSetting('profile.data_protection_officer_email');
        $message = '';

        foreach ($users as $user) {
            $userId = $user['id'];
            $userInfo = $this->connection->fetchAssociative('SELECT * FROM user WHERE id = ?', [$userId]);
            $userInfo['complete_name'] = $userInfo['firstname'].' '.$userInfo['lastname'];
            $userInfo['complete_name_with_username'] = $userInfo['complete_name'].' ('.$userInfo['username'].')';

            if (!$userInfo) {
                continue;
            }

            $content = $this->translator->trans(
                'The user %name% is waiting for an action about his/her personal data request. To manage personal data requests you can follow this link: %link%',
                ['%name%' => $userInfo['complete_name'], '%link%' => $link]
            );

            if ($email) {
                $emailMessage = (new TemplatedEmail())
                    ->from($administrator['email'])
                    ->to($email)
                    ->subject($subject)
                    ->html($content)
                ;

                $this->mailer->send($emailMessage);
            } else {
                MessageManager::sendMessageToAllAdminUsers($defaultSenderId, $subject, $content);
            }

            $date = (new DateTime($user['updated_at']))->format('Y-m-d H:i:s');
            $message .= \sprintf(
                "User %s is waiting for an action since %s \n",
                $userInfo['complete_name_with_username'],
                $date
            );

            if ($debug) {
                $io->note("Processed user {$userInfo['complete_name']} with ID: {$userId}");
            }
        }

        return $message;
    }
}