Passed
Pull Request — master (#6703)
by
unknown
08:09
created

PluginHelper::resolveTitle()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 4
nop 1
dl 0
loc 27
rs 9.7
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Helpers;
8
9
use Chamilo\CoreBundle\Entity\AccessUrl;
10
use Chamilo\CoreBundle\Entity\Plugin as PluginEntity;
11
use Chamilo\CoreBundle\Repository\AccessUrlRelPluginRepository;
12
use Chamilo\CoreBundle\Repository\PluginRepository;
13
use Event;
14
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
15
16
final class PluginHelper
17
{
18
    /** @var array<string,string> normalized_title => OriginalTitle */
19
    private array $titleMap = [];
20
21
    public function __construct(
22
        private readonly ParameterBagInterface $parameterBag,
23
        private readonly AccessUrlRelPluginRepository $pluginRelRepo,
24
        private readonly PluginRepository $pluginRepo,
25
        private readonly AccessUrlHelper $accessUrlHelper,
26
    ) {
27
        $this->titleMap = [];
28
    }
29
30
    private static function normalize(string $s): string
31
    {
32
        return strtolower(preg_replace('/[^a-z0-9]/i', '', $s));
33
    }
34
35
    private function buildTitleMap(): void
36
    {
37
        if (!empty($this->titleMap)) {
38
            return;
39
        }
40
        $all = $this->pluginRepo->findAll();
41
        foreach ($all as $p) {
42
            /** @var PluginEntity $p */
43
            $title = $p->getTitle();
44
            $norm  = self::normalize($title);
45
            $this->titleMap[$norm] = $title;
46
        }
47
    }
48
49
    private function resolveTitle(string $name): ?string
50
    {
51
        $this->buildTitleMap();
52
53
        $norm = self::normalize($name);
54
        if (isset($this->titleMap[$norm])) {
55
            return $this->titleMap[$norm];
56
        }
57
58
        $studly = implode('', array_map('ucfirst', preg_split('/[^a-z0-9]+/i', $name)));
59
        $candidates = array_unique([
60
            $name,
61
            ucfirst(strtolower($name)),
62
            strtolower($name),
63
            strtoupper($name),
64
            $studly,
65
            self::normalize($studly)
66
        ]);
67
68
        foreach ($candidates as $cand) {
69
            $normCand = self::normalize((string) $cand);
70
            if (isset($this->titleMap[$normCand])) {
71
                return $this->titleMap[$normCand];
72
            }
73
        }
74
75
        return null;
76
    }
77
78
    public function loadLegacyPlugin(string $pluginName): ?object
79
    {
80
        $projectDir = $this->parameterBag->get('kernel.project_dir');
81
82
        $cands = array_unique([
83
            $pluginName,
84
            implode('', array_map('ucfirst', preg_split('/[^a-z0-9]+/i', $pluginName))),
85
        ]);
86
87
        foreach ($cands as $cand) {
88
            $pluginPath  = $projectDir.'/public/plugin/'.$cand .'/src/'.$cand .'.php';
89
            $pluginClass = $cand;
90
91
            if (!file_exists($pluginPath)) {
92
                continue;
93
            }
94
            if (!class_exists($pluginClass)) {
95
                require_once $pluginPath;
96
            }
97
            if (class_exists($pluginClass) && method_exists($pluginClass, 'create')) {
98
                return $pluginClass::create();
99
            }
100
        }
101
102
        return null;
103
    }
104
105
    public function getPluginSetting(string $pluginName, string $settingKey): mixed
106
    {
107
        $plugin = $this->loadLegacyPlugin($pluginName);
108
109
        if (!$plugin || !method_exists($plugin, 'get')) {
110
            return null;
111
        }
112
113
        return $plugin->get($settingKey);
114
    }
115
116
    public function isPluginEnabled(string $pluginName): bool
117
    {
118
        $accessUrl = $this->accessUrlHelper->getCurrent();
119
        if (!$accessUrl instanceof AccessUrl) {
120
            return false;
121
        }
122
123
        $realTitle = $this->resolveTitle($pluginName);
124
        if (null === $realTitle) {
125
            return false;
126
        }
127
128
        $plugin = $this->pluginRepo->findOneBy(['title' => $realTitle]);
129
        if (!$plugin || !$plugin->isInstalled()) {
130
            return false;
131
        }
132
133
        $rel = $this->pluginRelRepo->findOneByPluginName($realTitle, $accessUrl->getId());
134
        return $rel && $rel->isActive();
135
    }
136
137
    public function shouldBlockAccessByPositioning(?int $userId, int $courseId, ?int $sessionId): bool
138
    {
139
        if (!$this->isPluginEnabled('Positioning') || !$userId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $userId of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
140
            return false;
141
        }
142
143
        $plugin = $this->loadLegacyPlugin('Positioning');
144
145
        if (!$plugin || 'true' !== $plugin->get('block_course_if_initial_exercise_not_attempted')) {
146
            return false;
147
        }
148
149
        $initialData = $plugin->getInitialExercise($courseId, $sessionId);
150
151
        if (empty($initialData['exercise_id'])) {
152
            return false;
153
        }
154
155
        $results = Event::getExerciseResultsByUser(
0 ignored issues
show
Bug introduced by
The method getExerciseResultsByUser() does not exist on Event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

155
        /** @scrutinizer ignore-call */ 
156
        $results = Event::getExerciseResultsByUser(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
156
            $userId,
157
            (int) $initialData['exercise_id'],
158
            $courseId,
159
            $sessionId
160
        );
161
162
        return empty($results);
163
    }
164
165
    /**
166
     * Return the whole configuration array for a plugin in the current Access URL,
167
     * or null if not found.
168
     */
169
    public function getPluginConfiguration(string $pluginName): ?array
170
    {
171
        $accessUrl = $this->accessUrlHelper->getCurrent();
172
        if (!$accessUrl instanceof AccessUrl) {
173
            return null;
174
        }
175
176
        $realTitle = $this->resolveTitle($pluginName);
177
        if (null === $realTitle) {
178
            return null;
179
        }
180
181
        $rel = $this->pluginRelRepo->findOneByPluginName($realTitle, $accessUrl->getId());
182
        if (!$rel) {
183
            return null;
184
        }
185
186
        $cfg = $rel->getConfiguration();
187
        return \is_array($cfg) ? $cfg : null;
188
    }
189
190
    /**
191
     * Get a single configuration value from the plugin configuration JSON.
192
     * Tries both the plain key ($key) and the legacy-prefixed key ($pluginName.'_'.$key).
193
     * Falls back to legacy plugin::get($key) if available.
194
     */
195
    public function getPluginConfigValue(string $pluginName, string $key, mixed $default = null): mixed
196
    {
197
        // Special case for legacy callers expecting "tool_enable"
198
        if ($key === 'tool_enable') {
199
            return $this->isPluginEnabled($pluginName) ? 'true' : 'false';
200
        }
201
202
        $cfg = $this->getPluginConfiguration($pluginName);
203
204
        if (\is_array($cfg)) {
205
            // try plain key
206
            if (\array_key_exists($key, $cfg)) {
207
                return $cfg[$key];
208
            }
209
            // try legacy-prefixed key (some migrations removed this, but keep BC)
210
            $prefixed = $pluginName . '_' . $key;
211
            if (\array_key_exists($prefixed, $cfg)) {
212
                return $cfg[$prefixed];
213
            }
214
        }
215
216
        // Fallback to legacy plugin object if present
217
        $legacy = $this->loadLegacyPlugin($pluginName);
218
        if ($legacy && \method_exists($legacy, 'get')) {
219
            return $legacy->get($key) ?? $default;
220
        }
221
222
        return $default;
223
    }
224
}
225