Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
53.49% |
46 / 86 |
|
25.00% |
2 / 8 |
CRAP | |
0.00% |
0 / 1 |
AbstractBaseGenerator | |
53.49% |
46 / 86 |
|
25.00% |
2 / 8 |
142.58 | |
0.00% |
0 / 1 |
getConfigValue | |
33.33% |
4 / 12 |
|
0.00% |
0 / 1 |
5.67 | |||
getFileObject | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
getFileInfo | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
preprocessFileMetadata | |
57.14% |
8 / 14 |
|
0.00% |
0 / 1 |
8.83 | |||
getRevisionTimestamp | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
3.00 | |||
setFallbackImageIfEnabled | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
5 | |||
getWikiLogo | |
64.29% |
9 / 14 |
|
0.00% |
0 / 1 |
9.23 | |||
setModifiedPublishedTime | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
16 | * |
17 | * @file |
18 | */ |
19 | |
20 | declare( strict_types=1 ); |
21 | |
22 | namespace MediaWiki\Extension\WikiSEO\Generator; |
23 | |
24 | use Config; |
25 | use ConfigException; |
26 | use Exception; |
27 | use ExtensionRegistry; |
28 | use File; |
29 | use InvalidArgumentException; |
30 | use MediaWiki\Extension\WikiSEO\WikiSEO; |
31 | use MediaWiki\MediaWikiServices; |
32 | use OutputPage; |
33 | use PageImages\PageImages; |
34 | |
35 | abstract class AbstractBaseGenerator { |
36 | /** |
37 | * Usually the metadata from the constructor |
38 | * |
39 | * @var array |
40 | */ |
41 | protected $metadata; |
42 | |
43 | /** |
44 | * Valid Tags for this generator |
45 | * |
46 | * @var array |
47 | */ |
48 | protected $tags = []; |
49 | |
50 | /** |
51 | * @var OutputPage |
52 | */ |
53 | protected $outputPage; |
54 | |
55 | /** |
56 | * True if the wiki logo is used as the current fallback image |
57 | * |
58 | * @var bool |
59 | */ |
60 | protected $fallbackImageActive = false; |
61 | |
62 | /** |
63 | * The WikiSEO Config object |
64 | * |
65 | * @var Config |
66 | */ |
67 | private static $config; |
68 | |
69 | /** |
70 | * Loads a config value for a given key from the main config |
71 | * Returns null on if an ConfigException was thrown |
72 | * |
73 | * @param string $key The config key |
74 | * |
75 | * @return mixed|null |
76 | */ |
77 | protected function getConfigValue( string $key ) { |
78 | if ( self::$config === null ) { |
79 | self::$config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'WikiSEO' ); |
80 | } |
81 | |
82 | try { |
83 | $value = self::$config->get( $key ); |
84 | } catch ( ConfigException $e ) { |
85 | wfLogWarning( |
86 | sprintf( |
87 | 'Could not get config for "$wg%s". %s', $key, |
88 | $e->getMessage() |
89 | ) |
90 | ); |
91 | $value = null; |
92 | } |
93 | |
94 | return $value; |
95 | } |
96 | |
97 | /** |
98 | * Returns a file object by name, throws InvalidArgument if not found. |
99 | * |
100 | * @param string $name Filename |
101 | * |
102 | * @return File |
103 | * |
104 | * @throws InvalidArgumentException |
105 | */ |
106 | protected function getFileObject( string $name ): File { |
107 | // This should remove the namespace if present |
108 | $nameSplit = explode( ':', $name ); |
109 | $name = array_pop( $nameSplit ) ?? ''; |
110 | |
111 | $title = MediaWikiServices::getInstance()->getTitleFactory()->makeTitle( |
112 | NS_FILE, |
113 | $name |
114 | ); |
115 | |
116 | $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $title ); |
117 | |
118 | if ( $file === false ) { |
119 | throw new InvalidArgumentException( sprintf( 'File %s not found.', $name ) ); |
120 | } |
121 | |
122 | return $file; |
123 | } |
124 | |
125 | /** |
126 | * Generate file metadata from a local file object |
127 | * |
128 | * @param File $file |
129 | * |
130 | * @return array |
131 | */ |
132 | protected function getFileInfo( File $file ): array { |
133 | $cacheHash = |
134 | '?version=' . md5( $file->getTimestamp() . $file->getWidth() . $file->getHeight() ); |
135 | $width = $file->getWidth(); |
136 | $height = $file->getHeight(); |
137 | $image = WikiSEO::protocolizeUrl( $file->getFullUrl(), $this->outputPage->getRequest() ); |
138 | |
139 | return [ |
140 | 'url' => $image . $cacheHash, |
141 | 'width' => $width, |
142 | 'height' => $height, |
143 | ]; |
144 | } |
145 | |
146 | /** |
147 | * If the metadata key 'image' is set, try to get file info of the local file |
148 | */ |
149 | protected function preprocessFileMetadata(): void { |
150 | if ( isset( $this->metadata['image'] ) ) { |
151 | try { |
152 | $file = $this->getFileObject( $this->metadata['image'] ); |
153 | $info = $this->getFileInfo( $file ); |
154 | |
155 | $this->metadata['image'] = $info['url']; |
156 | $this->metadata['image_width'] = $info['width']; |
157 | $this->metadata['image_height'] = $info['height']; |
158 | } catch ( InvalidArgumentException $e ) { |
159 | // File does not exist. |
160 | // Maybe the user has set an URL, should we do something? |
161 | } |
162 | } else { |
163 | if ( $this->getConfigValue( 'WikiSeoDisableLogoFallbackImage' ) === true ) { |
164 | return; |
165 | } |
166 | |
167 | try { |
168 | $logo = $this->getWikiLogo(); |
169 | |
170 | if ( $logo !== false ) { |
171 | $this->metadata['image'] = $logo; |
172 | $this->fallbackImageActive = true; |
173 | } |
174 | } catch ( Exception $e ) { |
175 | // We do nothing |
176 | } |
177 | } |
178 | } |
179 | |
180 | /** |
181 | * Tries to load the current revision timestamp for the page or current timestamp if nothing |
182 | * could be found. |
183 | * |
184 | * @return bool|string |
185 | */ |
186 | protected function getRevisionTimestamp() { |
187 | $timestamp = $this->outputPage->getRevisionTimestamp(); |
188 | |
189 | // No cached timestamp, load it from the database |
190 | if ( $timestamp === null ) { |
191 | $timestamp = |
192 | MediaWikiServices::getInstance() |
193 | ->getRevisionLookup() |
194 | ->getKnownCurrentRevision( |
195 | $this->outputPage->getTitle(), |
196 | $this->outputPage->getRevisionId() |
197 | ); |
198 | |
199 | if ( $timestamp === false ) { |
200 | $timestamp = wfTimestampNow(); |
201 | } else { |
202 | $timestamp = $timestamp->getTimestamp() ?? wfTimestampNow(); |
203 | } |
204 | } |
205 | |
206 | return wfTimestamp( TS_ISO_8601, $timestamp ); |
207 | } |
208 | |
209 | /** |
210 | * Sets a fallback image if no '|image=' parameter was given AND the page does not have a page image |
211 | * |
212 | * @return void |
213 | */ |
214 | protected function setFallbackImageIfEnabled(): void { |
215 | $continue = true; |
216 | |
217 | if ( ExtensionRegistry::getInstance()->isLoaded( 'PageImages' ) ) { |
218 | $continue = PageImages::getPageImage( $this->outputPage->getTitle() ) === false; |
219 | } |
220 | |
221 | if ( !isset( $this->metadata['image'] ) && $continue ) { |
222 | $defaultImage = $this->getConfigValue( 'WikiSeoDefaultImage' ); |
223 | |
224 | if ( $defaultImage !== null ) { |
225 | $this->metadata['image'] = $defaultImage; |
226 | } |
227 | } |
228 | } |
229 | |
230 | /** |
231 | * Tries to return the expanded url to the wiki's logo based on $wgLogos or $wgLogo |
232 | * If nothing was found, or no url could be generated, returns false |
233 | * |
234 | * @return string|false |
235 | * @throws Exception |
236 | */ |
237 | protected function getWikiLogo() { |
238 | $logos = $this->getConfigValue( 'Logos' ); |
239 | if ( $logos === false || !is_array( $logos ) ) { |
240 | $logo = $this->getConfigValue( 'Logo' ); |
241 | |
242 | if ( is_string( $logo ) ) { |
243 | return MediaWikiServices::getInstance()->getUrlUtils()->expand( $logo ) ?? false; |
244 | } |
245 | |
246 | return false; |
247 | } |
248 | |
249 | foreach ( $logos as $path ) { |
250 | if ( !is_string( $path ) ) { |
251 | continue; |
252 | } |
253 | |
254 | $parts = explode( '.', $path ); |
255 | $ext = array_pop( $parts ) ?? ''; |
256 | if ( in_array( strtolower( $ext ), [ 'jpg', 'jpeg', 'png', 'gif', 'webp' ], true ) ) { |
257 | return MediaWikiServices::getInstance()->getUrlUtils()->expand( $path ) ?? false; |
258 | } |
259 | } |
260 | |
261 | return false; |
262 | } |
263 | |
264 | /** |
265 | * Sets modified_time and published_time metadata, if it has not been disabled by setting their values to '-' |
266 | * |
267 | * @return void |
268 | */ |
269 | protected function setModifiedPublishedTime(): void { |
270 | if ( !isset( $this->metadata['modified_time'] ) && ( $this->metadata['modified_time'] ?? '' ) !== '-' ) { |
271 | $this->metadata['modified_time'] = $this->getRevisionTimestamp(); |
272 | } elseif ( ( $this->metadata['modified_time'] ?? '' ) === '-' ) { |
273 | unset( $this->metadata['modified_time'] ); |
274 | } |
275 | |
276 | if ( !isset( $this->metadata['published_time'] ) && isset( $this->metadata['modified_time'] ) ) { |
277 | $this->metadata['published_time'] = $this->metadata['modified_time']; |
278 | } |
279 | } |
280 | } |