Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.31% covered (success)
92.31%
24 / 26
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
MainSlotRoleHandler
92.31% covered (success)
92.31%
24 / 26
75.00% covered (warning)
75.00%
3 / 4
11.06
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 supportsArticleCount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isAllowedModel
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultModel
88.24% covered (warning)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 1
8.10
1<?php
2/**
3 * This file is part of MediaWiki.
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Revision;
10
11use MediaWiki\Content\IContentHandlerFactory;
12use MediaWiki\Content\UnknownContentModelException;
13use MediaWiki\HookContainer\HookContainer;
14use MediaWiki\HookContainer\HookRunner;
15use MediaWiki\Linker\LinkTarget;
16use MediaWiki\Page\PageIdentity;
17use MediaWiki\Page\PageReference;
18use MediaWiki\Title\TitleFactory;
19
20/**
21 * A SlotRoleHandler for the main slot. While most slot roles serve a specific purpose and
22 * thus typically exhibit the same behaviour on all pages, the main slot is used for different
23 * things in different pages, typically depending on the namespace, a "file extension" in
24 * the page name, or the content model of the slot's content.
25 *
26 * MainSlotRoleHandler implements some of the per-namespace and per-model behavior that was
27 * supported prior to MediaWiki Version 1.33.
28 *
29 * @since 1.33
30 */
31class MainSlotRoleHandler extends SlotRoleHandler {
32
33    /**
34     * @var string[] A mapping of namespaces to content models.
35     * @see $wgNamespaceContentModels
36     */
37    private $namespaceContentModels;
38
39    /** @var IContentHandlerFactory */
40    private $contentHandlerFactory;
41
42    /** @var HookRunner */
43    private $hookRunner;
44
45    /** @var TitleFactory */
46    private $titleFactory;
47
48    /**
49     * @param string[] $namespaceContentModels A mapping of namespaces to content models,
50     *        typically from $wgNamespaceContentModels.
51     * @param IContentHandlerFactory $contentHandlerFactory
52     * @param HookContainer $hookContainer
53     * @param TitleFactory $titleFactory
54     */
55    public function __construct(
56        array $namespaceContentModels,
57        IContentHandlerFactory $contentHandlerFactory,
58        HookContainer $hookContainer,
59        TitleFactory $titleFactory
60    ) {
61        parent::__construct( SlotRecord::MAIN, CONTENT_MODEL_WIKITEXT );
62        $this->namespaceContentModels = $namespaceContentModels;
63        $this->contentHandlerFactory = $contentHandlerFactory;
64        $this->hookRunner = new HookRunner( $hookContainer );
65        $this->titleFactory = $titleFactory;
66    }
67
68    /** @inheritDoc */
69    public function supportsArticleCount() {
70        return true;
71    }
72
73    /**
74     * @param string $model
75     * @param PageIdentity $page
76     *
77     * @return bool
78     * @throws UnknownContentModelException
79     */
80    public function isAllowedModel( $model, PageIdentity $page ) {
81        $title = $this->titleFactory->newFromPageIdentity( $page );
82        $handler = $this->contentHandlerFactory->getContentHandler( $model );
83
84        return $handler->canBeUsedOn( $title );
85    }
86
87    /**
88     * @param LinkTarget|PageReference $page
89     *
90     * @return string
91     */
92    public function getDefaultModel( $page ) {
93        // NOTE: this method must not rely on $title->getContentModel() directly or indirectly,
94        //       because it is used to initialize the mContentModel member.
95
96        $ns = $page->getNamespace();
97        $model = $this->namespaceContentModels[$ns] ?? null;
98
99        // Hook can determine default model
100        if ( $page instanceof PageReference ) {
101            $title = $this->titleFactory->newFromPageReference( $page );
102        } else {
103            $title = $this->titleFactory->newFromLinkTarget( $page );
104        }
105        // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
106        if ( !$this->hookRunner->onContentHandlerDefaultModelFor( $title, $model ) && $model !== null ) {
107            return $model;
108        }
109
110        // Code can only exist in these namespaces
111        if ( $ns === NS_MEDIAWIKI ||
112            // Code in the user namespace can only exist on subpages
113            ( $ns === NS_USER && str_contains( $title->getDBkey(), '/' ) )
114        ) {
115            // Could this page contain code based on the title?
116            if ( preg_match( '/\.(css|js|json|vue)$/', $title->getDBkey(), $m ) ) {
117                return match ( $m[1] ) {
118                    'css' => CONTENT_MODEL_CSS,
119                    'js' => CONTENT_MODEL_JAVASCRIPT,
120                    'json' => CONTENT_MODEL_JSON,
121                    'vue' => CONTENT_MODEL_VUE,
122                };
123            }
124        }
125
126        // Is this wikitext, according to $wgNamespaceContentModels or the DefaultModelFor hook?
127        return $model ?? CONTENT_MODEL_WIKITEXT;
128    }
129
130}