This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||||||
2 | |||||||
3 | declare(strict_types = 1); |
||||||
4 | |||||||
5 | namespace App\Model; |
||||||
6 | |||||||
7 | use App\Repository\PageAssessmentsRepository; |
||||||
8 | |||||||
9 | /** |
||||||
10 | * A PageAssessments is responsible for handling logic around |
||||||
11 | * processing page assessments of a given set of Pages on a Project. |
||||||
12 | * @see https://www.mediawiki.org/wiki/Extension:PageAssessments |
||||||
13 | */ |
||||||
14 | class PageAssessments extends Model |
||||||
15 | { |
||||||
16 | /** |
||||||
17 | * Namespaces in which there may be page assessments. |
||||||
18 | * @var int[] |
||||||
19 | * @todo Always JOIN on page_assessments and only display the data if it exists. |
||||||
20 | */ |
||||||
21 | public const SUPPORTED_NAMESPACES = [ |
||||||
22 | // Core namespaces |
||||||
23 | ...[0, 4, 6, 10, 12, 14], |
||||||
24 | // Custom namespaces |
||||||
25 | ...[ |
||||||
26 | 100, // Portal |
||||||
27 | 102, // WikiProject (T360774) |
||||||
28 | 108, // Book |
||||||
29 | 118, // Draft |
||||||
30 | 828, // Module |
||||||
31 | ], |
||||||
32 | ]; |
||||||
33 | |||||||
34 | /** @var array|null The assessments config. */ |
||||||
35 | protected ?array $config; |
||||||
36 | |||||||
37 | /** |
||||||
38 | * Create a new PageAssessments. |
||||||
39 | * @param PageAssessmentsRepository $repository |
||||||
40 | * @param Project $project |
||||||
41 | */ |
||||||
42 | public function __construct(PageAssessmentsRepository $repository, Project $project) |
||||||
43 | { |
||||||
44 | $this->repository = $repository; |
||||||
45 | $this->project = $project; |
||||||
46 | } |
||||||
47 | |||||||
48 | /** |
||||||
49 | * Get page assessments configuration for the Project and cache in static variable. |
||||||
50 | * @return string[][][]|null As defined in config/assessments.yaml, or false if none exists. |
||||||
51 | */ |
||||||
52 | public function getConfig(): ?array |
||||||
53 | { |
||||||
54 | if (!isset($this->config)) { |
||||||
55 | return $this->config = $this->repository->getConfig($this->project); |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
56 | } |
||||||
57 | |||||||
58 | return $this->config; |
||||||
59 | } |
||||||
60 | |||||||
61 | /** |
||||||
62 | * Is the given namespace supported in Page Assessments? |
||||||
63 | * @param int $nsId Namespace ID. |
||||||
64 | * @return bool |
||||||
65 | */ |
||||||
66 | public function isSupportedNamespace(int $nsId): bool |
||||||
67 | { |
||||||
68 | return $this->isEnabled() && in_array($nsId, self::SUPPORTED_NAMESPACES); |
||||||
69 | } |
||||||
70 | |||||||
71 | /** |
||||||
72 | * Does this project support page assessments? |
||||||
73 | * @return bool |
||||||
74 | */ |
||||||
75 | public function isEnabled(): bool |
||||||
76 | { |
||||||
77 | return (bool)$this->getConfig(); |
||||||
78 | } |
||||||
79 | |||||||
80 | /** |
||||||
81 | * Does this project have importance ratings through Page Assessments? |
||||||
82 | * @return bool |
||||||
83 | */ |
||||||
84 | public function hasImportanceRatings(): bool |
||||||
85 | { |
||||||
86 | $config = $this->getConfig(); |
||||||
87 | return isset($config['importance']); |
||||||
88 | } |
||||||
89 | |||||||
90 | /** |
||||||
91 | * Get the image URL of the badge for the given page assessment. |
||||||
92 | * @param string|null $class Valid classification for project, such as 'Start', 'GA', etc. Null for unknown. |
||||||
93 | * @param bool $filenameOnly Get only the filename, not the URL. |
||||||
94 | * @return string URL to image. |
||||||
95 | */ |
||||||
96 | public function getBadgeURL(?string $class, bool $filenameOnly = false): string |
||||||
97 | { |
||||||
98 | $config = $this->getConfig(); |
||||||
99 | |||||||
100 | if (isset($config['class'][$class])) { |
||||||
101 | $url = 'https://upload.wikimedia.org/wikipedia/commons/'.$config['class'][$class]['badge']; |
||||||
102 | } elseif (isset($config['class']['Unknown'])) { |
||||||
103 | $url = 'https://upload.wikimedia.org/wikipedia/commons/'.$config['class']['Unknown']['badge']; |
||||||
104 | } else { |
||||||
105 | $url = ""; |
||||||
106 | } |
||||||
107 | |||||||
108 | if ($filenameOnly) { |
||||||
109 | $parts = explode('/', $url); |
||||||
110 | return end($parts); |
||||||
111 | } |
||||||
112 | |||||||
113 | return $url; |
||||||
114 | } |
||||||
115 | |||||||
116 | /** |
||||||
117 | * Get the single overall assessment of the given page. |
||||||
118 | * @param Page $page |
||||||
119 | * @return string[]|false With keys 'value' and 'badge', or false if assessments are unsupported. |
||||||
120 | */ |
||||||
121 | public function getAssessment(Page $page) |
||||||
122 | { |
||||||
123 | if (!$this->isEnabled() || !$this->isSupportedNamespace($page->getNamespace())) { |
||||||
124 | return false; |
||||||
125 | } |
||||||
126 | |||||||
127 | $data = $this->repository->getAssessments($page, true); |
||||||
0 ignored issues
–
show
The method
getAssessments() does not exist on App\Repository\Repository . It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\PageAssessmentsRepository .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
128 | |||||||
129 | if (isset($data[0])) { |
||||||
130 | return $this->getClassFromAssessment($data[0]); |
||||||
131 | } |
||||||
132 | |||||||
133 | // 'Unknown' class. |
||||||
134 | return $this->getClassFromAssessment(['class' => '']); |
||||||
135 | } |
||||||
136 | |||||||
137 | /** |
||||||
138 | * Get assessments for the given Page. |
||||||
139 | * @param Page $page |
||||||
140 | * @return string[]|null null if unsupported, or array in the format of: |
||||||
141 | * [ |
||||||
142 | * 'assessment' => [ |
||||||
143 | * // overall assessment |
||||||
144 | * 'badge' => 'https://upload.wikimedia.org/wikipedia/commons/b/bc/Featured_article_star.svg', |
||||||
145 | * 'color' => '#9CBDFF', |
||||||
146 | * 'category' => 'Category:FA-Class articles', |
||||||
147 | * 'class' => 'FA', |
||||||
148 | * ] |
||||||
149 | * 'wikiprojects' => [ |
||||||
150 | * 'Biography' => [ |
||||||
151 | * 'assessment' => 'C', |
||||||
152 | * 'badge' => 'url', |
||||||
153 | * ], |
||||||
154 | * ... |
||||||
155 | * ], |
||||||
156 | * 'wikiproject_prefix' => 'Wikipedia:WikiProject_', |
||||||
157 | * ] |
||||||
158 | * @todo Add option to get ORES prediction. |
||||||
159 | */ |
||||||
160 | public function getAssessments(Page $page): ?array |
||||||
161 | { |
||||||
162 | if (!$this->isEnabled() || !$this->isSupportedNamespace($page->getNamespace())) { |
||||||
163 | return null; |
||||||
164 | } |
||||||
165 | |||||||
166 | $config = $this->getConfig(); |
||||||
167 | $data = $this->repository->getAssessments($page); |
||||||
168 | |||||||
169 | // Set the default decorations for the overall assessment. |
||||||
170 | // This will be replaced with the first valid class defined for any WikiProject. |
||||||
171 | $overallAssessment = array_merge(['class' => '???'], $config['class']['Unknown']); |
||||||
172 | $overallAssessment['badge'] = $this->getBadgeURL($overallAssessment['badge']); |
||||||
173 | |||||||
174 | $decoratedAssessments = []; |
||||||
175 | |||||||
176 | // Go through each raw assessment data from the database, and decorate them |
||||||
177 | // with the colours and badges as retrieved from the XTools assessments config. |
||||||
178 | foreach ($data as $assessment) { |
||||||
179 | $assessment['class'] = $this->getClassFromAssessment($assessment); |
||||||
180 | |||||||
181 | // Replace the overall assessment with the first non-empty assessment. |
||||||
182 | if ('???' === $overallAssessment['class'] && '???' !== $assessment['class']['value']) { |
||||||
183 | $overallAssessment['class'] = $assessment['class']['value']; |
||||||
184 | $overallAssessment['color'] = $assessment['class']['color']; |
||||||
185 | $overallAssessment['category'] = $assessment['class']['category']; |
||||||
186 | $overallAssessment['badge'] = $assessment['class']['badge']; |
||||||
187 | } |
||||||
188 | |||||||
189 | $assessment['importance'] = $this->getImportanceFromAssessment($assessment); |
||||||
190 | |||||||
191 | $decoratedAssessments[$assessment['wikiproject']] = $assessment; |
||||||
192 | } |
||||||
193 | |||||||
194 | // Don't show 'Unknown' assessment outside of the mainspace. |
||||||
195 | if (0 !== $page->getNamespace() && '???' === $overallAssessment['class']) { |
||||||
196 | return []; |
||||||
197 | } |
||||||
198 | |||||||
199 | return [ |
||||||
200 | 'assessment' => $overallAssessment, |
||||||
201 | 'wikiprojects' => $decoratedAssessments, |
||||||
202 | 'wikiproject_prefix' => $config['wikiproject_prefix'], |
||||||
203 | ]; |
||||||
204 | } |
||||||
205 | |||||||
206 | /** |
||||||
207 | * Get the class attributes for the given class value, as fetched from the config. |
||||||
208 | * @param string|null $classValue Such as 'FA', 'GA', 'Start', etc. |
||||||
209 | * @return string[] Attributes as fetched from the XTools assessments config. |
||||||
210 | */ |
||||||
211 | public function getClassAttrs(?string $classValue): array |
||||||
212 | { |
||||||
213 | $classValue = $classValue ?: 'Unknown'; |
||||||
214 | return $this->getConfig()['class'][$classValue] ?? $this->getConfig()['class']['Unknown']; |
||||||
215 | } |
||||||
216 | |||||||
217 | /** |
||||||
218 | * Get the properties of the assessment class, including: |
||||||
219 | * 'value' (class name in plain text), |
||||||
220 | * 'color' (as hex RGB), |
||||||
221 | * 'badge' (full URL to assessment badge), |
||||||
222 | * 'category' (wiki path to related class category). |
||||||
223 | * @param array $assessment |
||||||
224 | * @return array Decorated class assessment. |
||||||
225 | */ |
||||||
226 | private function getClassFromAssessment(array $assessment): array |
||||||
227 | { |
||||||
228 | $classValue = $assessment['class']; |
||||||
229 | |||||||
230 | // Use ??? as the presented value when the class is unknown or is not defined in the config |
||||||
231 | if ('Unknown' === $classValue || '' === $classValue || !isset($this->getConfig()['class'][$classValue])) { |
||||||
232 | return array_merge($this->getClassAttrs('Unknown'), [ |
||||||
233 | 'value' => '???', |
||||||
234 | 'badge' => $this->getBadgeURL('Unknown'), |
||||||
235 | ]); |
||||||
236 | } |
||||||
237 | |||||||
238 | // Known class. |
||||||
239 | $classAttrs = $this->getClassAttrs($classValue); |
||||||
240 | $class = [ |
||||||
241 | 'value' => $classValue, |
||||||
242 | 'color' => $classAttrs['color'], |
||||||
243 | 'category' => $classAttrs['category'], |
||||||
244 | ]; |
||||||
245 | |||||||
246 | // add full URL to badge icon |
||||||
247 | if ('' !== $classAttrs['badge']) { |
||||||
248 | $class['badge'] = $this->getBadgeURL($classValue); |
||||||
249 | } |
||||||
250 | |||||||
251 | return $class; |
||||||
252 | } |
||||||
253 | |||||||
254 | /** |
||||||
255 | * Get the properties of the assessment importance, including: |
||||||
256 | * 'value' (importance in plain text), |
||||||
257 | * 'color' (as hex RGB), |
||||||
258 | * 'weight' (integer, 0 is lowest importance), |
||||||
259 | * 'category' (wiki path to the related importance category). |
||||||
260 | * @param array $assessment |
||||||
261 | * @return array|null Decorated importance assessment. Null if importance could not be determined. |
||||||
262 | */ |
||||||
263 | private function getImportanceFromAssessment(array $assessment): ?array |
||||||
264 | { |
||||||
265 | $importanceValue = $assessment['importance']; |
||||||
266 | |||||||
267 | if ('' == $importanceValue && !isset($this->getConfig()['importance'])) { |
||||||
268 | return null; |
||||||
269 | } |
||||||
270 | |||||||
271 | // Known importance level. |
||||||
272 | $importanceUnknown = 'Unknown' === $importanceValue || '' === $importanceValue; |
||||||
273 | |||||||
274 | if ($importanceUnknown || !isset($this->getConfig()['importance'][$importanceValue])) { |
||||||
275 | $importanceAttrs = $this->getConfig()['importance']['Unknown']; |
||||||
276 | |||||||
277 | return array_merge($importanceAttrs, [ |
||||||
278 | 'value' => '???', |
||||||
279 | 'category' => $importanceAttrs['category'], |
||||||
280 | ]); |
||||||
281 | } else { |
||||||
282 | $importanceAttrs = $this->getConfig()['importance'][$importanceValue]; |
||||||
283 | return [ |
||||||
284 | 'value' => $importanceValue, |
||||||
285 | 'color' => $importanceAttrs['color'], |
||||||
286 | 'weight' => $importanceAttrs['weight'], // numerical weight for sorting purposes |
||||||
287 | 'category' => $importanceAttrs['category'], |
||||||
288 | ]; |
||||||
289 | } |
||||||
290 | } |
||||||
291 | } |
||||||
292 |