X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fchangetags%2FChangeTags.php;h=12f738fedbba177e3be48be9cc5a03ed1a0cac54;hb=8b2139e1a773ab4be16a58c8cf2edb1c86b4b798;hp=a73011667976296b4e08500e70b578e93dc236e4;hpb=02d026e89330cec63d98631e843a24143abce7ae;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/changetags/ChangeTags.php b/includes/changetags/ChangeTags.php index a730116679..5aac495641 100644 --- a/includes/changetags/ChangeTags.php +++ b/includes/changetags/ChangeTags.php @@ -153,8 +153,10 @@ class ChangeTags { * * @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 ); @@ -169,18 +171,28 @@ class ChangeTags { // 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__ ); } @@ -346,8 +358,12 @@ class ChangeTags { 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 @@ -413,25 +429,31 @@ class ChangeTags { 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 ); @@ -609,17 +631,16 @@ class ChangeTags { * 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; @@ -632,14 +653,24 @@ class ChangeTags { '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; @@ -743,14 +774,12 @@ class ChangeTags { * @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- @@ -761,6 +790,12 @@ class ChangeTags { 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(); } @@ -807,8 +842,12 @@ class ChangeTags { * @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 @@ -862,8 +901,12 @@ class ChangeTags { * @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 @@ -885,7 +928,7 @@ class ChangeTags { // 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 ); } @@ -991,15 +1034,19 @@ class ChangeTags { 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 ); } @@ -1044,6 +1091,7 @@ class ChangeTags { // store the tag usage statistics $tagUsage = self::tagUsageStatistics(); + $hitcount = isset( $tagUsage[$tag] ) ? $tagUsage[$tag] : 0; // do it! $deleteResult = self::deleteTagEverywhere( $tag ); @@ -1052,7 +1100,7 @@ class ChangeTags { } // log it - $logId = self::logTagManagementAction( 'delete', $tag, $reason, $user, $tagUsage[$tag] ); + $logId = self::logTagManagementAction( 'delete', $tag, $reason, $user, $hitcount ); $deleteResult->value = $logId; return $deleteResult; } @@ -1066,15 +1114,20 @@ class ChangeTags { 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 + ) ); } @@ -1106,16 +1159,21 @@ class ChangeTags { 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 + ) ); } @@ -1131,14 +1189,19 @@ class ChangeTags { 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 + ) ); } @@ -1170,6 +1233,7 @@ class ChangeTags { /** * 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. @@ -1178,13 +1242,14 @@ class ChangeTags { */ 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(*)' ), @@ -1193,21 +1258,18 @@ class ChangeTags { 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 + ) ); }