Passed
Pull Request — master (#6503)
by
unknown
07:59
created

AccessUrlController::formatReport()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 4
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Controller;
8
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Entity\AccessUrl;
11
use Doctrine\ORM\EntityManagerInterface;
12
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\Routing\Annotation\Route;
16
use Symfony\Component\Security\Http\Attribute\IsGranted;
17
use Symfony\Contracts\Translation\TranslatorInterface;
18
19
#[Route('/access-url')]
20
class AccessUrlController extends AbstractController
21
{
22
    public function __construct(
23
        private readonly TranslatorInterface $translator,
24
        private readonly EntityManagerInterface $em,
25
    ) {}
26
27
    #[IsGranted('ROLE_ADMIN')]
28
    #[Route('/users/import', name: 'chamilo_core_access_url_users_import', methods: ['GET', 'POST'])]
29
    public function importUsers(Request $request): Response
30
    {
31
        $report = [];
32
33
        if ($request->isMethod('POST') && $request->files->has('csv_file')) {
34
            $file = $request->files->get('csv_file')->getPathname();
35
            $handle = fopen($file, 'r');
36
            $lineNumber = 0;
37
38
            while (($data = fgetcsv($handle, 1000, ',')) !== false) {
39
                $lineNumber++;
40
41
                if ($lineNumber === 1 && strtolower(trim($data[0])) === 'username') {
42
                    continue; // Skip header
43
                }
44
45
                if (count($data) < 2) {
46
                    $report[] = $this->formatReport('alert-circle', 'Line %s: invalid format. Two columns expected.', [$lineNumber]);
47
                    continue;
48
                }
49
50
                [$username, $url] = array_map('trim', $data);
51
52
                if (!$username || !$url) {
53
                    $report[] = $this->formatReport('alert-circle', 'Line %s: missing username or URL.', [$lineNumber]);
54
                    continue;
55
                }
56
57
                // Normalize URL
58
                if (!str_starts_with($url, 'http')) {
59
                    $url = 'https://' . $url;
60
                }
61
                if (!str_ends_with($url, '/')) {
62
                    $url .= '/';
63
                }
64
65
                $user = $this->em->getRepository(User::class)->findOneBy(['username' => $username]);
66
                if (!$user) {
67
                    $report[] = $this->formatReport('close-circle', "Line %s: user '%s' not found.", [$lineNumber, $username]);
68
                    continue;
69
                }
70
71
                $accessUrl = $this->em->getRepository(AccessUrl::class)->findOneBy(['url' => $url]);
72
                if (!$accessUrl) {
73
                    $report[] = $this->formatReport('close-circle', "Line %s: URL '%s' not found.", [$lineNumber, $url]);
74
                    continue;
75
                }
76
77
                if ($accessUrl->hasUser($user)) {
78
                    $report[] = $this->formatReport('information-outline', "Line %s: user '%s' is already assigned to '%s'.", [$lineNumber, $username, $url]);
79
                } else {
80
                    $accessUrl->addUser($user);
81
                    $this->em->persist($accessUrl);
82
                    $report[] = $this->formatReport('check-circle', "Line %s: user '%s' successfully assigned to '%s'.", [$lineNumber, $username, $url]);
83
                }
84
            }
85
86
            fclose($handle);
87
            $this->em->flush();
88
        }
89
90
        return $this->render('@ChamiloCore/AccessUrl/import_users.html.twig', [
91
            'report' => $report,
92
            'title' => $this->translator->trans('Assign users to URLs from CSV'),
93
        ]);
94
    }
95
96
    #[IsGranted('ROLE_ADMIN')]
97
    #[Route('/users/remove', name: 'chamilo_core_access_url_users_remove', methods: ['GET', 'POST'])]
98
    public function removeUsers(Request $request): Response
99
    {
100
        $report = [];
101
102
        if ($request->isMethod('POST') && $request->files->has('csv_file')) {
103
            $file = $request->files->get('csv_file')->getPathname();
104
            $handle = fopen($file, 'r');
105
            $lineNumber = 0;
106
107
            while (($data = fgetcsv($handle, 1000, ',')) !== false) {
108
                $lineNumber++;
109
110
                if ($lineNumber === 1 && strtolower(trim($data[0])) === 'username') {
111
                    continue; // Skip header
112
                }
113
114
                [$username, $url] = array_map('trim', $data);
115
116
                if (!$username || !$url) {
117
                    $report[] = $this->formatReport('alert-circle', 'Line %s: empty fields.', [$lineNumber]);
118
                    continue;
119
                }
120
121
                if (!str_starts_with($url, 'http')) {
122
                    $url = 'https://' . $url;
123
                }
124
                if (!str_ends_with($url, '/')) {
125
                    $url .= '/';
126
                }
127
128
                $user = $this->em->getRepository(User::class)->findOneBy(['username' => $username]);
129
                if (!$user) {
130
                    $report[] = $this->formatReport('close-circle', "Line %s: user '%s' not found.", [$lineNumber, $username]);
131
                    continue;
132
                }
133
134
                $accessUrl = $this->em->getRepository(AccessUrl::class)->findOneBy(['url' => $url]);
135
                if (!$accessUrl) {
136
                    $report[] = $this->formatReport('close-circle', "Line %s: URL '%s' not found.", [$lineNumber, $url]);
137
                    continue;
138
                }
139
140
                foreach ($accessUrl->getUsers() as $rel) {
141
                    if ($rel->getUser()->getId() === $user->getId()) {
142
                        $this->em->remove($rel);
143
                        $report[] = $this->formatReport('account-remove-outline', "Line %s: user '%s' removed from '%s'.", [$lineNumber, $username, $url]);
144
                        continue 2;
145
                    }
146
                }
147
148
                $report[] = $this->formatReport('alert-circle', 'Line %s: no relation found between user and URL.', [$lineNumber]);
149
            }
150
151
            fclose($handle);
152
            $this->em->flush();
153
        }
154
155
        return $this->render('@ChamiloCore/AccessUrl/remove_users.html.twig', [
156
            'report' => $report,
157
            'title' => $this->translator->trans('Remove users from URLs from CSV')
158
        ]);
159
    }
160
161
    private function formatReport(string $icon, string $message, array $params): string
162
    {
163
        $text = vsprintf($this->translator->trans($message), $params);
164
        return sprintf('<i class="mdi mdi-%s text-base me-1"></i> %s', $icon, $text);
165
    }
166
}
167