Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SitemapHandlerBase
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 5
56
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getOffset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 generateResponseSpec
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 getResponseHeaderSettings
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 getXml
n/a
0 / 0
n/a
0 / 0
0
 getResponseSchema
n/a
0 / 0
n/a
0 / 0
0
 getResponseExample
n/a
0 / 0
n/a
0 / 0
0
1<?php
2
3namespace MediaWiki\Rest\Handler;
4
5use MediaWiki\Config\Config;
6use MediaWiki\Language\Language;
7use MediaWiki\Language\LanguageConverterFactory;
8use MediaWiki\MainConfigNames;
9use MediaWiki\Page\SitemapGenerator;
10use MediaWiki\Permissions\PermissionManager;
11use MediaWiki\Rest\Handler;
12use MediaWiki\Rest\HeaderParser\HttpDate;
13use MediaWiki\Rest\ResponseHeaders;
14use MediaWiki\Rest\StringStream;
15use Wikimedia\Timestamp\ConvertibleTimestamp;
16
17abstract class SitemapHandlerBase extends Handler {
18    protected bool $enabled;
19    /** @var int */
20    protected $indexSize;
21    /** @var int */
22    protected $sitemapSize;
23    /** @var int */
24    protected $expiry;
25
26    protected function __construct(
27        Config $config,
28        protected LanguageConverterFactory $languageConverterFactory,
29        protected Language $contLang,
30        private PermissionManager $permissionManager,
31    ) {
32        $apiConf = $config->get( MainConfigNames::SitemapApiConfig );
33        $this->enabled = $apiConf['enabled'] ?? false;
34        $this->indexSize = $apiConf['sitemapsPerIndex'] ?? 50_000;
35        $variants = SitemapGenerator::getVariants( $contLang, $languageConverterFactory );
36        $this->sitemapSize = (int)( ( $apiConf['pagesPerSitemap'] ?? 10_000 ) / ( count( $variants ) + 1 ) );
37        $this->expiry = $apiConf['expiry'] ?? 3600;
38    }
39
40    /**
41     * @param int $indexId
42     * @param int $pageId
43     * @return int
44     */
45    protected function getOffset( $indexId, $pageId ) {
46        return $this->sitemapSize * ( $indexId * $this->indexSize + $pageId );
47    }
48
49    protected function generateResponseSpec( string $method ): array {
50        $spec = parent::generateResponseSpec( $method );
51
52        $spec['200']['content']['application/xml'] = [
53            'schema' => $this->getResponseSchema(),
54            'example' => $this->getResponseExample(),
55        ];
56
57        return $spec;
58    }
59
60    /** @inheritDoc */
61    public function execute() {
62        if ( !$this->enabled ) {
63            return $this->getResponseFactory()
64                ->createHttpError( 404, [ 'message' => 'Disabled by configuration' ] );
65        }
66        if ( !$this->permissionManager->isEveryoneAllowed( 'read' ) ) {
67            return $this->getResponseFactory()
68                ->createHttpError( 403, [ 'message' => 'This site has restricted access' ] );
69        }
70        $xml = $this->getXml();
71        $response = $this->getResponseFactory()->create();
72        $response->setHeader( 'Content-Type', 'application/xml; charset=utf-8' );
73        $response->setHeader(
74            'Expires',
75            HttpDate::format(
76                ( $this->getLastModified() ?? ConvertibleTimestamp::time() ) + $this->expiry
77            )
78        );
79        $response->setHeader( 'Cache-Control', 'public' );
80        $response->setBody( new StringStream( $xml ) );
81        return $response;
82    }
83
84    /** @inheritDoc */
85    public function getResponseHeaderSettings(): array {
86        return array_merge(
87            parent::getResponseHeaderSettings(),
88            [
89                ResponseHeaders::CONTENT_TYPE => ResponseHeaders::RESPONSE_HEADER_DEFINITIONS[
90                    ResponseHeaders::CONTENT_TYPE
91                ],
92                ResponseHeaders::EXPIRES => ResponseHeaders::RESPONSE_HEADER_DEFINITIONS[
93                    ResponseHeaders::EXPIRES
94                ],
95            ]
96        );
97    }
98
99    abstract protected function getXml(): string;
100
101    abstract protected function getResponseSchema(): array;
102
103    abstract protected function getResponseExample(): string;
104}