Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
94.74% |
72 / 76 |
|
66.67% |
6 / 9 |
CRAP | |
0.00% |
0 / 1 |
| SpecialCreateAbstract | |
94.74% |
72 / 76 |
|
66.67% |
6 / 9 |
24.08 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getRestriction | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getDescription | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| isListed | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| userCanExecute | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
| displayNotAvailableError | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| getTargetTitle | |
85.71% |
12 / 14 |
|
0.00% |
0 / 1 |
7.14 | |||
| execute | |
100.00% |
48 / 48 |
|
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 | |
| 11 | namespace MediaWiki\Extension\WikiLambda\Special; |
| 12 | |
| 13 | use MediaWiki\Content\ContentHandlerFactory; |
| 14 | use MediaWiki\Exception\ErrorPageError; |
| 15 | use MediaWiki\Extension\WikiLambda\AbstractContent\AbstractContentEditPageTrait; |
| 16 | use MediaWiki\Extension\WikiLambda\PageTitle\PageTitleBuilder; |
| 17 | use MediaWiki\Extension\WikiLambda\WikidataEntityLookup; |
| 18 | use MediaWiki\Extension\WikiLambda\WikiLambdaServices; |
| 19 | use MediaWiki\Revision\RevisionStore; |
| 20 | use MediaWiki\SpecialPage\SpecialPage; |
| 21 | use MediaWiki\Title\Title; |
| 22 | use MediaWiki\User\User; |
| 23 | |
| 24 | class 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 | } |