x-tools /
xtools
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
Loading history...
|
|||||||
| 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
Loading history...
|
|||||||
| 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 |