public function execute() { $params = $this->extractRequestParams(); $prop = array_flip($params['prop']); $fld_displayname = isset($prop['displayname']); $fld_description = isset($prop['description']); $fld_hitcount = isset($prop['hitcount']); $fld_defined = isset($prop['defined']); $fld_source = isset($prop['source']); $fld_active = isset($prop['active']); $limit = $params['limit']; $result = $this->getResult(); $extensionDefinedTags = array_fill_keys(ChangeTags::listExtensionDefinedTags(), 0); $explicitlyDefinedTags = array_fill_keys(ChangeTags::listExplicitlyDefinedTags(), 0); $extensionActivatedTags = array_fill_keys(ChangeTags::listExtensionActivatedTags(), 0); $definedTags = array_merge($extensionDefinedTags, $explicitlyDefinedTags); # Fetch defined tags that aren't past the continuation if ($params['continue'] !== null) { $cont = $params['continue']; $tags = array_filter(array_keys($definedTags), function ($v) use($cont) { return $v >= $cont; }); $tags = array_fill_keys($tags, 0); } else { $tags = $definedTags; } # Merge in all used tags $this->addTables('change_tag'); $this->addFields('ct_tag'); $this->addFields(array('hitcount' => $fld_hitcount ? 'COUNT(*)' : '0')); $this->addOption('LIMIT', $limit + 1); $this->addOption('GROUP BY', 'ct_tag'); $this->addWhereRange('ct_tag', 'newer', $params['continue'], null); $res = $this->select(__METHOD__); foreach ($res as $row) { $tags[$row->ct_tag] = (int) $row->hitcount; } # Now make sure the array is sorted for proper continuation ksort($tags); $count = 0; foreach ($tags as $tagName => $hitcount) { if (++$count > $limit) { $this->setContinueEnumParameter('continue', $tagName); break; } $tag = array(); $tag['name'] = $tagName; if ($fld_displayname) { $tag['displayname'] = ChangeTags::tagDescription($tagName); } if ($fld_description) { $msg = $this->msg("tag-{$tagName}-description"); $tag['description'] = $msg->exists() ? $msg->text() : ''; } if ($fld_hitcount) { $tag['hitcount'] = $hitcount; } $isExtension = isset($extensionDefinedTags[$tagName]); $isExplicit = isset($explicitlyDefinedTags[$tagName]); if ($fld_defined) { $tag['defined'] = $isExtension || $isExplicit; } if ($fld_source) { $tag['source'] = array(); if ($isExtension) { $tag['source'][] = 'extension'; } if ($isExplicit) { $tag['source'][] = 'manual'; } } if ($fld_active) { $tag['active'] = $isExplicit || isset($extensionActivatedTags[$tagName]); } $fit = $result->addValue(array('query', $this->getModuleName()), null, $tag); if (!$fit) { $this->setContinueEnumParameter('continue', $tagName); break; } } $result->addIndexedTagName(array('query', $this->getModuleName()), 'tag'); }
function showTagList() { $out = $this->getOutput(); $out->setPageTitle($this->msg('tags-title')); $out->wrapWikiMsg("<div class='mw-tags-intro'>\n\$1\n</div>", 'tags-intro'); $user = $this->getUser(); $userCanManage = $user->isAllowed('managechangetags'); $userCanDelete = $user->isAllowed('deletechangetags'); $userCanEditInterface = $user->isAllowed('editinterface'); // Show form to create a tag if ($userCanManage) { $fields = ['Tag' => ['type' => 'text', 'label' => $this->msg('tags-create-tag-name')->plain(), 'required' => true], 'Reason' => ['type' => 'text', 'label' => $this->msg('tags-create-reason')->plain(), 'size' => 50], 'IgnoreWarnings' => ['type' => 'hidden']]; $form = new HTMLForm($fields, $this->getContext()); $form->setAction($this->getPageTitle('create')->getLocalURL()); $form->setWrapperLegendMsg('tags-create-heading'); $form->setHeaderText($this->msg('tags-create-explanation')->parseAsBlock()); $form->setSubmitCallback([$this, 'processCreateTagForm']); $form->setSubmitTextMsg('tags-create-submit'); $form->show(); // If processCreateTagForm generated a redirect, there's no point // continuing with this, as the user is just going to end up getting sent // somewhere else. Additionally, if we keep going here, we end up // populating the memcache of tag data (see ChangeTags::listDefinedTags) // with out-of-date data from the slave, because the slave hasn't caught // up to the fact that a new tag has been created as part of an implicit, // as yet uncommitted transaction on master. if ($out->getRedirect() !== '') { return; } } // Used to get hitcounts for #doTagRow() $tagStats = ChangeTags::tagUsageStatistics(); // Used in #doTagRow() $this->explicitlyDefinedTags = array_fill_keys(ChangeTags::listExplicitlyDefinedTags(), true); $this->extensionDefinedTags = array_fill_keys(ChangeTags::listExtensionDefinedTags(), true); // List all defined tags, even if they were never applied $definedTags = array_keys(array_merge($this->explicitlyDefinedTags, $this->extensionDefinedTags)); // Show header only if there exists atleast one tag if (!$tagStats && !$definedTags) { return; } // Write the headers $html = Xml::tags('tr', null, Xml::tags('th', null, $this->msg('tags-tag')->parse()) . Xml::tags('th', null, $this->msg('tags-display-header')->parse()) . Xml::tags('th', null, $this->msg('tags-description-header')->parse()) . Xml::tags('th', null, $this->msg('tags-source-header')->parse()) . Xml::tags('th', null, $this->msg('tags-active-header')->parse()) . Xml::tags('th', null, $this->msg('tags-hitcount-header')->parse()) . ($userCanManage ? Xml::tags('th', ['class' => 'unsortable'], $this->msg('tags-actions-header')->parse()) : '')); // Used in #doTagRow() $this->extensionActivatedTags = array_fill_keys(ChangeTags::listExtensionActivatedTags(), true); // Insert tags that have been applied at least once foreach ($tagStats as $tag => $hitcount) { $html .= $this->doTagRow($tag, $hitcount, $userCanManage, $userCanDelete, $userCanEditInterface); } // Insert tags defined somewhere but never applied foreach ($definedTags as $tag) { if (!isset($tagStats[$tag])) { $html .= $this->doTagRow($tag, 0, $userCanManage, $userCanDelete, $userCanEditInterface); } } $out->addHTML(Xml::tags('table', ['class' => 'mw-datatable sortable mw-tags-table'], $html)); }
function showTagList() { $out = $this->getOutput(); $out->setPageTitle($this->msg('tags-title')); $out->wrapWikiMsg("<div class='mw-tags-intro'>\n\$1\n</div>", 'tags-intro'); $user = $this->getUser(); // Show form to create a tag if ($user->isAllowed('managechangetags')) { $fields = array('Tag' => array('type' => 'text', 'label' => $this->msg('tags-create-tag-name')->plain(), 'required' => true), 'Reason' => array('type' => 'text', 'label' => $this->msg('tags-create-reason')->plain(), 'size' => 50), 'IgnoreWarnings' => array('type' => 'hidden')); $form = new HTMLForm($fields, $this->getContext()); $form->setAction($this->getPageTitle('create')->getLocalURL()); $form->setWrapperLegendMsg('tags-create-heading'); $form->setHeaderText($this->msg('tags-create-explanation')->plain()); $form->setSubmitCallback(array($this, 'processCreateTagForm')); $form->setSubmitTextMsg('tags-create-submit'); $form->show(); // If processCreateTagForm generated a redirect, there's no point // continuing with this, as the user is just going to end up getting sent // somewhere else. Additionally, if we keep going here, we end up // populating the memcache of tag data (see ChangeTags::listDefinedTags) // with out-of-date data from the slave, because the slave hasn't caught // up to the fact that a new tag has been created as part of an implicit, // as yet uncommitted transaction on master. if ($out->getRedirect() !== '') { return; } } // Whether to show the "Actions" column in the tag list // If any actions added in the future require other user rights, add those // rights here $showActions = $user->isAllowed('managechangetags'); // Write the headers $tagUsageStatistics = ChangeTags::tagUsageStatistics(); // Show header only if there exists atleast one tag if (!$tagUsageStatistics) { return; } $html = Xml::tags('tr', null, Xml::tags('th', null, $this->msg('tags-tag')->parse()) . Xml::tags('th', null, $this->msg('tags-display-header')->parse()) . Xml::tags('th', null, $this->msg('tags-description-header')->parse()) . Xml::tags('th', null, $this->msg('tags-source-header')->parse()) . Xml::tags('th', null, $this->msg('tags-active-header')->parse()) . Xml::tags('th', null, $this->msg('tags-hitcount-header')->parse()) . ($showActions ? Xml::tags('th', array('class' => 'unsortable'), $this->msg('tags-actions-header')->parse()) : '')); // Used in #doTagRow() $this->explicitlyDefinedTags = array_fill_keys(ChangeTags::listExplicitlyDefinedTags(), true); $this->extensionDefinedTags = array_fill_keys(ChangeTags::listExtensionDefinedTags(), true); $this->extensionActivatedTags = array_fill_keys(ChangeTags::listExtensionActivatedTags(), true); foreach ($tagUsageStatistics as $tag => $hitcount) { $html .= $this->doTagRow($tag, $hitcount, $showActions); } $out->addHTML(Xml::tags('table', array('class' => 'mw-datatable sortable mw-tags-table'), $html)); }