*
* @since 1.25
*/
- public static function updateTags( $tagsToAdd, $tagsToRemove, &$rc_id = null,
- &$rev_id = null, &$log_id = null, $params = null ) {
+ public static function updateTags(
+ $tagsToAdd, $tagsToRemove,
+ &$rc_id = null, &$rev_id = null, &$log_id = null, $params = null
+ ) {
$tagsToAdd = array_filter( (array)$tagsToAdd ); // Make sure we're submitting all tags...
$tagsToRemove = array_filter( (array)$tagsToRemove );
// Might as well look for rcids and so on.
if ( !$rc_id ) {
// Info might be out of date, somewhat fractionally, on slave.
+ // LogEntry/LogPage and WikiPage match rev/log/rc timestamps,
+ // so use that relation to avoid full table scans.
if ( $log_id ) {
$rc_id = $dbw->selectField(
- 'recentchanges',
+ array( 'logging', 'recentchanges' ),
'rc_id',
- array( 'rc_logid' => $log_id ),
+ array(
+ 'log_id' => $log_id,
+ 'rc_timestamp = log_timestamp',
+ 'rc_logid = log_id'
+ ),
__METHOD__
);
} elseif ( $rev_id ) {
$rc_id = $dbw->selectField(
- 'recentchanges',
+ array( 'revision', 'recentchanges' ),
'rc_id',
- array( 'rc_this_oldid' => $rev_id ),
+ array(
+ 'rev_id' => $rev_id,
+ 'rc_timestamp = rev_timestamp',
+ 'rc_this_oldid = rev_id'
+ ),
__METHOD__
);
}
public static function canAddTagsAccompanyingChange( array $tags,
User $user = null ) {
- if ( !is_null( $user ) && !$user->isAllowed( 'applychangetags' ) ) {
- return Status::newFatal( 'tags-apply-no-permission' );
+ if ( !is_null( $user ) ) {
+ if ( !$user->isAllowed( 'applychangetags' ) ) {
+ return Status::newFatal( 'tags-apply-no-permission' );
+ } elseif ( $user->isBlocked() ) {
+ return Status::newFatal( 'tags-apply-blocked' );
+ }
}
// to be applied, a tag has to be explicitly defined
public static function canUpdateTags( array $tagsToAdd, array $tagsToRemove,
User $user = null ) {
- if ( !is_null( $user ) && !$user->isAllowed( 'changetags' ) ) {
- return Status::newFatal( 'tags-update-no-permission' );
+ if ( !is_null( $user ) ) {
+ if ( !$user->isAllowed( 'changetags' ) ) {
+ return Status::newFatal( 'tags-update-no-permission' );
+ } elseif ( $user->isBlocked() ) {
+ return Status::newFatal( 'tags-update-blocked' );
+ }
}
- // to be added, a tag has to be explicitly defined
- // @todo Allow extensions to define tags that can be applied by users...
- $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
- $diff = array_diff( $tagsToAdd, $explicitlyDefinedTags );
- if ( $diff ) {
- return self::restrictedTagError( 'tags-update-add-not-allowed-one',
- 'tags-update-add-not-allowed-multi', $diff );
+ if ( $tagsToAdd ) {
+ // to be added, a tag has to be explicitly defined
+ // @todo Allow extensions to define tags that can be applied by users...
+ $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
+ $diff = array_diff( $tagsToAdd, $explicitlyDefinedTags );
+ if ( $diff ) {
+ return self::restrictedTagError( 'tags-update-add-not-allowed-one',
+ 'tags-update-add-not-allowed-multi', $diff );
+ }
}
- // to be removed, a tag has to be either explicitly defined or not defined
- // at all
- $definedTags = self::listDefinedTags();
- $diff = array_diff( $tagsToRemove, $explicitlyDefinedTags );
- if ( $diff ) {
- $intersect = array_intersect( $diff, $definedTags );
+ if ( $tagsToRemove ) {
+ // to be removed, a tag must not be defined by an extension, or equivalently it
+ // has to be either explicitly defined or not defined at all
+ // (assuming no edge case of a tag both explicitly-defined and extension-defined)
+ $extensionDefinedTags = self::listExtensionDefinedTags();
+ $intersect = array_intersect( $tagsToRemove, $extensionDefinedTags );
if ( $intersect ) {
return self::restrictedTagError( 'tags-update-remove-not-allowed-one',
'tags-update-remove-not-allowed-multi', $intersect );
* Build a text box to select a change tag
*
* @param string $selected Tag to select by default
- * @param bool $fullForm
- * - if false, then it returns an array of (label, form).
- * - if true, it returns an entire form around the selector.
- * @param Title $title Title object to send the form to.
- * Used when, and only when $fullForm is true.
+ * @param bool $fullForm Affects return value, see below
+ * @param Title $title Title object to send the form to. Used only if $fullForm is true.
+ * @param bool $ooui Use an OOUI TextInputWidget as selector instead of a non-OOUI input field
+ * You need to call OutputPage::enableOOUI() yourself.
* @return string|array
- * - if $fullForm is false: Array with
- * - if $fullForm is true: String, html fragment
+ * - if $fullForm is false: an array of (label, selector).
+ * - if $fullForm is true: HTML of entire form built around the selector.
*/
public static function buildTagFilterSelector( $selected = '',
- $fullForm = false, Title $title = null
+ $fullForm = false, Title $title = null, $ooui = false
) {
global $wgUseTagFilter;
'label',
array( 'for' => 'tagfilter' ),
wfMessage( 'tag-filter' )->parse()
- ),
- Xml::input(
+ )
+ );
+
+ if ( $ooui ) {
+ $data[] = new OOUI\TextInputWidget( array(
+ 'id' => 'tagfilter',
+ 'name' => 'tagfilter',
+ 'value' => $selected,
+ 'classes' => 'mw-tagfilter-input',
+ ) );
+ } else {
+ $data[] = Xml::input(
'tagfilter',
20,
$selected,
array( 'class' => 'mw-tagfilter-input mw-ui-input mw-ui-input-inline', 'id' => 'tagfilter' )
- )
- );
+ );
+ }
if ( !$fullForm ) {
return $data;
* @since 1.25
*/
public static function canActivateTag( $tag, User $user = null ) {
- if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
- return Status::newFatal( 'tags-manage-no-permission' );
- }
-
- // non-existing tags cannot be activated
- $tagUsage = self::tagUsageStatistics();
- if ( !isset( $tagUsage[$tag] ) ) {
- return Status::newFatal( 'tags-activate-not-found', $tag );
+ if ( !is_null( $user ) ) {
+ if ( !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ } elseif ( $user->isBlocked() ) {
+ return Status::newFatal( 'tags-manage-blocked' );
+ }
}
// defined tags cannot be activated (a defined tag is either extension-
return Status::newFatal( 'tags-activate-not-allowed', $tag );
}
+ // non-existing tags cannot be activated
+ $tagUsage = self::tagUsageStatistics();
+ if ( !isset( $tagUsage[$tag] ) ) { // we already know the tag is undefined
+ return Status::newFatal( 'tags-activate-not-found', $tag );
+ }
+
return Status::newGood();
}
* @since 1.25
*/
public static function canDeactivateTag( $tag, User $user = null ) {
- if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
- return Status::newFatal( 'tags-manage-no-permission' );
+ if ( !is_null( $user ) ) {
+ if ( !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ } elseif ( $user->isBlocked() ) {
+ return Status::newFatal( 'tags-manage-blocked' );
+ }
}
// only explicitly-defined tags can be deactivated
* @since 1.25
*/
public static function canCreateTag( $tag, User $user = null ) {
- if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
- return Status::newFatal( 'tags-manage-no-permission' );
+ if ( !is_null( $user ) ) {
+ if ( !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ } elseif ( $user->isBlocked() ) {
+ return Status::newFatal( 'tags-manage-blocked' );
+ }
}
// no empty tags
// does the tag already exist?
$tagUsage = self::tagUsageStatistics();
- if ( isset( $tagUsage[$tag] ) ) {
+ if ( isset( $tagUsage[$tag] ) || in_array( $tag, self::listDefinedTags() ) ) {
return Status::newFatal( 'tags-create-already-exists', $tag );
}
public static function canDeleteTag( $tag, User $user = null ) {
$tagUsage = self::tagUsageStatistics();
- if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
- return Status::newFatal( 'tags-manage-no-permission' );
+ if ( !is_null( $user ) ) {
+ if ( !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ } elseif ( $user->isBlocked() ) {
+ return Status::newFatal( 'tags-manage-blocked' );
+ }
}
- if ( !isset( $tagUsage[$tag] ) ) {
+ if ( !isset( $tagUsage[$tag] ) && !in_array( $tag, self::listDefinedTags() ) ) {
return Status::newFatal( 'tags-delete-not-found', $tag );
}
- if ( $tagUsage[$tag] > self::MAX_DELETE_USES ) {
+ if ( isset( $tagUsage[$tag] ) && $tagUsage[$tag] > self::MAX_DELETE_USES ) {
return Status::newFatal( 'tags-delete-too-many-uses', $tag, self::MAX_DELETE_USES );
}
// store the tag usage statistics
$tagUsage = self::tagUsageStatistics();
+ $hitcount = isset( $tagUsage[$tag] ) ? $tagUsage[$tag] : 0;
// do it!
$deleteResult = self::deleteTagEverywhere( $tag );
}
// log it
- $logId = self::logTagManagementAction( 'delete', $tag, $reason, $user, $tagUsage[$tag] );
+ $logId = self::logTagManagementAction( 'delete', $tag, $reason, $user, $hitcount );
$deleteResult->value = $logId;
return $deleteResult;
}
public static function listExtensionActivatedTags() {
return ObjectCache::getMainWANInstance()->getWithSetCallback(
wfMemcKey( 'active-tags' ),
- function() {
+ 300,
+ function ( $oldValue, &$ttl, array &$setOpts ) {
+ $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+
// Ask extensions which tags they consider active
$extensionActive = array();
Hooks::run( 'ChangeTagsListActive', array( &$extensionActive ) );
return $extensionActive;
},
- 300,
- array( wfMemcKey( 'active-tags' ) ),
- array( 'lockTSE' => INF )
+ array(
+ 'checkKeys' => array( wfMemcKey( 'active-tags' ) ),
+ 'lockTSE' => 300,
+ 'pcTTL' => 30
+ )
);
}
return ObjectCache::getMainWANInstance()->getWithSetCallback(
wfMemcKey( 'valid-tags-db' ),
- function() use ( $fname ) {
+ 300,
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
$dbr = wfGetDB( DB_SLAVE );
- $tags = $dbr->selectFieldValues(
- 'valid_tag', 'vt_tag', array(), $fname );
+
+ $setOpts += Database::getCacheSetOptions( $dbr );
+
+ $tags = $dbr->selectFieldValues( 'valid_tag', 'vt_tag', array(), $fname );
return array_filter( array_unique( $tags ) );
},
- 300,
- array( wfMemcKey( 'valid-tags-db' ) ),
- array( 'lockTSE' => INF )
+ array(
+ 'checkKeys' => array( wfMemcKey( 'valid-tags-db' ) ),
+ 'lockTSE' => 300,
+ 'pcTTL' => 30
+ )
);
}
public static function listExtensionDefinedTags() {
return ObjectCache::getMainWANInstance()->getWithSetCallback(
wfMemcKey( 'valid-tags-hook' ),
- function() {
+ 300,
+ function ( $oldValue, &$ttl, array &$setOpts ) {
+ $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+
$tags = array();
Hooks::run( 'ListDefinedTags', array( &$tags ) );
return array_filter( array_unique( $tags ) );
},
- 300,
- array( wfMemcKey( 'valid-tags-hook' ) ),
- array( 'lockTSE' => INF )
+ array(
+ 'checkKeys' => array( wfMemcKey( 'valid-tags-hook' ) ),
+ 'lockTSE' => 300,
+ 'pcTTL' => 30
+ )
);
}
/**
* Returns a map of any tags used on the wiki to number of edits
* tagged with them, ordered descending by the hitcount.
+ * This does not include tags defined somewhere that have never been applied.
*
* Keeps a short-term cache in memory, so calling this multiple times in the
* same request should be fine.
*/
public static function tagUsageStatistics() {
$fname = __METHOD__;
-
return ObjectCache::getMainWANInstance()->getWithSetCallback(
wfMemcKey( 'change-tag-statistics' ),
- function() use ( $fname ) {
- $out = array();
-
+ 300,
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
$dbr = wfGetDB( DB_SLAVE, 'vslow' );
+
+ $setOpts += Database::getCacheSetOptions( $dbr );
+
$res = $dbr->select(
'change_tag',
array( 'ct_tag', 'hitcount' => 'count(*)' ),
array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' )
);
+ $out = array();
foreach ( $res as $row ) {
$out[$row->ct_tag] = $row->hitcount;
}
- foreach ( ChangeTags::listDefinedTags() as $tag ) {
- if ( !isset( $out[$tag] ) ) {
- $out[$tag] = 0;
- }
- }
-
return $out;
},
- 300,
- array( wfMemcKey( 'change-tag-statistics' ) ),
- array( 'lockTSE' => INF )
+ array(
+ 'checkKeys' => array( wfMemcKey( 'change-tag-statistics' ) ),
+ 'lockTSE' => 300,
+ 'pcTTL' => 30
+ )
);
}