Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.79% |
103 / 111 |
|
70.00% |
7 / 10 |
CRAP | |
0.00% |
0 / 1 |
SpecialAboutTopic | |
92.79% |
103 / 111 |
|
70.00% |
7 / 10 |
22.18 | |
0.00% |
0 / 1 |
newFromGlobalState | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
1 | |||
__construct | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
showContent | |
88.00% |
22 / 25 |
|
0.00% |
0 / 1 |
5.04 | |||
getDescription | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
createForm | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
1 | |||
getItemIdParam | |
60.00% |
6 / 10 |
|
0.00% |
0 / 1 |
5.02 | |||
getArticleUrl | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
getRobotPolicy | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
5 |
1 | <?php |
2 | |
3 | namespace ArticlePlaceholder\Specials; |
4 | |
5 | use ArticlePlaceholder\AboutTopicRenderer; |
6 | use MediaWiki\HTMLForm\HTMLForm; |
7 | use MediaWiki\MediaWikiServices; |
8 | use MediaWiki\SpecialPage\SpecialPage; |
9 | use MediaWiki\Title\TitleFactory; |
10 | use Wikibase\Client\WikibaseClient; |
11 | use Wikibase\DataModel\Entity\EntityIdParser; |
12 | use Wikibase\DataModel\Entity\EntityIdParsingException; |
13 | use Wikibase\DataModel\Entity\ItemId; |
14 | use Wikibase\DataModel\Services\Lookup\EntityLookup; |
15 | use Wikibase\Lib\Store\SiteLinkLookup; |
16 | use Wikimedia\Assert\Assert; |
17 | use Wikimedia\Assert\ParameterTypeException; |
18 | |
19 | /** |
20 | * The AboutTopic SpecialPage for the ArticlePlaceholder extension |
21 | * |
22 | * @ingroup Extensions |
23 | * @license GPL-2.0-or-later |
24 | * @author Lucie-Aimée Kaffee |
25 | */ |
26 | class SpecialAboutTopic extends SpecialPage { |
27 | |
28 | public static function newFromGlobalState() { |
29 | // TODO inject services |
30 | $mwServices = MediaWikiServices::getInstance(); |
31 | $config = $mwServices->getMainConfig(); |
32 | $settings = WikibaseClient::getSettings( $mwServices ); |
33 | $store = WikibaseClient::getStore( $mwServices ); |
34 | |
35 | return new self( |
36 | new AboutTopicRenderer( |
37 | WikibaseClient::getFallbackLabelDescriptionLookupFactory( $mwServices ), |
38 | $store->getSiteLinkLookup(), |
39 | $mwServices->getSiteLookup(), |
40 | WikibaseClient::getLangLinkSiteGroup( $mwServices ), |
41 | $mwServices->getTitleFactory(), |
42 | WikibaseClient::getOtherProjectsSidebarGeneratorFactory( $mwServices ), |
43 | $mwServices->getPermissionManager(), |
44 | WikibaseClient::getRepoLinker( $mwServices ) |
45 | ), |
46 | WikibaseClient::getEntityIdParser( $mwServices ), |
47 | $store->getSiteLinkLookup(), |
48 | $mwServices->getTitleFactory(), |
49 | $settings->getSetting( 'siteGlobalID' ), |
50 | $store->getEntityLookup(), |
51 | $config->get( 'ArticlePlaceholderSearchEngineIndexed' ) |
52 | ); |
53 | } |
54 | |
55 | /** |
56 | * @var AboutTopicRenderer |
57 | */ |
58 | private $aboutTopicRenderer; |
59 | |
60 | /** |
61 | * @var EntityIdParser |
62 | */ |
63 | private $idParser; |
64 | |
65 | /** |
66 | * @var SiteLinkLookup |
67 | */ |
68 | private $siteLinkLookup; |
69 | |
70 | /** |
71 | * @var TitleFactory |
72 | */ |
73 | private $titleFactory; |
74 | |
75 | /** |
76 | * @var string |
77 | */ |
78 | private $siteGlobalID; |
79 | |
80 | /** |
81 | * @var EntityLookup |
82 | */ |
83 | private $entityLookup; |
84 | |
85 | /** |
86 | * @var bool|string |
87 | */ |
88 | private $searchEngineIndexed; |
89 | |
90 | /** |
91 | * @param AboutTopicRenderer $aboutTopicRenderer |
92 | * @param EntityIdParser $idParser |
93 | * @param SiteLinkLookup $siteLinkLookup |
94 | * @param TitleFactory $titleFactory |
95 | * @param string $siteGlobalID |
96 | * @param EntityLookup $entityLookup |
97 | * @param bool|string $searchEngineIndexed |
98 | * |
99 | * @throws ParameterTypeException |
100 | */ |
101 | public function __construct( |
102 | AboutTopicRenderer $aboutTopicRenderer, |
103 | EntityIdParser $idParser, |
104 | SiteLinkLookup $siteLinkLookup, |
105 | TitleFactory $titleFactory, |
106 | $siteGlobalID, |
107 | EntityLookup $entityLookup, |
108 | $searchEngineIndexed |
109 | ) { |
110 | parent::__construct( 'AboutTopic' ); |
111 | |
112 | Assert::parameterType( |
113 | 'boolean|string', |
114 | $searchEngineIndexed, |
115 | '$searchEngineIndexed' |
116 | ); |
117 | |
118 | $this->aboutTopicRenderer = $aboutTopicRenderer; |
119 | $this->idParser = $idParser; |
120 | $this->siteLinkLookup = $siteLinkLookup; |
121 | $this->titleFactory = $titleFactory; |
122 | $this->siteGlobalID = $siteGlobalID; |
123 | $this->entityLookup = $entityLookup; |
124 | $this->searchEngineIndexed = $searchEngineIndexed; |
125 | } |
126 | |
127 | /** |
128 | * @param string|null $sub |
129 | */ |
130 | public function execute( $sub ) { |
131 | $this->showContent( $sub ); |
132 | } |
133 | |
134 | /** |
135 | * @param string|null $itemIdString |
136 | */ |
137 | private function showContent( ?string $itemIdString ) { |
138 | $out = $this->getOutput(); |
139 | $itemId = $this->getItemIdParam( $itemIdString ); |
140 | |
141 | if ( $itemId !== null ) { |
142 | $out->setProperty( 'wikibase_item', $itemId->getSerialization() ); |
143 | |
144 | $out->setCanonicalUrl( |
145 | $this->getTitleFor( $this->getName(), $itemId->getSerialization() )->getCanonicalURL() |
146 | ); |
147 | } |
148 | $this->setHeaders(); |
149 | |
150 | // Unconditionally cache the special page for a day, see T109458 |
151 | $out->setCdnMaxage( 86400 ); |
152 | |
153 | if ( $itemId === null ) { |
154 | $this->createForm(); |
155 | return; |
156 | } |
157 | |
158 | if ( !$this->entityLookup->hasEntity( $itemId ) ) { |
159 | $this->createForm(); |
160 | $out->addWikiMsg( 'articleplaceholder-abouttopic-no-entity-error' ); |
161 | return; |
162 | } |
163 | |
164 | $articleOnWiki = $this->getArticleUrl( $itemId ); |
165 | |
166 | if ( $articleOnWiki !== null ) { |
167 | $out->redirect( $articleOnWiki ); |
168 | } else { |
169 | $this->aboutTopicRenderer->showPlaceholder( |
170 | $itemId, |
171 | $this->getLanguage(), |
172 | $this->getUser(), |
173 | $out |
174 | ); |
175 | } |
176 | } |
177 | |
178 | /** |
179 | * @inheritDoc |
180 | */ |
181 | public function getDescription() { |
182 | return $this->msg( 'articleplaceholder-abouttopic' ); |
183 | } |
184 | |
185 | /** |
186 | * @inheritDoc |
187 | */ |
188 | protected function getGroupName() { |
189 | return 'other'; |
190 | } |
191 | |
192 | /** |
193 | * Create html elements |
194 | */ |
195 | protected function createForm() { |
196 | $form = HTMLForm::factory( 'ooui', [ |
197 | 'text' => [ |
198 | 'type' => 'text', |
199 | 'name' => 'entityid', |
200 | 'id' => 'ap-abouttopic-entityid', |
201 | 'required' => true, |
202 | 'cssclass' => 'ap-input', |
203 | 'label-message' => 'articleplaceholder-abouttopic-entityid', |
204 | 'default' => $this->getRequest()->getVal( 'entityid' ), |
205 | ] |
206 | ], $this->getContext() ); |
207 | |
208 | $form |
209 | ->setMethod( 'get' ) |
210 | ->setId( 'ap-abouttopic-form1' ) |
211 | ->setHeaderHtml( $this->msg( 'articleplaceholder-abouttopic-intro' )->parse() ) |
212 | ->setWrapperLegend( '' ) |
213 | ->setSubmitTextMsg( 'articleplaceholder-abouttopic-submit' ) |
214 | ->prepareForm() |
215 | ->displayForm( false ); |
216 | } |
217 | |
218 | /** |
219 | * @param string|null $fallback |
220 | * |
221 | * @return ItemId|null |
222 | */ |
223 | private function getItemIdParam( ?string $fallback ): ?ItemId { |
224 | $rawId = trim( $this->getRequest()->getText( 'entityid', $fallback ?? '' ) ); |
225 | |
226 | if ( $rawId === '' ) { |
227 | return null; |
228 | } |
229 | |
230 | try { |
231 | $id = $this->idParser->parse( $rawId ); |
232 | if ( !( $id instanceof ItemId ) ) { |
233 | throw new EntityIdParsingException(); |
234 | } |
235 | |
236 | return $id; |
237 | } catch ( EntityIdParsingException $ex ) { |
238 | $this->getOutput()->addWikiMsg( 'articleplaceholder-abouttopic-no-entity-error' ); |
239 | } |
240 | |
241 | return null; |
242 | } |
243 | |
244 | /** |
245 | * @param ItemId $entityId |
246 | * |
247 | * @return string|null |
248 | */ |
249 | private function getArticleUrl( ItemId $entityId ) { |
250 | $sitelinkTitles = $this->siteLinkLookup->getLinks( |
251 | [ $entityId->getNumericId() ], |
252 | [ $this->siteGlobalID ] |
253 | ); |
254 | |
255 | if ( isset( $sitelinkTitles[0][1] ) ) { |
256 | $sitelinkTitle = $sitelinkTitles[0][1]; |
257 | return $this->titleFactory->newFromText( $sitelinkTitle )->getLinkURL(); |
258 | } |
259 | |
260 | return null; |
261 | } |
262 | |
263 | /** |
264 | * @return string |
265 | */ |
266 | protected function getRobotPolicy() { |
267 | $wikibaseItem = $this->getOutput()->getProperty( 'wikibase_item' ); |
268 | if ( $wikibaseItem === null ) { |
269 | // No item id set: We're showing the form, not an actual placeholder. |
270 | return parent::getRobotPolicy(); |
271 | } |
272 | |
273 | if ( $this->searchEngineIndexed === true ) { |
274 | return 'index,follow'; |
275 | } |
276 | |
277 | if ( is_string( $this->searchEngineIndexed ) ) { |
278 | $entityId = new ItemId( $wikibaseItem ); |
279 | |
280 | $maxEntityId = new ItemId( $this->searchEngineIndexed ); |
281 | |
282 | if ( $entityId->getNumericId() <= $maxEntityId->getNumericId() ) { |
283 | return 'index,follow'; |
284 | } |
285 | } |
286 | |
287 | return parent::getRobotPolicy(); |
288 | } |
289 | |
290 | } |