43 parent::__construct(
'Tags' );
51 $this->
getOutput()->addModuleStyles(
'mediawiki.codex.messagebox.styles' );
72 private function showTagList() {
74 $out->setPageTitleMsg( $this->
msg(
'tags-title' ) );
75 $out->wrapWikiMsg(
"<div class='mw-tags-intro'>\n$1\n</div>",
'tags-intro' );
78 $userCanManage = $authority->isAllowed(
'managechangetags' );
79 $userCanDelete = $authority->isAllowed(
'deletechangetags' );
80 $userCanEditInterface = $authority->isAllowed(
'editinterface' );
83 if ( $userCanManage ) {
87 'label' => $this->
msg(
'tags-create-tag-name' )->plain(),
92 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
93 'label' => $this->
msg(
'tags-create-reason' )->plain(),
101 HTMLForm::factory(
'ooui', $fields, $this->
getContext() )
102 ->setAction( $this->
getPageTitle(
'create' )->getLocalURL() )
103 ->setWrapperLegendMsg(
'tags-create-heading' )
104 ->setHeaderHtml( $this->
msg(
'tags-create-explanation' )->parseAsBlock() )
105 ->setSubmitCallback( $this->processCreateTagForm( ... ) )
106 ->setSubmitTextMsg(
'tags-create-submit' )
116 if ( $out->getRedirect() !==
'' ) {
122 $tagStats = $this->changeTagsStore->tagUsageStatistics();
125 $this->explicitlyDefinedTags = array_fill_keys(
126 $this->changeTagsStore->listExplicitlyDefinedTags(),
true );
127 $this->softwareDefinedTags = array_fill_keys(
128 $this->changeTagsStore->listSoftwareDefinedTags(),
true );
131 $definedTags = array_keys( $this->explicitlyDefinedTags + $this->softwareDefinedTags );
134 if ( !$tagStats && !$definedTags ) {
139 $thead = Html::rawElement(
'tr', [], Html::rawElement(
'th', [], $this->
msg(
'tags-tag' )->parse() ) .
140 Html::rawElement(
'th', [], $this->
msg(
'tags-display-header' )->parse() ) .
141 Html::rawElement(
'th', [], $this->
msg(
'tags-description-header' )->parse() ) .
142 Html::rawElement(
'th', [], $this->
msg(
'tags-source-header' )->parse() ) .
143 Html::rawElement(
'th', [], $this->
msg(
'tags-active-header' )->parse() ) .
144 Html::rawElement(
'th', [], $this->
msg(
'tags-hitcount-header' )->parse() ) .
145 ( ( $userCanManage || $userCanDelete ) ?
146 Html::rawElement(
'th', [
'class' =>
'unsortable' ],
147 $this->
msg(
'tags-actions-header' )->parse() ) :
153 $this->softwareActivatedTags = array_fill_keys(
154 $this->changeTagsStore->listSoftwareActivatedTags(),
true );
157 foreach ( $tagStats as $tag => $hitcount ) {
158 $tbody .= $this->doTagRow( $tag, $hitcount, $userCanManage,
159 $userCanDelete, $userCanEditInterface );
162 foreach ( $definedTags as $tag ) {
163 if ( !isset( $tagStats[$tag] ) ) {
164 $tbody .= $this->doTagRow( $tag, 0, $userCanManage, $userCanDelete, $userCanEditInterface );
168 $out->addModuleStyles( [
169 'jquery.tablesorter.styles',
170 'mediawiki.pager.styles'
172 $out->addModules(
'jquery.tablesorter' );
173 $out->addHTML( Html::rawElement(
175 [
'class' =>
'mw-datatable sortable mw-tags-table' ],
176 Html::rawElement(
'thead', [], $thead ) .
177 Html::rawElement(
'tbody', [], $tbody )
181 private function doTagRow(
182 string $tag,
int $hitcount,
bool $showManageActions,
bool $showDeleteActions,
bool $showEditLinks
185 $newRow .= Html::rawElement(
'td', [],
Html::element(
'code', [], $tag ) );
188 $disp = ChangeTags::tagDescription( $tag, $this->
getContext() );
189 if ( $disp ===
false ) {
192 if ( $showEditLinks ) {
194 $editLink = $linkRenderer->makeLink(
195 $this->
msg(
"tag-$tag" )->getTitle(),
196 $this->
msg(
'tags-edit' )->text(),
198 [
'action' =>
'edit' ]
200 $helpEditLink = $linkRenderer->makeLink(
201 $this->
msg(
"tag-$tag-helppage" )->inContentLanguage()->getTitle(),
202 $this->
msg(
'tags-helppage-edit' )->text(),
204 [
'action' =>
'edit' ]
206 $disp .= $this->
msg(
'parentheses' )->rawParams(
207 $this->
getLanguage()->pipeList( [ $editLink, $helpEditLink ] )
210 $newRow .= Html::rawElement(
'td', [], $disp );
212 $msg = $this->
msg(
"tag-$tag-description" );
213 $desc = !$msg->exists() ?
'' : $msg->parse();
214 if ( $showEditLinks ) {
216 $editDescLink = $linkRenderer->makeLink(
217 $this->
msg(
"tag-$tag-description" )->inContentLanguage()->getTitle(),
218 $this->
msg(
'tags-edit' )->text(),
220 [
'action' =>
'edit' ]
222 $desc .= $this->
msg(
'parentheses' )->rawParams( $editDescLink )->escaped();
224 $newRow .= Html::rawElement(
'td', [], $desc );
227 $isSoftware = isset( $this->softwareDefinedTags[$tag] );
228 $isExplicit = isset( $this->explicitlyDefinedTags[$tag] );
231 $sourceMsgs[] = $this->
msg(
'tags-source-extension' )->escaped();
234 $sourceMsgs[] = $this->
msg(
'tags-source-manual' )->escaped();
236 if ( !$sourceMsgs ) {
237 $sourceMsgs[] = $this->
msg(
'tags-source-none' )->escaped();
239 $newRow .= Html::rawElement(
'td', [], implode(
Html::element(
'br' ), $sourceMsgs ) );
241 $isActive = $isExplicit || isset( $this->softwareActivatedTags[$tag] );
242 $activeMsg = ( $isActive ?
'tags-active-yes' :
'tags-active-no' );
245 $hitcountLabelMsg = $this->
msg(
'tags-hitcount' )->numParams( $hitcount );
247 $hitcountLabel = $linkRenderer->makeLink(
249 $hitcountLabelMsg->text(),
251 [
'tagfilter' => $tag ]
254 $hitcountLabel = $hitcountLabelMsg->escaped();
258 $newRow .= Html::rawElement(
'td', [
'data-sort-value' => $hitcount ], $hitcountLabel );
262 if ( $showDeleteActions && ChangeTags::canDeleteTag( $tag )->isOK() ) {
263 $actionLinks[] = $linkRenderer->makeKnownLink(
265 $this->
msg(
'tags-delete' )->text(),
270 if ( $showManageActions ) {
271 if ( ChangeTags::canActivateTag( $tag )->isOK() ) {
272 $actionLinks[] = $linkRenderer->makeKnownLink(
274 $this->
msg(
'tags-activate' )->text(),
279 if ( ChangeTags::canDeactivateTag( $tag )->isOK() ) {
280 $actionLinks[] = $linkRenderer->makeKnownLink(
282 $this->
msg(
'tags-deactivate' )->text(),
288 if ( $showDeleteActions || $showManageActions ) {
289 $newRow .= Html::rawElement(
'td', [], $this->
getLanguage()->pipeList( $actionLinks ) );
292 return Html::rawElement(
'tr', [], $newRow ) .
"\n";
295 private function processCreateTagForm( array $data, HTMLForm $form ): bool {
297 $out = $context->getOutput();
299 $tag = trim( strval( $data[
'Tag'] ) );
300 $ignoreWarnings = isset( $data[
'IgnoreWarnings'] ) && $data[
'IgnoreWarnings'] ===
'1';
301 $status = ChangeTags::createTagWithChecks( $tag, $data[
'Reason'],
302 $context->getAuthority(), $ignoreWarnings );
304 if ( $status->isGood() ) {
305 $out->redirect( $this->getPageTitle()->getLocalURL() );
307 } elseif ( $status->isOK() ) {
311 'IgnoreWarnings' => [
317 $headerText = $this->
msg(
'tags-create-warnings-above', $tag,
318 count( $status->getMessages(
'warning' ) ) )->parseAsBlock() .
319 $out->parseAsInterface( $status->getWikiText() ) .
320 $this->
msg(
'tags-create-warnings-below' )->parseAsBlock();
322 $form->setHeaderHtml( $headerText )
323 ->setSubmitTextMsg(
'htmlform-yes' );
325 $out->addBacklinkSubtitle( $this->getPageTitle() );
328 foreach ( $status->getMessages() as $msg ) {
329 $out->addHTML( Html::errorBox(
330 $this->
msg( $msg )->parse()
341 $authority = $this->getAuthority();
342 if ( !$authority->isAllowed(
'deletechangetags' ) ) {
346 $out = $this->getOutput();
347 $out->setPageTitleMsg( $this->msg(
'tags-delete-title' ) );
348 $out->addBacklinkSubtitle( $this->getPageTitle() );
351 $canDeleteResult = ChangeTags::canDeleteTag( $tag, $authority );
352 if ( !$canDeleteResult->isGood() ) {
353 foreach ( $canDeleteResult->getMessages() as $msg ) {
354 $out->addHTML( Html::errorBox(
355 $this->msg( $msg )->parse()
358 if ( !$canDeleteResult->isOK() ) {
363 $preText = $this->msg(
'tags-delete-explanation-initial', $tag )->parseAsBlock();
364 $tagUsage = $this->changeTagsStore->tagUsageStatistics();
365 if ( isset( $tagUsage[$tag] ) && $tagUsage[$tag] > 0 ) {
366 $preText .= $this->msg(
'tags-delete-explanation-in-use', $tag,
367 $tagUsage[$tag] )->parseAsBlock();
369 $preText .= $this->msg(
'tags-delete-explanation-warning', $tag )->parseAsBlock();
372 $this->softwareActivatedTags = array_fill_keys(
373 $this->changeTagsStore->listSoftwareActivatedTags(),
true );
374 if ( isset( $this->softwareActivatedTags[$tag] ) ) {
375 $preText .= $this->msg(
'tags-delete-explanation-active', $tag )->parseAsBlock();
379 $fields[
'Reason'] = [
381 'label' => $this->msg(
'tags-delete-reason' )->plain(),
384 $fields[
'HiddenTag'] = [
391 HTMLForm::factory(
'ooui', $fields, $this->getContext() )
392 ->setAction( $this->getPageTitle(
'delete' )->getLocalURL() )
393 ->setSubmitCallback(
function ( $data, $form ) {
394 return $this->processTagForm( $data, $form,
'delete' );
396 ->setSubmitTextMsg(
'tags-delete-submit' )
397 ->setSubmitDestructive()
398 ->addPreHtml( $preText )
407 $actionStr = $activate ?
'activate' :
'deactivate';
409 $authority = $this->getAuthority();
410 if ( !$authority->isAllowed(
'managechangetags' ) ) {
414 $out = $this->getOutput();
416 $out->setPageTitleMsg( $this->msg(
"tags-$actionStr-title" ) );
417 $out->addBacklinkSubtitle( $this->getPageTitle() );
421 $result = ChangeTags::canActivateTag( $tag, $authority );
423 $result = ChangeTags::canDeactivateTag( $tag, $authority );
425 if ( !$result->isGood() ) {
426 foreach ( $result->getMessages() as $msg ) {
427 $out->addHTML( Html::errorBox(
428 $this->msg( $msg )->parse()
431 if ( !$result->isOK() ) {
437 $preText = $this->msg(
"tags-$actionStr-question", $tag )->parseAsBlock();
441 $fields[
'Reason'] = [
443 'label' => $this->msg(
"tags-$actionStr-reason" )->plain(),
446 $fields[
'HiddenTag'] = [
453 HTMLForm::factory(
'ooui', $fields, $this->getContext() )
454 ->setAction( $this->getPageTitle( $actionStr )->getLocalURL() )
455 ->setSubmitCallback(
function ( $data, $form ) use ( $actionStr ) {
456 return $this->processTagForm( $data, $form, $actionStr );
459 ->setSubmitTextMsg(
"tags-$actionStr-submit" )
460 ->addPreHtml( $preText )
472 $out = $context->getOutput();
474 $tag = $data[
'HiddenTag'];
476 $method =
"{$action}TagWithChecks";
477 $status = ChangeTags::$method(
478 $tag, $data[
'Reason'], $context->getUser(),
true );
480 if ( $status->isGood() ) {
481 $out->redirect( $this->getPageTitle()->getLocalURL() );
483 } elseif ( $status->isOK() && $action ===
'delete' ) {
485 $out->addWikiTextAsInterface( $this->msg(
'tags-delete-warnings-after-delete', $tag,
486 count( $status->getMessages(
'warning' ) ) )->text() .
"\n" .
487 $status->getWikitext() );
488 $out->addReturnTo( $this->getPageTitle() );
491 foreach ( $status->getMessages() as $msg ) {
492 $out->addHTML( Html::errorBox(
493 $this->msg( $msg )->parse()