56 parent::__construct(
'Tags' );
57 $this->changeTagsStore = $changeTagsStore;
64 $this->
getOutput()->addModuleStyles(
'mediawiki.codex.messagebox.styles' );
85 private function showTagList() {
87 $out->setPageTitleMsg( $this->
msg(
'tags-title' ) );
88 $out->wrapWikiMsg(
"<div class='mw-tags-intro'>\n$1\n</div>",
'tags-intro' );
91 $userCanManage = $authority->isAllowed(
'managechangetags' );
92 $userCanDelete = $authority->isAllowed(
'deletechangetags' );
93 $userCanEditInterface = $authority->isAllowed(
'editinterface' );
96 if ( $userCanManage ) {
100 'label' => $this->
msg(
'tags-create-tag-name' )->plain(),
105 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
106 'label' => $this->
msg(
'tags-create-reason' )->plain(),
109 'IgnoreWarnings' => [
114 HTMLForm::factory(
'ooui', $fields, $this->
getContext() )
115 ->setAction( $this->
getPageTitle(
'create' )->getLocalURL() )
116 ->setWrapperLegendMsg(
'tags-create-heading' )
117 ->setHeaderHtml( $this->
msg(
'tags-create-explanation' )->parseAsBlock() )
118 ->setSubmitCallback( $this->processCreateTagForm( ... ) )
119 ->setSubmitTextMsg(
'tags-create-submit' )
129 if ( $out->getRedirect() !==
'' ) {
135 $tagStats = $this->changeTagsStore->tagUsageStatistics();
138 $this->explicitlyDefinedTags = array_fill_keys(
139 $this->changeTagsStore->listExplicitlyDefinedTags(),
true );
140 $this->softwareDefinedTags = array_fill_keys(
141 $this->changeTagsStore->listSoftwareDefinedTags(),
true );
144 $definedTags = array_keys( $this->explicitlyDefinedTags + $this->softwareDefinedTags );
147 if ( !$tagStats && !$definedTags ) {
152 $thead = Html::rawElement(
'tr', [], Html::rawElement(
'th', [], $this->
msg(
'tags-tag' )->parse() ) .
153 Html::rawElement(
'th', [], $this->
msg(
'tags-display-header' )->parse() ) .
154 Html::rawElement(
'th', [], $this->
msg(
'tags-description-header' )->parse() ) .
155 Html::rawElement(
'th', [], $this->
msg(
'tags-source-header' )->parse() ) .
156 Html::rawElement(
'th', [], $this->
msg(
'tags-active-header' )->parse() ) .
157 Html::rawElement(
'th', [], $this->
msg(
'tags-hitcount-header' )->parse() ) .
158 ( ( $userCanManage || $userCanDelete ) ?
159 Html::rawElement(
'th', [
'class' =>
'unsortable' ],
160 $this->
msg(
'tags-actions-header' )->parse() ) :
166 $this->softwareActivatedTags = array_fill_keys(
167 $this->changeTagsStore->listSoftwareActivatedTags(),
true );
170 foreach ( $tagStats as $tag => $hitcount ) {
171 $tbody .= $this->doTagRow( $tag, $hitcount, $userCanManage,
172 $userCanDelete, $userCanEditInterface );
175 foreach ( $definedTags as $tag ) {
176 if ( !isset( $tagStats[$tag] ) ) {
177 $tbody .= $this->doTagRow( $tag, 0, $userCanManage, $userCanDelete, $userCanEditInterface );
181 $out->addModuleStyles( [
182 'jquery.tablesorter.styles',
183 'mediawiki.pager.styles'
185 $out->addModules(
'jquery.tablesorter' );
186 $out->addHTML( Html::rawElement(
188 [
'class' =>
'mw-datatable sortable mw-tags-table' ],
189 Html::rawElement(
'thead', [], $thead ) .
190 Html::rawElement(
'tbody', [], $tbody )
194 private function doTagRow(
195 string $tag,
int $hitcount,
bool $showManageActions,
bool $showDeleteActions,
bool $showEditLinks
198 $newRow .= Html::rawElement(
'td', [],
Html::element(
'code', [], $tag ) );
201 $disp = ChangeTags::tagDescription( $tag, $this->
getContext() );
202 if ( $disp ===
false ) {
205 if ( $showEditLinks ) {
207 $editLink = $linkRenderer->makeLink(
209 $this->
msg(
'tags-edit' )->text(),
211 [
'action' =>
'edit' ]
213 $helpEditLink = $linkRenderer->makeLink(
214 $this->
msg(
"tag-$tag-helppage" )->inContentLanguage()->
getTitle(),
215 $this->
msg(
'tags-helppage-edit' )->text(),
217 [
'action' =>
'edit' ]
219 $disp .= $this->
msg(
'parentheses' )->rawParams(
220 $this->
getLanguage()->pipeList( [ $editLink, $helpEditLink ] )
223 $newRow .= Html::rawElement(
'td', [], $disp );
225 $msg = $this->
msg(
"tag-$tag-description" );
226 $desc = !$msg->exists() ?
'' : $msg->parse();
227 if ( $showEditLinks ) {
229 $editDescLink = $linkRenderer->makeLink(
230 $this->
msg(
"tag-$tag-description" )->inContentLanguage()->
getTitle(),
231 $this->
msg(
'tags-edit' )->text(),
233 [
'action' =>
'edit' ]
235 $desc .= $this->
msg(
'parentheses' )->rawParams( $editDescLink )->escaped();
237 $newRow .= Html::rawElement(
'td', [], $desc );
240 $isSoftware = isset( $this->softwareDefinedTags[$tag] );
241 $isExplicit = isset( $this->explicitlyDefinedTags[$tag] );
244 $sourceMsgs[] = $this->
msg(
'tags-source-extension' )->escaped();
247 $sourceMsgs[] = $this->
msg(
'tags-source-manual' )->escaped();
249 if ( !$sourceMsgs ) {
250 $sourceMsgs[] = $this->
msg(
'tags-source-none' )->escaped();
252 $newRow .= Html::rawElement(
'td', [], implode(
Html::element(
'br' ), $sourceMsgs ) );
254 $isActive = $isExplicit || isset( $this->softwareActivatedTags[$tag] );
255 $activeMsg = ( $isActive ?
'tags-active-yes' :
'tags-active-no' );
256 $newRow .= Html::rawElement(
'td', [], $this->
msg( $activeMsg )->escaped() );
258 $hitcountLabelMsg = $this->
msg(
'tags-hitcount' )->numParams( $hitcount );
260 $hitcountLabel = $linkRenderer->makeLink(
262 $hitcountLabelMsg->text(),
264 [
'tagfilter' => $tag ]
267 $hitcountLabel = $hitcountLabelMsg->escaped();
271 $newRow .= Html::rawElement(
'td', [
'data-sort-value' => $hitcount ], $hitcountLabel );
275 if ( $showDeleteActions && ChangeTags::canDeleteTag( $tag )->isOK() ) {
276 $actionLinks[] = $linkRenderer->makeKnownLink(
278 $this->
msg(
'tags-delete' )->text(),
283 if ( $showManageActions ) {
284 if ( ChangeTags::canActivateTag( $tag )->isOK() ) {
285 $actionLinks[] = $linkRenderer->makeKnownLink(
287 $this->
msg(
'tags-activate' )->text(),
292 if ( ChangeTags::canDeactivateTag( $tag )->isOK() ) {
293 $actionLinks[] = $linkRenderer->makeKnownLink(
295 $this->
msg(
'tags-deactivate' )->text(),
301 if ( $showDeleteActions || $showManageActions ) {
302 $newRow .= Html::rawElement(
'td', [], $this->
getLanguage()->pipeList( $actionLinks ) );
305 return Html::rawElement(
'tr', [], $newRow ) .
"\n";
308 private function processCreateTagForm( array $data, HTMLForm $form ): bool {
310 $out = $context->getOutput();
312 $tag = trim( strval( $data[
'Tag'] ) );
313 $ignoreWarnings = isset( $data[
'IgnoreWarnings'] ) && $data[
'IgnoreWarnings'] ===
'1';
314 $status = ChangeTags::createTagWithChecks( $tag, $data[
'Reason'],
315 $context->getAuthority(), $ignoreWarnings );
317 if ( $status->isGood() ) {
318 $out->redirect( $this->getPageTitle()->getLocalURL() );
320 } elseif ( $status->isOK() ) {
324 'IgnoreWarnings' => [
330 $headerText = $this->msg(
'tags-create-warnings-above', $tag,
331 count( $status->getMessages(
'warning' ) ) )->parseAsBlock() .
332 $out->parseAsInterface( $status->getWikiText() ) .
333 $this->msg(
'tags-create-warnings-below' )->parseAsBlock();
335 $form->setHeaderHtml( $headerText )
336 ->setSubmitTextMsg(
'htmlform-yes' );
338 $out->addBacklinkSubtitle( $this->getPageTitle() );
341 foreach ( $status->getMessages() as $msg ) {
342 $out->addHTML( Html::errorBox(
343 $this->msg( $msg )->parse()
351 $authority = $this->getAuthority();
352 if ( !$authority->isAllowed(
'deletechangetags' ) ) {
356 $out = $this->getOutput();
357 $out->setPageTitleMsg( $this->msg(
'tags-delete-title' ) );
358 $out->addBacklinkSubtitle( $this->getPageTitle() );
361 $canDeleteResult = ChangeTags::canDeleteTag( $tag, $authority );
362 if ( !$canDeleteResult->isGood() ) {
363 foreach ( $canDeleteResult->getMessages() as $msg ) {
364 $out->addHTML( Html::errorBox(
365 $this->msg( $msg )->parse()
368 if ( !$canDeleteResult->isOK() ) {
373 $preText = $this->msg(
'tags-delete-explanation-initial', $tag )->parseAsBlock();
374 $tagUsage = $this->changeTagsStore->tagUsageStatistics();
375 if ( isset( $tagUsage[$tag] ) && $tagUsage[$tag] > 0 ) {
376 $preText .= $this->msg(
'tags-delete-explanation-in-use', $tag,
377 $tagUsage[$tag] )->parseAsBlock();
379 $preText .= $this->msg(
'tags-delete-explanation-warning', $tag )->parseAsBlock();
382 $this->softwareActivatedTags = array_fill_keys(
383 $this->changeTagsStore->listSoftwareActivatedTags(),
true );
384 if ( isset( $this->softwareActivatedTags[$tag] ) ) {
385 $preText .= $this->msg(
'tags-delete-explanation-active', $tag )->parseAsBlock();
389 $fields[
'Reason'] = [
391 'label' => $this->msg(
'tags-delete-reason' )->plain(),
394 $fields[
'HiddenTag'] = [
401 HTMLForm::factory(
'ooui', $fields, $this->getContext() )
402 ->setAction( $this->getPageTitle(
'delete' )->getLocalURL() )
403 ->setSubmitCallback(
function ( $data, $form ) {
404 return $this->processTagForm( $data, $form,
'delete' );
406 ->setSubmitTextMsg(
'tags-delete-submit' )
407 ->setSubmitDestructive()
408 ->addPreHtml( $preText )
413 $actionStr = $activate ?
'activate' :
'deactivate';
415 $authority = $this->getAuthority();
416 if ( !$authority->isAllowed(
'managechangetags' ) ) {
420 $out = $this->getOutput();
422 $out->setPageTitleMsg( $this->msg(
"tags-$actionStr-title" ) );
423 $out->addBacklinkSubtitle( $this->getPageTitle() );
427 $result = ChangeTags::canActivateTag( $tag, $authority );
429 $result = ChangeTags::canDeactivateTag( $tag, $authority );
431 if ( !$result->isGood() ) {
432 foreach ( $result->getMessages() as $msg ) {
433 $out->addHTML( Html::errorBox(
434 $this->msg( $msg )->parse()
437 if ( !$result->isOK() ) {
443 $preText = $this->msg(
"tags-$actionStr-question", $tag )->parseAsBlock();
447 $fields[
'Reason'] = [
449 'label' => $this->msg(
"tags-$actionStr-reason" )->plain(),
452 $fields[
'HiddenTag'] = [
459 HTMLForm::factory(
'ooui', $fields, $this->getContext() )
460 ->setAction( $this->getPageTitle( $actionStr )->getLocalURL() )
461 ->setSubmitCallback(
function ( $data, $form ) use ( $actionStr ) {
462 return $this->processTagForm( $data, $form, $actionStr );
465 ->setSubmitTextMsg(
"tags-$actionStr-submit" )
466 ->addPreHtml( $preText )
478 $out = $context->getOutput();
480 $tag = $data[
'HiddenTag'];
482 $method =
"{$action}TagWithChecks";
483 $status = ChangeTags::$method(
484 $tag, $data[
'Reason'], $context->getUser(),
true );
486 if ( $status->isGood() ) {
487 $out->redirect( $this->getPageTitle()->getLocalURL() );
489 } elseif ( $status->isOK() && $action ===
'delete' ) {
491 $out->addWikiTextAsInterface( $this->msg(
'tags-delete-warnings-after-delete', $tag,
492 count( $status->getMessages(
'warning' ) ) )->text() .
"\n" .
493 $status->getWikitext() );
494 $out->addReturnTo( $this->getPageTitle() );
497 foreach ( $status->getMessages() as $msg ) {
498 $out->addHTML( Html::errorBox(
499 $this->msg( $msg )->parse()