http://www.mediawiki.org/wiki/Special:Code/MediaWiki/54291
Revision: 54291 Author: btongminh Date: 2009-08-03 17:48:01 +0000 (Mon, 03 Aug 2009) Log Message: ----------- (bug 19004) Added support for tags to the API. Patch by Matthew Britton. Modified Paths: -------------- trunk/phase3/CREDITS trunk/phase3/RELEASE-NOTES trunk/phase3/includes/AutoLoader.php trunk/phase3/includes/ChangeTags.php trunk/phase3/includes/api/ApiQuery.php trunk/phase3/includes/api/ApiQueryLogEvents.php trunk/phase3/includes/api/ApiQueryRecentChanges.php trunk/phase3/includes/api/ApiQueryRevisions.php trunk/phase3/includes/api/ApiQueryUserContributions.php trunk/phase3/includes/specials/SpecialTags.php Added Paths: ----------- trunk/phase3/includes/api/ApiQueryTags.php Modified: trunk/phase3/CREDITS =================================================================== --- trunk/phase3/CREDITS 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/CREDITS 2009-08-03 17:48:01 UTC (rev 54291) @@ -83,6 +83,7 @@ * Marcin Cieślak * Marcus Buck * Marooned +* Matthew Britton * Max Semenik * Michael De La Rue * Michael Walsh Modified: trunk/phase3/RELEASE-NOTES =================================================================== --- trunk/phase3/RELEASE-NOTES 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/RELEASE-NOTES 2009-08-03 17:48:01 UTC (rev 54291) @@ -426,6 +426,7 @@ * (bug 19907) $wgCrossSiteAJAXdomains added to allow specified (or all) external domains to access api.php via AJAX, if the browser supports the Access-Control-Allow-Origin HTTP header +* (bug 19004) Added support for tags to the API. === Languages updated in 1.16 === Modified: trunk/phase3/includes/AutoLoader.php =================================================================== --- trunk/phase3/includes/AutoLoader.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/AutoLoader.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -304,6 +304,7 @@ 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php', 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php', 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php', + 'ApiQueryTags' => 'includes/api/ApiQueryTags.php', 'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php', 'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php', 'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php', Modified: trunk/phase3/includes/ChangeTags.php =================================================================== --- trunk/phase3/includes/ChangeTags.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/ChangeTags.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -185,4 +185,35 @@ $wgMemc->set( $key, $emptyTags, 300 ); return $emptyTags; } + + /** Returns associative array of tag names and hitcounts */ + static function getHitCounts() { + + global $wgMemc; + $key = wfMemcKey( 'hitcounts' ); + + if ($hitcounts = $wgMemc->get( $key )) + return $hitcounts; + + $dbr = wfGetDB( DB_SLAVE ); + $hitcounts = array(); + + // Fetch defined tags + $res = $dbr->select( 'valid_tag', 'vt_tag', array(), __METHOD__ ); + while( $row = $res->fetchObject() ) { + $hitcounts[$row->vt_tag] = 0; + } + + // Fetch hit counts + $res = $dbr->select( 'change_tag', array('ct_tag', 'count(*) AS hitcount'), array(), __METHOD__, array('GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC') ); + + while( $row = $res->fetchObject() ) { + $hitcounts[$row->ct_tag] = $row->hitcount; + } + + // Short-term caching + $wgMemc->set( $key, $hitcounts, 300 ); + return $hitcounts; + } + } Modified: trunk/phase3/includes/api/ApiQuery.php =================================================================== --- trunk/phase3/includes/api/ApiQuery.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/api/ApiQuery.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -74,6 +74,7 @@ 'logevents' => 'ApiQueryLogEvents', 'recentchanges' => 'ApiQueryRecentChanges', 'search' => 'ApiQuerySearch', + 'tags' => 'ApiQueryTags', 'usercontribs' => 'ApiQueryContributions', 'watchlist' => 'ApiQueryWatchlist', 'watchlistraw' => 'ApiQueryWatchlistRaw', Modified: trunk/phase3/includes/api/ApiQueryLogEvents.php =================================================================== --- trunk/phase3/includes/api/ApiQueryLogEvents.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/api/ApiQueryLogEvents.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -51,6 +51,7 @@ $this->fld_timestamp = in_array('timestamp', $prop); $this->fld_comment = in_array('comment', $prop); $this->fld_details = in_array('details', $prop); + $this->fld_tags = in_array('tags', $prop); list($tbl_logging, $tbl_page, $tbl_user) = $db->tableNamesN('logging', 'page', 'user'); @@ -85,6 +86,16 @@ $this->addFieldsIf('log_comment', $this->fld_comment); $this->addFieldsIf('log_params', $this->fld_details); + if($this->fld_tags || !is_null($params['tag'])) { + $this->addTables('tag_summary'); + $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', 'log_id=ts_log_id'))); + $this->addFields('ts_tags'); + } + + if( !is_null($params['tag']) ) { + $this->addWhereFld('ts_tags', $params['tag']); + } + if( !is_null($params['type']) ) { $this->addWhereFld('log_type', $params['type']); $index = 'type_time'; @@ -247,6 +258,10 @@ } } + if ($this->fld_tags && isset($row->ts_tags)) { + $vals['tags'] = $row->ts_tags; + } + return $vals; } @@ -265,6 +280,7 @@ 'timestamp', 'comment', 'details', + 'tags' ) ), 'type' => array ( @@ -285,6 +301,7 @@ ), 'user' => null, 'title' => null, + 'tag' => null, 'limit' => array ( ApiBase :: PARAM_DFLT => 10, ApiBase :: PARAM_TYPE => 'limit', @@ -304,6 +321,7 @@ 'dir' => 'In which direction to enumerate.', 'user' => 'Filter entries to those made by the given user.', 'title' => 'Filter entries to those related to a page.', + 'tag' => 'Only list entries with this tag', 'limit' => 'How many total event entries to return.' ); } Modified: trunk/phase3/includes/api/ApiQueryRecentChanges.php =================================================================== --- trunk/phase3/includes/api/ApiQueryRecentChanges.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/api/ApiQueryRecentChanges.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -42,7 +42,7 @@ private $fld_comment = false, $fld_user = false, $fld_flags = false, $fld_timestamp = false, $fld_title = false, $fld_ids = false, - $fld_sizes = false; + $fld_sizes = false, $fld_tags = false; /** * Get an array mapping token names to their handler functions. * The prototype for a token function is func($pageid, $title, $rc) @@ -174,6 +174,7 @@ $this->fld_redirect = isset($prop['redirect']); $this->fld_patrolled = isset($prop['patrolled']); $this->fld_loginfo = isset($prop['loginfo']); + $this->fld_tags = isset($prop['tags']); global $wgUser; if($this->fld_patrolled && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) @@ -203,6 +204,17 @@ $this->addFields('page_is_redirect'); } } + + if($this->fld_tags || !is_null($params['tag'])) { + $this->addTables('tag_summary'); + $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('rc_id=ts_rc_id')))); + $this->addFields('ts_tags'); + } + + if(!is_null($params['tag'])) { + $this->addWhereFld('ts_tags' , $params['tag']); + } + $this->token = $params['token']; $this->addOption('LIMIT', $params['limit'] +1); $this->addOption('USE INDEX', array('recentchanges' => $index)); @@ -335,6 +347,10 @@ $row->rc_log_type, $row->rc_timestamp); } + if ($this->fld_tags && isset($row->ts_tags)) { + $vals['tags'] = $row->ts_tags; + } + if(!is_null($this->token)) { $tokenFunctions = $this->getTokenFunctions(); @@ -408,6 +424,7 @@ 'redirect', 'patrolled', 'loginfo', + 'tags', ) ), 'token' => array( @@ -443,7 +460,8 @@ 'new', 'log' ) - ) + ), + 'tag' => null, ); } @@ -462,6 +480,7 @@ 'For example, to see only minor edits done by logged-in users, set show=minor|!anon' ), 'type' => 'Which types of changes to show.', + 'tag' => 'Only list changes with this tag', 'limit' => 'How many total changes to return.' ); } Modified: trunk/phase3/includes/api/ApiQueryRevisions.php =================================================================== --- trunk/phase3/includes/api/ApiQueryRevisions.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/api/ApiQueryRevisions.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -42,7 +42,7 @@ } private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false, - $fld_comment = false, $fld_user = false, $fld_content = false; + $fld_comment = false, $fld_user = false, $fld_content = false, $fld_tags = false; protected function getTokenFunctions() { // tokenname => function @@ -121,9 +121,8 @@ } $db = $this->getDB(); - $this->addTables('revision'); + $this->addTables(array('page', 'revision')); $this->addFields(Revision::selectFields()); - $this->addTables('page'); $this->addWhere('page_id = rev_page'); $prop = array_flip($params['prop']); @@ -135,6 +134,7 @@ $this->fld_timestamp = isset ($prop['timestamp']); $this->fld_comment = isset ($prop['comment']); $this->fld_size = isset ($prop['size']); + $this->fld_tags = isset ($prop['tags']); $this->fld_user = isset ($prop['user']); $this->token = $params['token']; $this->diffto = $params['diffto']; @@ -143,6 +143,16 @@ $this->addFields( Revision::selectPageFields() ); } + if ($this->fld_tags || !is_null($params['tag'])) { + $this->addTables('tag_summary'); + $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('rev_id=ts_rev_id')))); + $this->addFields('ts_tags'); + } + + if( !is_null($params['tag']) ) { + $this->addWhereFld('ts_tags', $params['tag']); + } + if (isset ($prop['content'])) { // For each page we will request, the user must have read rights for that page @@ -293,9 +303,9 @@ $this->setContinueEnumParameter('startid', intval($row->rev_id)); break; } - $revision = new Revision( $row ); + // - $fit = $this->addPageSubItem($revision->getPage(), $this->extractRowInfo($revision), 'rev'); + $fit = $this->addPageSubItem($row->rev_page, $this->extractRowInfo($row), 'rev'); if(!$fit) { if($enumRevMode) @@ -311,7 +321,8 @@ $db->freeResult($res); } - private function extractRowInfo( $revision ) { + private function extractRowInfo( $row ) { + $revision = new Revision( $row ); $title = $revision->getTitle(); $vals = array (); @@ -353,6 +364,9 @@ } } + if ($this->fld_tags && $row->ts_tags) + $vals['tags'] = $row->ts_tags; + if(!is_null($this->token)) { $tokenFunctions = $this->getTokenFunctions(); @@ -427,6 +441,7 @@ 'size', 'comment', 'content', + 'tags' ) ), 'limit' => array ( @@ -460,6 +475,7 @@ 'excludeuser' => array( ApiBase :: PARAM_TYPE => 'user' ), + 'tag' => null, 'expandtemplates' => false, 'generatexml' => false, 'section' => null, @@ -483,6 +499,7 @@ 'dir' => 'direction of enumeration - towards "newer" or "older" revisions (enum)', 'user' => 'only include revisions made by user', 'excludeuser' => 'exclude revisions made by user', + 'tag' => 'only list revisions with this tag', 'expandtemplates' => 'expand templates in revision content', 'generatexml' => 'generate XML parse tree for revision content', 'section' => 'only retrieve the content of this section', Added: trunk/phase3/includes/api/ApiQueryTags.php =================================================================== --- trunk/phase3/includes/api/ApiQueryTags.php (rev 0) +++ trunk/phase3/includes/api/ApiQueryTags.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -0,0 +1,167 @@ +<?php + +/* + * Created on Jul 9, 2009 + * + * API for MediaWiki 1.8+ + * + * Copyright (C) 2009 Matthew Britton <firstname>.<lastname>@btinternet.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ('ApiQueryBase.php'); +} + +/** + * Query module to enumerate change tags. + * + * @ingroup API + */ +class ApiQueryTags extends ApiQueryBase { + + private $limit, $result; + private $fld_displayname = false, $fld_description = false, + $fld_hitcount = false; + + public function __construct($query, $moduleName) { + parent :: __construct($query, $moduleName, 'tg'); + } + + public function execute() { + $params = $this->extractRequestParams(); + + $prop = array_flip($params['prop']); + + $this->fld_displayname = isset($prop['displayname']); + $this->fld_description = isset($prop['description']); + $this->fld_hitcount = isset($prop['hitcount']); + + $this->limit = $params['limit']; + $this->result = $this->getResult(); + + $pageSet = $this->getPageSet(); + $titles = $pageSet->getTitles(); + $data = array(); + $ok = true; + + if($this->fld_hitcount) { + foreach( ChangeTags::getHitCounts() as $tag => $count ) { + if(!$ok) break; + $ok = $this->doTag( $tag, $count ); + } + } else { + foreach( ChangeTags::listDefinedTags() as $tag ) { + if(!$ok) break; + $ok = $this->doTag( $tag, 0 ); + } + } + + $this->result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'tag'); + } + + private function doTag( $tagName, $hitcount ) { + static $count = 0; + static $doneTags = array(); + + if ( in_array( $tagName, $doneTags ) ) { + return true; + } + + if(++$count > $this->limit) + { + $this->setContinueEnumParameter('continue', $tagName); + return false; + } + + $tag = array(); + $tag['name'] = $tagName; + + if($this->fld_displayname) + $tag['displayname'] = ChangeTags::tagDescription( $tagName ); + + if($this->fld_description) + { + $msg = wfMsg( "tag-$tagName-description" ); + $msg = wfEmptyMsg( "tag-$tagName-description", $msg ) ? '' : $msg; + $tag['description'] = $msg; + } + + if($this->fld_hitcount) + $tag['hitcount'] = $hitcount; + + $doneTags[] = $tagName; + + $fit = $this->result->addValue(array('query', $this->getModuleName()), null, $tag); + if(!$fit) + { + $this->setContinueEnumParameter('continue', $tagName); + return false; + } + + return true; + } + + public function getAllowedParams() { + return array ( + 'continue' => array( + ), + 'end' => array( + ), + 'limit' => array( + ApiBase :: PARAM_DFLT => 10, + ApiBase :: PARAM_TYPE => 'limit', + ApiBase :: PARAM_MIN => 1, + ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, + ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + ), + 'prop' => array( + ApiBase :: PARAM_DFLT => 'name', + ApiBase :: PARAM_TYPE => array( + 'name', + 'displayname', + 'description', + 'hitcount' + ), + ApiBase :: PARAM_ISMULTI => true + ) + ); + } + + public function getParamDescription() { + return array ( + 'continue' => 'When more results are available, use this to continue', + 'limit' => 'The maximum number of tags to list', + 'prop' => 'Which properties to get', + ); + } + + public function getDescription() { + return 'List change tags.'; + } + + protected function getExamples() { + return array ( + 'api.php?action=query&list=tags&tgprop=displayname|description|hitcount' + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiQueryTags.php'; + } +} Property changes on: trunk/phase3/includes/api/ApiQueryTags.php ___________________________________________________________________ Added: svn:keywords + id Added: svn:eol-style + native Modified: trunk/phase3/includes/api/ApiQueryUserContributions.php =================================================================== --- trunk/phase3/includes/api/ApiQueryUserContributions.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/api/ApiQueryUserContributions.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -42,7 +42,7 @@ private $params, $username; private $fld_ids = false, $fld_title = false, $fld_timestamp = false, $fld_comment = false, $fld_flags = false, - $fld_patrolled = false; + $fld_patrolled = false, $fld_tags = false; public function execute() { @@ -57,6 +57,7 @@ $this->fld_flags = isset($prop['flags']); $this->fld_timestamp = isset($prop['timestamp']); $this->fld_patrolled = isset($prop['patrolled']); + $this->fld_tags = isset($prop['tags']); // TODO: if the query is going only against the revision table, should this be done? $this->selectNamedDB('contributions', DB_SLAVE, 'contributions'); @@ -141,7 +142,7 @@ private function prepareQuery() { // We're after the revision table, and the corresponding page // row for anything we retrieve. We may also need the - // recentchanges row. + // recentchanges row and/or tag summary row. global $wgUser; $tables = array('page', 'revision'); // Order may change $this->addWhere('page_id=rev_page'); @@ -245,6 +246,16 @@ $this->addFieldsIf('rev_minor_edit', $this->fld_flags); $this->addFieldsIf('rev_parent_id', $this->fld_flags); $this->addFieldsIf('rc_patrolled', $this->fld_patrolled); + + if($this->fld_tags || !is_null($this->params['tag'])) { + $this->addTables('tag_summary'); + $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('rev_id=ts_rev_id')))); + $this->addFields('ts_tags'); + } + + if( !is_null($this->params['tag']) ) { + $this->addWhereFld('ts_tags', $this->params['tag']); + } } /** @@ -292,6 +303,9 @@ if ($this->fld_size && !is_null($row->rev_len)) $vals['size'] = intval($row->rev_len); + if ($this->fld_tags && $row->ts_tags) + $vals['tags'] = $row->ts_tags; + return $vals; } @@ -343,6 +357,7 @@ 'size', 'flags', 'patrolled', + 'tags', ) ), 'show' => array ( @@ -354,6 +369,7 @@ '!patrolled', ) ), + 'tag' => null, ); } @@ -370,6 +386,7 @@ 'prop' => 'Include additional pieces of information', 'show' => array('Show only items that meet this criteria, e.g. non minor edits only: show=!minor', 'NOTE: if show=patrolled or show=!patrolled is set, revisions older than $wgRCMaxAge won\'t be shown',), + 'tag' => 'Only list contributions with this tag', ); } Modified: trunk/phase3/includes/specials/SpecialTags.php =================================================================== --- trunk/phase3/includes/specials/SpecialTags.php 2009-08-03 17:04:17 UTC (rev 54290) +++ trunk/phase3/includes/specials/SpecialTags.php 2009-08-03 17:48:01 UTC (rev 54291) @@ -25,17 +25,11 @@ Xml::tags( 'th', null, wfMsgExt( 'tags-description-header', 'parseinline' ) ) . Xml::tags( 'th', null, wfMsgExt( 'tags-hitcount-header', 'parseinline' ) ) ); - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( 'change_tag', array( 'ct_tag', 'count(*) as hitcount' ), array(), __METHOD__, array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' ) ); - while ( $row = $res->fetchObject() ) { - $html .= $this->doTagRow( $row->ct_tag, $row->hitcount ); + foreach( ChangeTags::getHitCounts() as $tag => $hitcount ) { + $html .= $this->doTagRow( $tag, $hitcount ); } - foreach( ChangeTags::listDefinedTags() as $tag ) { - $html .= $this->doTagRow( $tag, 0 ); - } - $wgOut->addHTML( Xml::tags( 'table', array( 'class' => 'wikitable mw-tags-table' ), $html ) ); } _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs