Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
54.44% covered (warning)
54.44%
49 / 90
12.50% covered (danger)
12.50%
1 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractBaseGenerator
54.44% covered (warning)
54.44%
49 / 90
12.50% covered (danger)
12.50%
1 / 8
150.81
0.00% covered (danger)
0.00%
0 / 1
 getConfigValue
33.33% covered (danger)
33.33%
4 / 12
0.00% covered (danger)
0.00%
0 / 1
5.67
 getFileObject
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 getFileInfo
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 preprocessFileMetadata
57.14% covered (warning)
57.14%
8 / 14
0.00% covered (danger)
0.00%
0 / 1
8.83
 getRevisionTimestamp
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
3.00
 setFallbackImageIfEnabled
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
6.03
 getWikiLogo
64.29% covered (warning)
64.29%
9 / 14
0.00% covered (danger)
0.00%
0 / 1
9.23
 setModifiedPublishedTime
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
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
20declare( strict_types=1 );
21
22namespace MediaWiki\Extension\WikiSEO\Generator;
23
24use Config;
25use ConfigException;
26use Exception;
27use ExtensionRegistry;
28use File;
29use InvalidArgumentException;
30use MediaWiki\Extension\WikiSEO\WikiSEO;
31use MediaWiki\MediaWikiServices;
32use OutputPage;
33use PageImages\PageImages;
34
35abstract 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        $title = MediaWikiServices::getInstance()->getTitleFactory()->makeTitleSafe(
108            NS_FILE,
109            $name
110        );
111
112        if ( $title === null ) {
113            throw new InvalidArgumentException( sprintf( 'Title %s is invalid.', $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 ) {
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 ) {
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            $services = MediaWikiServices::getInstance();
219            if ( $services->hasService( 'PageImages.PageImages' ) ) {
220                $continue = $services->getService( 'PageImages.PageImages' )
221                    ->getImage( $this->outputPage->getTitle() ) === null;
222            } else {
223                $continue = PageImages::getPageImage( $this->outputPage->getTitle() ) === false;
224            }
225        }
226
227        if ( !isset( $this->metadata['image'] ) && $continue ) {
228            $defaultImage = $this->getConfigValue( 'WikiSeoDefaultImage' );
229
230            if ( $defaultImage !== null ) {
231                $this->metadata['image'] = $defaultImage;
232            }
233        }
234    }
235
236    /**
237     * Tries to return the expanded url to the wiki's logo based on $wgLogos or $wgLogo
238     * If nothing was found, or no url could be generated, returns false
239     *
240     * @return string|false
241     * @throws Exception
242     */
243    protected function getWikiLogo() {
244        $logos = $this->getConfigValue( 'Logos' );
245        if ( $logos === false || !is_array( $logos ) ) {
246            $logo = $this->getConfigValue( 'Logo' );
247
248            if ( is_string( $logo ) ) {
249                return MediaWikiServices::getInstance()->getUrlUtils()->expand( $logo ) ?? false;
250            }
251
252            return false;
253        }
254
255        foreach ( $logos as $path ) {
256            if ( !is_string( $path ) ) {
257                continue;
258            }
259
260            $parts = explode( '.', $path );
261            $ext = array_pop( $parts ) ?? '';
262            if ( in_array( strtolower( $ext ), [ 'jpg', 'jpeg', 'png', 'gif', 'webp' ], true ) ) {
263                return MediaWikiServices::getInstance()->getUrlUtils()->expand( $path ) ?? false;
264            }
265        }
266
267        return false;
268    }
269
270    /**
271     * Sets modified_time and published_time metadata, if it has not been disabled by setting their values to '-'
272     *
273     * @return void
274     */
275    protected function setModifiedPublishedTime(): void {
276        if ( !isset( $this->metadata['modified_time'] ) && ( $this->metadata['modified_time'] ?? '' ) !== '-' ) {
277            $this->metadata['modified_time'] = $this->getRevisionTimestamp();
278        } elseif ( ( $this->metadata['modified_time'] ?? '' ) === '-' ) {
279            unset( $this->metadata['modified_time'] );
280        }
281
282        if ( !isset( $this->metadata['published_time'] ) && isset( $this->metadata['modified_time'] ) ) {
283            $this->metadata['published_time'] = $this->metadata['modified_time'];
284        }
285    }
286}