Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.74% covered (success)
94.74%
72 / 76
66.67% covered (warning)
66.67%
6 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialCreateAbstract
94.74% covered (success)
94.74%
72 / 76
66.67% covered (warning)
66.67%
6 / 9
24.08
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRestriction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescription
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isListed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 userCanExecute
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 displayNotAvailableError
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getTargetTitle
85.71% covered (warning)
85.71%
12 / 14
0.00% covered (danger)
0.00%
0 / 1
7.14
 execute
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2/**
3 * WikiLambda Special:CreateAbstract page
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\Special;
12
13use MediaWiki\Content\ContentHandlerFactory;
14use MediaWiki\Exception\ErrorPageError;
15use MediaWiki\Extension\WikiLambda\AbstractContent\AbstractContentEditPageTrait;
16use MediaWiki\Extension\WikiLambda\PageTitle\PageTitleBuilder;
17use MediaWiki\Extension\WikiLambda\WikidataEntityLookup;
18use MediaWiki\Extension\WikiLambda\WikiLambdaServices;
19use MediaWiki\Revision\RevisionStore;
20use MediaWiki\SpecialPage\SpecialPage;
21use MediaWiki\Title\Title;
22use MediaWiki\User\User;
23
24class SpecialCreateAbstract extends SpecialPage {
25    use AbstractContentEditPageTrait;
26
27    public function __construct(
28        private readonly RevisionStore $revisionStore,
29        private readonly ContentHandlerFactory $contentHandlerFactory,
30        private readonly WikidataEntityLookup $entityLookup
31    ) {
32        parent::__construct( 'CreateAbstract' );
33    }
34
35    /** @inheritDoc */
36    public function getRestriction(): string {
37        return 'wikilambda-abstract-create';
38    }
39
40    /**
41     * @inheritDoc
42     */
43    protected function getGroupName() {
44        // Triggers use of message specialpages-group-abstractwiki
45        return 'abstractwiki';
46    }
47
48    /**
49     * @inheritDoc
50     */
51    public function getDescription() {
52        return $this->msg( 'wikilambda-abstract-special-create' );
53    }
54
55    /**
56     * @inheritDoc
57     */
58    public function isListed() {
59        return WikiLambdaServices::getMode()->isAbstract();
60    }
61
62    /**
63     * @inheritDoc
64     */
65    public function userCanExecute( User $user ) {
66        $block = $user->getBlock();
67
68        return (
69            // Does the user have the relevant right (wikilambda-abstract-create, as set above)?
70            parent::userCanExecute( $user ) &&
71            // If they're blocked in some way, is it 'site-wide' and so prohibits all editing?
72            ( !$block || !$block->isSitewide() )
73        );
74    }
75
76    /**
77     * Output an error message telling the user that the Abstract Mode is not enabled
78     *
79     * @throws ErrorPageError
80     */
81    private function displayNotAvailableError(): never {
82        $titleMessage = $this->msg( 'wikilambda-abstract-special-create-not-enabled-title' );
83        $errorMessage = $this->msg( 'wikilambda-abstract-special-create-not-enabled' );
84        throw new ErrorPageError( $titleMessage, $errorMessage );
85    }
86
87    /**
88     * Get target title from subpage and parameters:
89     * 1. If there is subpage, that will contain namespace:qid, or just qid for main ns
90     *       * Special:CreateAbstract/Abstract_Wikipedia:Q42
91     *       * Special:CreateAbstract/Q42
92     * 2. Else, look into request parameters qid, which might contain title or ns:qid
93     *    * Special:CreateAbstract?qid=Q42
94     *    * Special:CreateAbstract?qid=Abstract_Wikipedia:Q42
95     * We ignore oldid= parameter in Special:CreateAbstract page
96     *
97     * @param string|null $subpage
98     * @return Title
99     */
100    private function getTargetTitle( $subpage ): Title {
101        $request = $this->getRequest();
102
103        // Get qid, if any, either in subpage or as the url parameter 'qid'
104        $rawTitle = $subpage ?: $request->getVal( 'qid' ) ?: '';
105
106        // Get primary namespace: first of WikiLambdaAbstractNamespaces or NS_MAIN
107        $namespaces = $this->getContext()->getConfig()->get( 'WikiLambdaAbstractNamespaces' );
108        $namespaceIds = is_array( $namespaces ) && ( count( $namespaces ) > 0 )
109            ? array_map( 'intval', array_keys( $namespaces ) )
110            : [ NS_MAIN ];
111        $primaryNamespace = $namespaceIds[0];
112
113        // Parse as full title
114        $title = Title::newFromText( $rawTitle );
115
116        // Invalid or empty raw title; return empty title in primary NS
117        if ( !$title ) {
118            return Title::makeTitle( $primaryNamespace, '' );
119        }
120
121        // If namespace is not a configured abstract namespace, overwrite it with primary AW ns
122        $ns = $title->getNamespace();
123        if ( !in_array( $ns, $namespaceIds, true ) ) {
124            return Title::makeTitle( $primaryNamespace, $title->getText() );
125        }
126
127        return $title;
128    }
129
130    /**
131     * @inheritDoc
132     */
133    public function execute( $subpage ) {
134        // Throw ErrorPageError if Abtract Mode is not enabled
135        if ( !WikiLambdaServices::getMode()->isAbstract() ) {
136            $this->displayNotAvailableError();
137        }
138
139        // Throw PermissionsError if user doesn't have the necessary rights
140        if ( !$this->userCanExecute( $this->getUser() ) ) {
141            $this->displayRestrictionError();
142        }
143
144        $output = $this->getOutput();
145        $context = $this->getContext();
146
147        $userLangCode = $context->getLanguage()->getCode();
148
149        $title = $this->getTargetTitle( $subpage );
150
151        // If title exists, we redirect to Special:ViewAbstract/<lang>/<title>
152        if ( $title->exists() ) {
153            $redirectSubpage = $userLangCode . '/' . $title->getPrefixedText();
154            $redirectTitle = SpecialPage::getTitleFor( 'ViewAbstract', $redirectSubpage );
155            $redirectParams = [ 'created' => 1 ];
156            $redirectURL = $redirectTitle->getFullURL( $redirectParams );
157            $output->redirect( $redirectURL );
158            return;
159        }
160
161        // If a QID was supplied but it doesn't exist on Wikidata, redirect to a clean create page
162        // If WikibaseClient is not loaded, we skip this check and let the create page proceed.
163        $qid = $title->getText();
164        if ( $qid !== '' && $this->entityLookup->wikidataItemExists( $qid ) === false ) {
165            $output->redirect( SpecialPage::getTitleFor( 'CreateAbstract' )->getFullURL() );
166            return;
167        }
168
169        // If title doesn't exist, start building the page:
170        $this->setHeaders();
171
172        // Generate Abstract Content payload and pass data through JS config vars.
173        // At this point we know we are creating a new Abstract Content object,
174        // so no need to check for revision deletion returned flag.
175        $this->generateAbstractContentPayload(
176            $context,
177            $this->revisionStore,
178            $this->contentHandlerFactory,
179            $output,
180            $title
181        );
182
183        // Always set the editpage-header wrapper structure so JS can later update
184        // the title span and add the copyable QID chip once a Wikidata entity is chosen.
185        $lang = $context->getLanguage();
186        $qid = $qid !== '' ? $qid : null;
187        $titleMsg = $qid
188            ? $this->msg( 'wikilambda-abstract-special-create-qid' )->params( $qid )->text()
189            : $this->msg( 'wikilambda-abstract-special-create' )->text();
190        $output->setPageTitle(
191            PageTitleBuilder::createAbstractEditPageTitle(
192                $titleMsg,
193                $lang->getCode(),
194                $lang->getDir(),
195                $qid
196            )
197        );
198
199        $output->addModules( [ 'ext.wikilambda.app', 'mediawiki.special' ] );
200        $output->addModuleStyles( [ 'ext.wikilambda.special.styles' ] );
201        // Load edit page header styles so the heading matches edit mode
202        $output->addModuleStyles( [ 'ext.wikilambda.editpage.styles' ] );
203
204        $output->addWikiMsg(
205            'wikilambda-abstract-special-create-intro',
206            'Special:MyLanguage/Abstract Wikipedia:List_of_policies_and_guidelines'
207        );
208
209        $this->addHelpLink( 'Help:How_to_create_an_article' );
210    }
211}