Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
99.05% |
104 / 105 |
|
87.50% |
7 / 8 |
CRAP | |
0.00% |
0 / 1 |
AboutTopicRenderer | |
99.05% |
104 / 105 |
|
87.50% |
7 / 8 |
20 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
showPlaceholder | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
4 | |||
showTopMessage | |
100.00% |
30 / 30 |
|
100.00% |
1 / 1 |
3 | |||
showCreateArticle | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
3.00 | |||
showTitle | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
showLanguageLinks | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
5 | |||
setOtherProjectsLinks | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
addMetaTags | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace ArticlePlaceholder; |
4 | |
5 | use MediaWiki\Html\Html; |
6 | use MediaWiki\Language\Language; |
7 | use MediaWiki\MediaWikiServices; |
8 | use MediaWiki\Output\OutputPage; |
9 | use MediaWiki\Permissions\PermissionManager; |
10 | use MediaWiki\Registration\ExtensionRegistry; |
11 | use MediaWiki\Site\SiteLookup; |
12 | use MediaWiki\SpecialPage\SpecialPage; |
13 | use MediaWiki\Title\MalformedTitleException; |
14 | use MediaWiki\Title\TitleFactory; |
15 | use MediaWiki\User\User; |
16 | use OOUI; |
17 | use Wikibase\Client\Hooks\OtherProjectsSidebarGeneratorFactory; |
18 | use Wikibase\Client\RepoLinker; |
19 | use Wikibase\Client\Usage\HashUsageAccumulator; |
20 | use Wikibase\Client\WikibaseClient; |
21 | use Wikibase\DataModel\Entity\ItemId; |
22 | use Wikibase\DataModel\Term\Term; |
23 | use Wikibase\DataModel\Term\TermTypes; |
24 | use Wikibase\Lib\Store\FallbackLabelDescriptionLookupFactory; |
25 | use Wikibase\Lib\Store\SiteLinkLookup; |
26 | |
27 | /** |
28 | * The AboutTopic SpecialPage for the ArticlePlaceholder extension |
29 | * The AboutTopicRenderer assumes the 'wikibase_item' OutputPage property |
30 | * is set in SpecialAboutTopic |
31 | * |
32 | * @ingroup Extensions |
33 | * @author Lucie-Aimée Kaffee |
34 | * @license GPL-2.0-or-later |
35 | */ |
36 | class AboutTopicRenderer { |
37 | |
38 | /** |
39 | * @var FallbackLabelDescriptionLookupFactory |
40 | */ |
41 | private $termLookupFactory; |
42 | |
43 | /** |
44 | * @var SiteLinkLookup |
45 | */ |
46 | private $siteLinkLookup; |
47 | |
48 | /** |
49 | * @var SiteLookup |
50 | */ |
51 | private $siteLookup; |
52 | |
53 | /** |
54 | * @var string |
55 | */ |
56 | private $langLinkSiteGroup; |
57 | |
58 | /** |
59 | * @var TitleFactory |
60 | */ |
61 | private $titleFactory; |
62 | |
63 | /** |
64 | * @var OtherProjectsSidebarGeneratorFactory |
65 | */ |
66 | private $otherProjectsSidebarGeneratorFactory; |
67 | |
68 | /** |
69 | * @var PermissionManager |
70 | */ |
71 | private $permissionManager; |
72 | |
73 | /** @var RepoLinker */ |
74 | private $repoLinker; |
75 | |
76 | /** |
77 | * @param FallbackLabelDescriptionLookupFactory $termLookupFactory |
78 | * @param SiteLinkLookup $siteLinkLookup |
79 | * @param SiteLookup $siteLookup |
80 | * @param string $langLinkSiteGroup |
81 | * @param TitleFactory $titleFactory |
82 | * @param OtherProjectsSidebarGeneratorFactory $otherProjectsSidebarGeneratorFactory |
83 | * @param PermissionManager $permissionManager |
84 | * @param RepoLinker $repoLinker |
85 | */ |
86 | public function __construct( |
87 | FallbackLabelDescriptionLookupFactory $termLookupFactory, |
88 | SiteLinkLookup $siteLinkLookup, |
89 | SiteLookup $siteLookup, |
90 | string $langLinkSiteGroup, |
91 | TitleFactory $titleFactory, |
92 | OtherProjectsSidebarGeneratorFactory $otherProjectsSidebarGeneratorFactory, |
93 | PermissionManager $permissionManager, |
94 | RepoLinker $repoLinker |
95 | ) { |
96 | $this->termLookupFactory = $termLookupFactory; |
97 | $this->siteLinkLookup = $siteLinkLookup; |
98 | $this->siteLookup = $siteLookup; |
99 | $this->langLinkSiteGroup = $langLinkSiteGroup; |
100 | $this->titleFactory = $titleFactory; |
101 | $this->otherProjectsSidebarGeneratorFactory = $otherProjectsSidebarGeneratorFactory; |
102 | $this->permissionManager = $permissionManager; |
103 | $this->repoLinker = $repoLinker; |
104 | } |
105 | |
106 | /** |
107 | * Show content of the ArticlePlaceholder |
108 | * |
109 | * @param ItemId $entityId |
110 | * @param Language $language |
111 | * @param User $user |
112 | * @param OutputPage $output |
113 | */ |
114 | public function showPlaceholder( |
115 | ItemId $entityId, |
116 | Language $language, |
117 | User $user, |
118 | OutputPage $output |
119 | ) { |
120 | $termLookup = $this->termLookupFactory->newLabelDescriptionLookup( |
121 | $language, |
122 | [ $entityId ], |
123 | [ TermTypes::TYPE_LABEL, TermTypes::TYPE_DESCRIPTION ] |
124 | ); |
125 | $label = $termLookup->getLabel( $entityId ); |
126 | $description = $termLookup->getDescription( $entityId ); |
127 | $canEdit = false; |
128 | |
129 | if ( $label !== null ) { |
130 | $label = $label->getText(); |
131 | $this->showTitle( $label, $output ); |
132 | |
133 | try { |
134 | $title = $this->titleFactory->newFromTextThrow( $label ); |
135 | if ( $this->permissionManager->quickUserCan( 'createpage', $user, $title ) ) { |
136 | $canEdit = true; |
137 | } |
138 | } catch ( MalformedTitleException $ex ) { |
139 | // When the entity's label contains characters not allowed in page titles |
140 | $label = ''; |
141 | $canEdit = true; |
142 | } |
143 | } |
144 | |
145 | $this->showTopMessage( $entityId, $label, $output, $canEdit ); |
146 | $output->addModuleStyles( 'ext.articleplaceholder.defaultDisplay' ); |
147 | $output->addWikiTextAsInterface( '{{aboutTopic|' . $entityId->getSerialization() . '}}' ); |
148 | |
149 | $this->showLanguageLinks( $entityId, $output ); |
150 | $this->setOtherProjectsLinks( $entityId, $output ); |
151 | $this->addMetaTags( $output, $description ); |
152 | } |
153 | |
154 | /** |
155 | * Adds the top message bar |
156 | * |
157 | * @param ItemId $entityId |
158 | * @param string|null $label |
159 | * @param OutputPage $output |
160 | * @param bool $canEdit |
161 | */ |
162 | private function showTopMessage( ItemId $entityId, ?string $label, OutputPage $output, bool $canEdit ) { |
163 | $infoIcon = new OOUI\IconWidget( [ |
164 | 'icon' => 'infoFilled', |
165 | 'title' => $output->msg( 'articleplaceholder-abouttopic-icon-title' )->text() |
166 | ] ); |
167 | |
168 | $output->enableOOUI(); |
169 | |
170 | $leftDIV = Html::rawElement( 'div', |
171 | [ 'class' => 'mw-articleplaceholder-topmessage-container-left' ], |
172 | $infoIcon |
173 | ); |
174 | |
175 | $buttonCode = ''; |
176 | if ( $label !== null && $canEdit ) { |
177 | $buttonCode = $this->showCreateArticle( $entityId, $label, $output ); |
178 | } |
179 | |
180 | $this->repoLinker = WikibaseClient::getRepoLinker(); |
181 | $messageP = Html::rawElement( 'p', [], $output->msg( |
182 | 'articleplaceholder-abouttopic-topmessage-text', |
183 | $this->repoLinker->getEntityUrl( $entityId ) |
184 | )->parse() |
185 | ); |
186 | $centerDIV = Html::rawElement( 'div', |
187 | [ 'class' => [ 'plainlinks', 'mw-articleplaceholder-topmessage-container-center' ] ], |
188 | $messageP |
189 | ); |
190 | |
191 | $rightDIV = Html::rawElement( 'div', |
192 | [ 'class' => 'mw-articleplaceholder-topmessage-container-right' ], |
193 | $buttonCode |
194 | ); |
195 | |
196 | $output->addHTML( Html::rawElement( 'div', |
197 | [ 'class' => 'mw-articleplaceholder-topmessage-container' ], |
198 | $leftDIV . $rightDIV . $centerDIV ) |
199 | ); |
200 | } |
201 | |
202 | /** |
203 | * Adds a button to create an article |
204 | * |
205 | * @param ItemId $itemId |
206 | * @param string $label |
207 | * @param OutputPage $output |
208 | * |
209 | * @return string HTML |
210 | */ |
211 | private function showCreateArticle( ItemId $itemId, $label, OutputPage $output ) { |
212 | $siteLinks = $this->siteLinkLookup->getSiteLinksForItem( $itemId ); |
213 | |
214 | $output->enableOOUI(); |
215 | $output->addModules( 'ext.articleplaceholder.createArticle' ); |
216 | $output->addJsConfigVars( 'apLabel', $label ); |
217 | |
218 | $contents = new OOUI\ButtonWidget( [ |
219 | 'id' => 'new-article-button', |
220 | 'flags' => [ 'primary', 'progressive' ], |
221 | 'infusable' => true, |
222 | 'label' => wfMessage( 'articleplaceholder-abouttopic-create-article-button' )->text(), |
223 | 'href' => SpecialPage::getTitleFor( 'CreateTopicPage', $label ) |
224 | ->getLocalURL( [ 'ref' => 'button' ] ), |
225 | 'target' => 'blank' |
226 | ] ); |
227 | |
228 | // TODO: Button should be hidden if the only sitelink links to the current wiki. |
229 | // $wikibaseClient->getSettings()->getSetting( 'siteGlobalID' ) should be injected here! |
230 | if ( ExtensionRegistry::getInstance()->isLoaded( 'ContentTranslation' ) && $siteLinks ) { |
231 | $output->addJsConfigVars( 'apContentTranslation', true ); |
232 | } |
233 | |
234 | return $contents; |
235 | } |
236 | |
237 | /** |
238 | * Show label as page title |
239 | * |
240 | * @param string $label |
241 | * @param OutputPage $output |
242 | */ |
243 | private function showTitle( $label, OutputPage $output ) { |
244 | $output->setPageTitle( htmlspecialchars( $label ) ); |
245 | } |
246 | |
247 | /** |
248 | * Set language links |
249 | * |
250 | * @param ItemId $entityId |
251 | * @param OutputPage $output |
252 | */ |
253 | private function showLanguageLinks( ItemId $entityId, OutputPage $output ) { |
254 | $siteLinks = $this->siteLinkLookup->getSiteLinksForItem( $entityId ); |
255 | $languageLinks = []; |
256 | $languageNames = []; |
257 | $pageNames = []; |
258 | |
259 | foreach ( $siteLinks as $siteLink ) { |
260 | $site = $this->siteLookup->getSite( $siteLink->getSiteId() ); |
261 | if ( $site === null ) { |
262 | continue; |
263 | } |
264 | $languageCode = $site->getLanguageCode(); |
265 | $group = $site->getGroup(); |
266 | // TODO: This should not contain the current wiki. |
267 | // $wikibaseClient->getSettings()->getSetting( 'siteGlobalID' ) should be injected here! |
268 | if ( $languageCode !== null && $group === $this->langLinkSiteGroup ) { |
269 | $languageLinks[$languageCode] = $languageCode . ':' . $siteLink->getPageName(); |
270 | |
271 | // TODO: We may want to filter with user languages |
272 | $languageNames[] = [ |
273 | 'data' => $languageCode, |
274 | 'label' => MediaWikiServices::getInstance()->getLanguageNameUtils() |
275 | ->getLanguageName( $languageCode ), |
276 | ]; |
277 | $pageNames[ $languageCode ] = $siteLink->getPageName(); |
278 | } |
279 | } |
280 | |
281 | $output->setLanguageLinks( $languageLinks ); |
282 | $output->addJsConfigVars( 'apLanguages', $languageNames ); |
283 | $output->addJsConfigVars( 'apPageNames', $pageNames ); |
284 | } |
285 | |
286 | /** |
287 | * @param ItemId $itemId |
288 | * @param OutputPage $output |
289 | */ |
290 | private function setOtherProjectsLinks( ItemId $itemId, OutputPage $output ) { |
291 | $otherProjectsSidebarGenerator = $this->otherProjectsSidebarGeneratorFactory |
292 | ->getOtherProjectsSidebarGenerator( new HashUsageAccumulator() ); |
293 | |
294 | $otherProjects = $otherProjectsSidebarGenerator->buildProjectLinkSidebarFromItemId( $itemId ); |
295 | $output->setProperty( 'wikibase-otherprojects-sidebar', $otherProjects ); |
296 | } |
297 | |
298 | /** |
299 | * @param OutputPage $output |
300 | * @param Term|null $description |
301 | */ |
302 | private function addMetaTags( OutputPage $output, ?Term $description ) { |
303 | if ( $description !== null ) { |
304 | $output->addMeta( 'description', trim( $description->getText() ) ); |
305 | } |
306 | } |
307 | |
308 | } |