Parent5446 has uploaded a new change for review.
https://gerrit.wikimedia.org/r/64650
Change subject: Added API action=changetags for ChangeTags
......................................................................
Added API action=changetags for ChangeTags
Added new API module for action=changetags that allows users
to view the ChangeTags associated with a revision. It also
allows users with the new 'changetags' permission to add tags
via the API. Only tags in $wgApiChangeTags are allowed to be
added via the API.
Change-Id: I8ace1d47290664d39973f541be71b933e454b9c9
---
M RELEASE-NOTES-1.22
M includes/AutoLoader.php
M includes/ChangeTags.php
M includes/DefaultSettings.php
A includes/api/ApiChangeTags.php
M includes/api/ApiMain.php
6 files changed, 258 insertions(+), 35 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/50/64650/1
diff --git a/RELEASE-NOTES-1.22 b/RELEASE-NOTES-1.22
index cd7971b..7bb122f 100644
--- a/RELEASE-NOTES-1.22
+++ b/RELEASE-NOTES-1.22
@@ -80,6 +80,10 @@
which can be cascading (previously 'sysop' was hard-coded as the only one).
* XHTML5 support has been improved. If you set $wgMimeType =
'application/xhtml+xml'
MediaWiki will try outputting markup acording to XHTML5 rules.
+* Revision tags (as displayed on Special:Tags) can now be added to changes via
the
+ API rather than just on the server-side. $wgApiChangeTags contains the tags
that
+ are allowed to be changed, and the 'changetags' permission allows users to
add
+ those tags.
=== Bug fixes in 1.22 ===
* Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 26d5b94..dbe79a1 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -332,6 +332,7 @@
# includes/api
'ApiBase' => 'includes/api/ApiBase.php',
'ApiBlock' => 'includes/api/ApiBlock.php',
+ 'ApiChangeTags' => 'includes/api/ApiChangeTags.php',
'ApiComparePages' => 'includes/api/ApiComparePages.php',
'ApiCreateAccount' => 'includes/api/ApiCreateAccount.php',
'ApiDelete' => 'includes/api/ApiDelete.php',
diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php
index 5478396..90f7a40 100644
--- a/includes/ChangeTags.php
+++ b/includes/ChangeTags.php
@@ -74,50 +74,61 @@
}
/**
+ * Get tags to a change given its rc_id, rev_id and/or log_id
+ *
+ * @param int $rc_id Recent changes ID to get tags for
+ * @param int $rev_id Revision ID to get tags for
+ * @param int $log_id Log entry ID to get tags for
+ *
+ * @exception MWException when $rc_id, $rev_id and $log_id are all null
+ * @return array Mapping of tag name to the its string parameter
+ */
+ static function getTags( $rc_id = null, $rev_id = null, $log_id = null,
$fromMaster = false ) {
+ self::fetchTagIds( $rc_id, $rev_id, $log_id );
+
+ $res = wfGetDB( $fromMaster ? DB_MASTER : DB_SLAVE )->select(
+ 'change_tag',
+ array( 'ct_tag', 'ct_params' ),
+ array_filter( array(
+ 'ct_rc_id' => $rc_id,
+ 'ct_rev_id' => $rev_id,
+ 'ct_log_id' => $log_id
+ ) ),
+ __METHOD__
+ );
+
+ $tags = array();
+ foreach ( $res as $row ) {
+ $tags[$row->ct_tag] = $row->ct_params;
+ }
+
+ return $tags;
+ }
+
+ /**
* Add tags to a change given its rc_id, rev_id and/or log_id
*
* @param string|array $tags Tags to add to the change
- * @param $rc_id int: rc_id of the change to add the tags to
- * @param $rev_id int: rev_id of the change to add the tags to
- * @param $log_id int: log_id of the change to add the tags to
+ * @param int $rc_id rc_id of the change to add the tags to
+ * @param int $rev_id rev_id of the change to add the tags to
+ * @param int $log_id log_id of the change to add the tags to
* @param string $params params to put in the ct_params field of table
'change_tag'
*
- * @throws MWException
- * @return bool: false if no changes are made, otherwise true
- *
* @exception MWException when $rc_id, $rev_id and $log_id are all null
+ * @return bool: false if no changes are made, otherwise true
*/
static function addTags( $tags, $rc_id = null, $rev_id = null, $log_id
= null, $params = null ) {
if ( !is_array( $tags ) ) {
$tags = array( $tags );
}
-
$tags = array_filter( $tags ); // Make sure we're submitting
all tags...
- if ( !$rc_id && !$rev_id && !$log_id ) {
- throw new MWException( "At least one of: RCID, revision
ID, and log ID MUST be specified when adding a tag to a change!" );
- }
-
- $dbr = wfGetDB( DB_SLAVE );
-
- // Might as well look for rcids and so on.
- if ( !$rc_id ) {
- $dbr = wfGetDB( DB_MASTER ); // Info might be out of
date, somewhat fractionally, on slave.
- if ( $log_id ) {
- $rc_id = $dbr->selectField( 'recentchanges',
'rc_id', array( 'rc_logid' => $log_id ), __METHOD__ );
- } elseif ( $rev_id ) {
- $rc_id = $dbr->selectField( 'recentchanges',
'rc_id', array( 'rc_this_oldid' => $rev_id ), __METHOD__ );
- }
- } elseif ( !$log_id && !$rev_id ) {
- $dbr = wfGetDB( DB_MASTER ); // Info might be out of
date, somewhat fractionally, on slave.
- $log_id = $dbr->selectField( 'recentchanges',
'rc_logid', array( 'rc_id' => $rc_id ), __METHOD__ );
- $rev_id = $dbr->selectField( 'recentchanges',
'rc_this_oldid', array( 'rc_id' => $rc_id ), __METHOD__ );
- }
+ self::fetchTagIds( $rc_id, $rev_id, $log_id );
$tsConds = array_filter( array( 'ts_rc_id' => $rc_id,
'ts_rev_id' => $rev_id, 'ts_log_id' => $log_id ) );
## Update the summary row.
- $prevTags = $dbr->selectField( 'tag_summary', 'ts_tags',
$tsConds, __METHOD__ );
+ $prevTags = wfGetDB( DB_SLAVE )->selectField( 'tag_summary',
'ts_tags', $tsConds, __METHOD__ );
$prevTags = $prevTags ? $prevTags : '';
$prevTags = array_filter( explode( ',', $prevTags ) );
$newTags = array_unique( array_merge( $prevTags, $tags ) );
@@ -128,14 +139,6 @@
// No change.
return false;
}
-
- $dbw = wfGetDB( DB_MASTER );
- $dbw->replace(
- 'tag_summary',
- array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ),
- array_filter( array_merge( $tsConds, array( 'ts_tags'
=> implode( ',', $newTags ) ) ) ),
- __METHOD__
- );
// Insert the tags rows.
$tagsRows = array();
@@ -151,12 +154,61 @@
);
}
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+ $dbw->replace(
+ 'tag_summary',
+ array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ),
+ array_filter( array_merge( $tsConds, array( 'ts_tags'
=> implode( ',', $newTags ) ) ) ),
+ __METHOD__
+ );
$dbw->insert( 'change_tag', $tagsRows, __METHOD__, array(
'IGNORE' ) );
+ $dbw->commit();
return true;
}
/**
+ * Given one or more of a rc/rev/log ID, fetch the remaining IDs for
the change tag
+ * associated with the given ID.
+ *
+ * @param int &$rc_id Recent changes ID
+ * @param int &$rev_id Revision ID
+ * @param int &$log_id Log entry ID
+ * @param bool $force Force checking the database even if all three IDs
are provided
+ *
+ * @return bool True if the IDs given matched something, false if
invalid
+ */
+ static function fetchTagIds( &$rc_id = null, &$rev_id = null, &$log_id
= null, $force = false ) {
+ if ( !$rc_id && !$rev_id && !$log_id ) {
+ throw new MWException( "At least one of: RCID, revision
ID, and log ID MUST be specified when adding a tag to a change!" );
+ } elseif ( !$force && $rc_id && $rev_id && $log_id ) {
+ // If we already have all three and no force, skip the
database query.
+ return true;
+ }
+
+ // Info might be out of date, somewhat fractionally, on slave.
+ $res = wfGetDB( DB_MASTER )->selectRow(
+ 'recentchanges',
+ array( 'rc_id', 'rc_logid', 'rc_this_oldid' ),
+ array_filter( array(
+ 'rc_id' => $rc_id,
+ 'rc_this_oldid' => $rev_id,
+ 'rc_logid' => $log_id
+ ) ),
+ __METHOD__
+ );
+
+ if ( $res ) {
+ $rc_id = $res->rc_id;
+ $rev_id = $res->rc_this_oldid;
+ $log_id = $res->rc_logid;
+ }
+
+ return (bool)$res;
+ }
+
+ /**
* Applies all tags-related changes to a query.
* Handles selecting tags, and filtering.
* Needs $tables to be set up properly, so we can figure out which join
conditions to use.
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 819e9a3..3194c2a 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -3899,6 +3899,7 @@
$wgGroupPermissions['bot']['suppressredirect'] = true;
$wgGroupPermissions['bot']['apihighlimits'] = true;
$wgGroupPermissions['bot']['writeapi'] = true;
+$wgGroupPermissions['bot']['changetags'] = true;
#$wgGroupPermissions['bot']['editprotected'] = true; // can edit all protected
pages without cascade protection enabled
// Most extra permission abilities go to this group
@@ -5996,6 +5997,14 @@
$wgAPIRequestLog = false;
/**
+ * Array of ChangeTags that can be added to changes via the API. All other
+ * ChangeTags can only be added on the server side by extensions.
+ * @see ChangeTags
+ * @since 1.22
+ */
+$wgApiChangeTags = array();
+
+/**
* Set the timeout for the API help text cache. If set to 0, caching disabled
*/
$wgAPICacheHelpTimeout = 60 * 60;
diff --git a/includes/api/ApiChangeTags.php b/includes/api/ApiChangeTags.php
new file mode 100644
index 0000000..83c42b3
--- /dev/null
+++ b/includes/api/ApiChangeTags.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * Recent changes tagging.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * API module that allows users to view/add ChangeTags for a specific
+ * revision/recent change/log entry.
+ *
+ * @see ChangeTags
+ * @since 1.22
+ * @ingroup API
+ */
+class ApiChangeTags extends ApiBase {
+ function execute() {
+ $params = $this->extractRequestParams();
+ $this->requireAtLeastOneParameter( $params, 'rcid', 'revid',
'logid' );
+
+ // Try to get a match for each parameter.
+ $valid = ChangeTags::fetchTagIds( $params['rcid'],
$params['revid'], $params['logid'], true );
+ if ( !$valid ) {
+ $this->dieUsage( 'No valid revid, rcid, or logid was
supplied.', 'invalid' );
+ }
+
+ // Get the title for permission checks.
+ $title = Revision::newFromId( $params['revid'] )->getTitle();
+
+ $wasPosted = $this->getRequest()->wasPosted();
+
+ if ( $wasPosted ) {
+ // Need the changetags permission to change tags
+ $permission = 'changetags';
+ } else {
+ // Otherwise, anybody who can read the page can see the
tags in the history anyway
+ $permission = 'read';
+ }
+
+ $errors = $title->getUserPermissionsErrors( $permission,
$this->getUser() );
+ if ( $errors ) {
+ $this->dieUsageMsg( $errors[0] );
+ }
+
+ // Actually get/change the tags.
+ $currentTags = array();
+ if ( $wasPosted ) {
+ ChangeTags::addTags( $params['tags'], $params['rcid'],
$params['revid'], $params['logid'], $currentTags );
+ }
+
+ $currentTags = ChangeTags::getTags( $params['rcid'],
$params['revid'], $params['logid'], $wasPosted );
+
+ $info = array(
+ 'rcid' => $params['rcid'],
+ 'revid' => $params['revid'],
+ 'logid' => $params['logid'],
+ );
+ foreach ( $currentTags as $tag => $params ) {
+ $info[] = array( 'name' => $tag, '*' => $params );
+ }
+
+ $result = $this->getResult();
+ $result->setIndexedTagName( $info, 'tag' );
+ $this->getResult()->addValue( null, $this->getModuleName(),
$info );
+ }
+
+ function getDescription() {
+ return 'Add change tags to a certain revision, recent change,
or log entry.';
+ }
+
+ function getAllowedParams() {
+ global $wgApiChangeTags;
+
+ return array(
+ 'revid' => array(
+ self::PARAM_TYPE => 'integer',
+ ),
+ 'rcid' => array(
+ self::PARAM_TYPE => 'integer',
+ ),
+ 'logid' => array(
+ self::PARAM_TYPE => 'integer',
+ ),
+ 'tags' => array(
+ self::PARAM_TYPE => array_intersect(
+ ChangeTags::listDefinedTags(),
+ $wgApiChangeTags
+ ),
+ self::PARAM_ISMULTI => true
+ )
+ );
+ }
+
+ function getParamDescription() {
+ return array(
+ 'revid' => 'ID of the revision to add tags to',
+ 'rcid' => 'ID of the recent change to add tags to',
+ 'logid' => 'ID of the log entry to add tags to',
+ 'tags' => 'Tags to add to the specified object',
+ );
+ }
+
+ function getResultProperties() {
+ return array( '' => array(
+ 'revid' => array(
+ self::PROP_TYPE => 'integer',
+ self::PROP_NULLABLE => true,
+ ),
+ 'rcid' => array(
+ self::PROP_TYPE => 'integer',
+ self::PROP_NULLABLE => true,
+ ),
+ 'logid' => array(
+ self::PROP_TYPE => 'integer',
+ self::PROP_NULLABLE => true,
+ ),
+ 'tags' => array(
+ self::PROP_TYPE => 'string',
+ self::PROP_LIST => true,
+ )
+ ) );
+ }
+
+ function getPossibleErrors() {
+ return array_merge(
+ parent::getPossibleErrors(),
+ self::getRequireAtLeastOneParameterErrorMessages(
'revid', 'rcid', 'logid' )
+ );
+ }
+
+ function getExamples() {
+ return array(
+ 'api.php?action=changetags&rcid=105',
+ 'api.php?action=changetags&rcid=107&logid=106&revid=32'
+ );
+ }
+
+ function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:ChangeTags';
+ }
+}
\ No newline at end of file
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 5ddb3ab..c9ec787 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -84,6 +84,7 @@
'userrights' => 'ApiUserrights',
'options' => 'ApiOptions',
'imagerotate' => 'ApiImageRotate',
+ 'changetags' => 'ApiChangeTags'
);
/**
--
To view, visit https://gerrit.wikimedia.org/r/64650
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8ace1d47290664d39973f541be71b933e454b9c9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Parent5446 <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits