24 namespace MediaWiki\Specials;
59 parent::__construct(
'Tags' );
60 $this->changeTagsStore = $changeTagsStore;
87 private function showTagList() {
89 $out->setPageTitleMsg( $this->
msg(
'tags-title' ) );
90 $out->wrapWikiMsg(
"<div class='mw-tags-intro'>\n$1\n</div>",
'tags-intro' );
93 $userCanManage = $authority->isAllowed(
'managechangetags' );
94 $userCanDelete = $authority->isAllowed(
'deletechangetags' );
95 $userCanEditInterface = $authority->isAllowed(
'editinterface' );
98 if ( $userCanManage ) {
102 'label' => $this->
msg(
'tags-create-tag-name' )->plain(),
108 'label' => $this->
msg(
'tags-create-reason' )->plain(),
111 'IgnoreWarnings' => [
117 ->setAction( $this->
getPageTitle(
'create' )->getLocalURL() )
118 ->setWrapperLegendMsg(
'tags-create-heading' )
119 ->setHeaderHtml( $this->
msg(
'tags-create-explanation' )->parseAsBlock() )
120 ->setSubmitCallback( [ $this,
'processCreateTagForm' ] )
121 ->setSubmitTextMsg(
'tags-create-submit' )
131 if ( $out->getRedirect() !==
'' ) {
137 $tagStats = $this->changeTagsStore->tagUsageStatistics();
140 $this->explicitlyDefinedTags = array_fill_keys(
141 $this->changeTagsStore->listExplicitlyDefinedTags(),
true );
142 $this->softwareDefinedTags = array_fill_keys(
143 $this->changeTagsStore->listSoftwareDefinedTags(),
true );
146 $definedTags = array_keys( $this->explicitlyDefinedTags + $this->softwareDefinedTags );
149 if ( !$tagStats && !$definedTags ) {
155 Xml::tags(
'th',
null, $this->
msg(
'tags-display-header' )->parse() ) .
156 Xml::tags(
'th',
null, $this->
msg(
'tags-description-header' )->parse() ) .
157 Xml::tags(
'th',
null, $this->
msg(
'tags-source-header' )->parse() ) .
158 Xml::tags(
'th',
null, $this->
msg(
'tags-active-header' )->parse() ) .
159 Xml::tags(
'th',
null, $this->
msg(
'tags-hitcount-header' )->parse() ) .
160 ( ( $userCanManage || $userCanDelete ) ?
161 Xml::tags(
'th', [
'class' =>
'unsortable' ],
162 $this->
msg(
'tags-actions-header' )->parse() ) :
168 $this->softwareActivatedTags = array_fill_keys(
169 $this->changeTagsStore->listSoftwareActivatedTags(),
true );
172 foreach ( $tagStats as $tag => $hitcount ) {
173 $tbody .= $this->doTagRow( $tag, $hitcount, $userCanManage,
174 $userCanDelete, $userCanEditInterface );
177 foreach ( $definedTags as $tag ) {
178 if ( !isset( $tagStats[$tag] ) ) {
179 $tbody .= $this->doTagRow( $tag, 0, $userCanManage, $userCanDelete, $userCanEditInterface );
183 $out->addModuleStyles( [
184 'jquery.tablesorter.styles',
185 'mediawiki.pager.styles'
187 $out->addModules(
'jquery.tablesorter' );
190 [
'class' =>
'mw-datatable sortable mw-tags-table' ],
196 private function doTagRow(
197 $tag, $hitcount, $showManageActions, $showDeleteActions, $showEditLinks
204 if ( $disp ===
false ) {
205 $disp =
Xml::element(
'em',
null, $this->
msg(
'tags-hidden' )->text() );
207 if ( $showEditLinks ) {
209 $editLink = $linkRenderer->makeLink(
210 $this->
msg(
"tag-$tag" )->inContentLanguage()->
getTitle(),
211 $this->
msg(
'tags-edit' )->text(),
213 [
'action' =>
'edit' ]
215 $disp .= $this->
msg(
'parentheses' )->rawParams( $editLink )->escaped();
217 $newRow .=
Xml::tags(
'td',
null, $disp );
219 $msg = $this->
msg(
"tag-$tag-description" );
220 $desc = !$msg->exists() ?
'' : $msg->parse();
221 if ( $showEditLinks ) {
223 $editDescLink = $linkRenderer->makeLink(
224 $this->
msg(
"tag-$tag-description" )->inContentLanguage()->
getTitle(),
225 $this->
msg(
'tags-edit' )->text(),
227 [
'action' =>
'edit' ]
229 $desc .= $this->
msg(
'parentheses' )->rawParams( $editDescLink )->escaped();
231 $newRow .=
Xml::tags(
'td',
null, $desc );
234 $isSoftware = isset( $this->softwareDefinedTags[$tag] );
235 $isExplicit = isset( $this->explicitlyDefinedTags[$tag] );
238 $sourceMsgs[] = $this->
msg(
'tags-source-extension' )->escaped();
241 $sourceMsgs[] = $this->
msg(
'tags-source-manual' )->escaped();
243 if ( !$sourceMsgs ) {
244 $sourceMsgs[] = $this->
msg(
'tags-source-none' )->escaped();
248 $isActive = $isExplicit || isset( $this->softwareActivatedTags[$tag] );
249 $activeMsg = ( $isActive ?
'tags-active-yes' :
'tags-active-no' );
250 $newRow .=
Xml::tags(
'td',
null, $this->
msg( $activeMsg )->escaped() );
252 $hitcountLabelMsg = $this->
msg(
'tags-hitcount' )->numParams( $hitcount );
254 $hitcountLabel = $linkRenderer->makeLink(
256 $hitcountLabelMsg->text(),
258 [
'tagfilter' => $tag ]
261 $hitcountLabel = $hitcountLabelMsg->escaped();
265 $newRow .=
Xml::tags(
'td', [
'data-sort-value' => $hitcount ], $hitcountLabel );
270 $actionLinks[] = $linkRenderer->makeKnownLink(
272 $this->
msg(
'tags-delete' )->text(),
277 if ( $showManageActions ) {
279 $actionLinks[] = $linkRenderer->makeKnownLink(
281 $this->
msg(
'tags-activate' )->text(),
287 $actionLinks[] = $linkRenderer->makeKnownLink(
289 $this->
msg(
'tags-deactivate' )->text(),
295 if ( $showDeleteActions || $showManageActions ) {
299 return Xml::tags(
'tr',
null, $newRow ) .
"\n";
304 $out = $context->getOutput();
306 $tag = trim( strval( $data[
'Tag'] ) );
307 $ignoreWarnings = isset( $data[
'IgnoreWarnings'] ) && $data[
'IgnoreWarnings'] ===
'1';
309 $context->getAuthority(), $ignoreWarnings );
311 if ( $status->isGood() ) {
314 } elseif ( $status->isOK() ) {
318 'IgnoreWarnings' => [
324 $headerText = $this->
msg(
'tags-create-warnings-above', $tag,
325 count( $status->getWarningsArray() ) )->parseAsBlock() .
326 $out->parseAsInterface( $status->getWikiText() ) .
327 $this->
msg(
'tags-create-warnings-below' )->parseAsBlock();
330 ->setSubmitTextMsg(
'htmlform-yes' );
335 $out->wrapWikiTextAsInterface(
'error', $status->getWikiText() );
342 if ( !$authority->isAllowed(
'deletechangetags' ) ) {
347 $out->setPageTitleMsg( $this->
msg(
'tags-delete-title' ) );
352 if ( !$canDeleteResult->isGood() ) {
353 $out->wrapWikiTextAsInterface(
'error', $canDeleteResult->getWikiText() );
354 if ( !$canDeleteResult->isOK() ) {
359 $preText = $this->
msg(
'tags-delete-explanation-initial', $tag )->parseAsBlock();
360 $tagUsage = $this->changeTagsStore->tagUsageStatistics();
361 if ( isset( $tagUsage[$tag] ) && $tagUsage[$tag] > 0 ) {
362 $preText .= $this->
msg(
'tags-delete-explanation-in-use', $tag,
363 $tagUsage[$tag] )->parseAsBlock();
365 $preText .= $this->
msg(
'tags-delete-explanation-warning', $tag )->parseAsBlock();
368 $this->softwareActivatedTags = array_fill_keys(
369 $this->changeTagsStore->listSoftwareActivatedTags(),
true );
370 if ( isset( $this->softwareActivatedTags[$tag] ) ) {
371 $preText .= $this->
msg(
'tags-delete-explanation-active', $tag )->parseAsBlock();
375 $fields[
'Reason'] = [
377 'label' => $this->
msg(
'tags-delete-reason' )->plain(),
380 $fields[
'HiddenTag'] = [
388 ->setAction( $this->
getPageTitle(
'delete' )->getLocalURL() )
389 ->setSubmitCallback(
function ( $data, $form ) {
392 ->setSubmitTextMsg(
'tags-delete-submit' )
393 ->setSubmitDestructive()
394 ->addPreHtml( $preText )
399 $actionStr = $activate ?
'activate' :
'deactivate';
402 if ( !$authority->isAllowed(
'managechangetags' ) ) {
408 $out->setPageTitleMsg( $this->
msg(
"tags-$actionStr-title" ) );
417 if ( !$result->isGood() ) {
418 $out->wrapWikiTextAsInterface(
'error', $result->getWikiText() );
419 if ( !$result->isOK() ) {
425 $preText = $this->
msg(
"tags-$actionStr-question", $tag )->parseAsBlock();
429 $fields[
'Reason'] = [
431 'label' => $this->
msg(
"tags-$actionStr-reason" )->plain(),
434 $fields[
'HiddenTag'] = [
442 ->setAction( $this->
getPageTitle( $actionStr )->getLocalURL() )
443 ->setSubmitCallback(
function ( $data, $form ) use ( $actionStr ) {
447 ->setSubmitTextMsg(
"tags-$actionStr-submit" )
448 ->addPreHtml( $preText )
460 $out = $context->getOutput();
462 $tag = $data[
'HiddenTag'];
464 $status = call_user_func( [ ChangeTags::class,
"{$action}TagWithChecks" ],
465 $tag, $data[
'Reason'], $context->getUser(),
true );
467 if ( $status->isGood() ) {
470 } elseif ( $status->isOK() && $action ===
'delete' ) {
472 $out->addWikiTextAsInterface( $this->
msg(
'tags-delete-warnings-after-delete', $tag,
473 count( $status->getWarningsArray() ) )->text() .
"\n" .
474 $status->getWikitext() );
478 $out->wrapWikiTextAsInterface(
'error', $status->getWikitext() );
507 class_alias( SpecialTags::class,
'SpecialTags' );
getContext()
Get the base IContextSource object.
A class containing constants representing the names of configuration variables.
const UseTagFilter
Name constant for the UseTagFilter setting, for use with Config::get()
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getPageTitle( $subpage=false)
Get a self-referential title object.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getAuthority()
Shortcut to get the Authority executing this instance.
getLanguage()
Shortcut to get user's language.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Show an error when a user tries to do something they do not have the necessary permissions for.
Module of static functions for generating XML.
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.