r46460 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r46459‎ | r46460 | r46461 >
Date:19:08, 28 January 2009
Author:werdna
Status:deferred (Comments)
Tags:
Comment:
Branch merge of change-tagging branch with trunk
-- Introduce tagging of individual changes (revisions, logs, and on recentchanges). The tags are customisable, and currently settable by the Abuse Filter and by the TorBlock extension. The tags can be styled on the various pages on which they appear.
-- Introduces a schema change, three new tables (valid_tag, change_tag, and tag_summary).
Modified paths:
  • /trunk/extensions/AbuseFilter/AbuseFilter.class.php (modified) (history)
  • /trunk/extensions/AbuseFilter/AbuseFilter.hooks.php (modified) (history)
  • /trunk/extensions/AbuseFilter/AbuseFilter.php (modified) (history)
  • /trunk/extensions/AbuseFilter/Views/AbuseFilterViewEdit.php (modified) (history)
  • /trunk/extensions/TorBlock/TorBlock.class.php (modified) (history)
  • /trunk/extensions/TorBlock/TorBlock.i18n.php (modified) (history)
  • /trunk/extensions/TorBlock/TorBlock.php (modified) (history)
  • /trunk/phase3/docs/hooks.txt (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/ChangeTags.php (added) (history)
  • /trunk/phase3/includes/ChangesList.php (modified) (history)
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/LogEventsList.php (modified) (history)
  • /trunk/phase3/includes/PageHistory.php (modified) (history)
  • /trunk/phase3/includes/SpecialPage.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialContributions.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialLog.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialNewpages.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialRecentchanges.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialRecentchangeslinked.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialTags.php (added) (history)
  • /trunk/phase3/includes/specials/SpecialWatchlist.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/maintenance/archives/patch-change_tag.sql (added) (history)
  • /trunk/phase3/maintenance/tables.sql (modified) (history)
  • /trunk/phase3/maintenance/updaters.inc (modified) (history)
  • /trunk/phase3/skins/common/history.js (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/archives/patch-change_tag.sql
@@ -0,0 +1,31 @@
 2+-- A table to track tags for revisions, logs and recent changes.
 3+-- Andrew Garrett, 2009-01
 4+CREATE TABLE /*_*/change_tag (
 5+ ct_rc_id int NULL,
 6+ ct_log_id int NULL,
 7+ ct_rev_id int NULL,
 8+ ct_tag varchar(255) NOT NULL,
 9+ ct_params BLOB NULL,
 10+
 11+ UNIQUE KEY (ct_rc_id,ct_tag),
 12+ UNIQUE KEY (ct_log_id,ct_tag),
 13+ UNIQUE KEY (ct_rev_id,ct_tag),
 14+ KEY (ct_tag,ct_rc_id,ct_rev_id,ct_log_id) -- Covering index, so we can pull all the info only out of the index.
 15+) /*$wgDBTableOptions*/;
 16+
 17+-- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT that only works on MySQL 4.1+
 18+CREATE TABLE /*_*/tag_summary (
 19+ ts_rc_id int NULL,
 20+ ts_log_id int NULL,
 21+ ts_rev_id int NULL,
 22+ ts_tags BLOB NOT NULL,
 23+
 24+ UNIQUE KEY (ts_rc_id),
 25+ UNIQUE KEY (ts_log_id),
 26+ UNIQUE KEY (ts_rev_id)
 27+) /*$wgDBTableOptions*/;
 28+
 29+CREATE TABLE /*_*/valid_tag (
 30+ vt_tag varchar(255) NOT NULL,
 31+ PRIMARY KEY (vt_tag)
 32+) /*$wgDBTableOptions*/;
\ No newline at end of file
Index: trunk/phase3/maintenance/updaters.inc
@@ -158,6 +158,9 @@
159159 array( 'do_active_users_init' ),
160160 array( 'add_field', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ),
161161 array( 'sqlite_initial_indexes' ),
 162+ array( 'add_table', 'change_tag', 'patch-change_tag.sql' ),
 163+ array( 'add_table', 'tag_summary', 'patch-change_tag.sql' ),
 164+ array( 'add_table', 'valid_tag', 'patch-change_tag.sql' ),
162165 ),
163166 );
164167
Index: trunk/phase3/maintenance/tables.sql
@@ -1234,4 +1234,36 @@
12351235 ul_key varchar(255) NOT NULL PRIMARY KEY
12361236 ) /*$wgDBTableOptions*/;
12371237
 1238+--- A table to track tags for revisions, logs and recent changes.
 1239+REATE TABLE /*_*/change_tag (
 1240+ ct_rc_id int NULL,
 1241+ ct_log_id int NULL,
 1242+ ct_rev_id int NULL,
 1243+ ct_tag varchar(255) NOT NULL,
 1244+ ct_params BLOB NULL,
 1245+
 1246+ UNIQUE KEY (ct_rc_id,ct_tag),
 1247+ UNIQUE KEY (ct_log_id,ct_tag),
 1248+ UNIQUE KEY (ct_rev_id,ct_tag),
 1249+ KEY (ct_tag,ct_rc_id,ct_rev_id,ct_log_id) -- Covering index, so we can pull all the info only out of the index.
 1250+) /*$wgDBTableOptions*/;
 1251+
 1252+-- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT that only works on MySQL 4.1+
 1253+CREATE TABLE /*_*/tag_summary (
 1254+ ts_rc_id int NULL,
 1255+ ts_log_id int NULL,
 1256+ ts_rev_id int NULL,
 1257+ ts_tags BLOB NOT NULL,
 1258+
 1259+ UNIQUE KEY (ts_rc_id),
 1260+ UNIQUE KEY (ts_log_id),
 1261+ UNIQUE KEY (ts_rev_id),
 1262+) /*$wgDBTableOptions*/;
 1263+
 1264+CREATE TABLE /*_*/valid_tag (
 1265+ vt_tag varchar(255) NOT NULL,
 1266+ PRIMARY KEY (vt_tag)
 1267+) /*$wgDBTableOptions*/;
 1268+
 1269+
12381270 -- vim: sw=2 sts=2 et
Index: trunk/phase3/skins/common/history.js
@@ -27,7 +27,13 @@
2828 }
2929 if (oli) { // it's the second checked radio
3030 if (inputs[1].checked) {
31 - oli.className = "selected";
 31+ if ( (typeof oli.className) != 'undefined') {
 32+ oli.classNameOriginal = oli.className.replace( 'selected', '' );
 33+ } else {
 34+ oli.classNameOriginal = '';
 35+ }
 36+
 37+ oli.className = "selected "+oli.classNameOriginal;
3238 return false;
3339 }
3440 } else if (inputs[0].checked) {
@@ -42,7 +48,13 @@
4349 if (dli) {
4450 inputs[1].style.visibility = 'hidden';
4551 }
46 - lis[i].className = "selected";
 52+ if ( (typeof lis[i].className) != 'undefined') {
 53+ lis[i].classNameOriginal = lis[i].className.replace( 'selected', '' );
 54+ } else {
 55+ lis[i].classNameOriginal = '';
 56+ }
 57+
 58+ lis[i].className = "selected "+lis[i].classNameOriginal;
4759 oli = lis[i];
4860 } else { // no radio is checked in this row
4961 if (!oli) {
@@ -55,7 +67,7 @@
5668 } else {
5769 inputs[1].style.visibility = 'visible';
5870 }
59 - lis[i].className = "";
 71+ lis[i].className = lis[i].classNameOriginal;
6072 }
6173 }
6274 }
Index: trunk/phase3/docs/hooks.txt
@@ -846,6 +846,9 @@
847847 'LinksUpdateConstructed': At the end of LinksUpdate() is contruction.
848848 &$linksUpdate: the LinkUpdate object
849849
 850+'ListDefinedTags': When trying to find all defined tags.
 851+&$tags: The list of tags.
 852+
850853 'LoadAllMessages': called by MessageCache::loadAllMessages() to load extensions messages
851854
852855 'LoadExtensionSchemaUpdates': called by maintenance/updaters.inc when upgrading database schema
Index: trunk/phase3/includes/ChangeTags.php
@@ -0,0 +1,163 @@
 2+<?php
 3+
 4+if (!defined( 'MEDIAWIKI' ))
 5+ die;
 6+
 7+class ChangeTags {
 8+ static function formatSummaryRow( $tags, $page ) {
 9+ if (!$tags)
 10+ return array('',array());
 11+
 12+ $classes = array();
 13+
 14+ $tags = explode( ',', $tags );
 15+ $displayTags = array();
 16+ foreach( $tags as $tag ) {
 17+ $displayTags[] = self::tagDescription( $tag );
 18+ $classes[] = "mw-tag-$tag";
 19+ }
 20+
 21+ return array( '(' . implode( ', ', $displayTags ) . ')', $classes );
 22+ }
 23+
 24+ static function tagDescription( $tag ) {
 25+ $msg = wfMsgExt( "tag-$tag", 'parseinline' );
 26+ if ( wfEmptyMsg( "tag-$tag", $msg ) ) {
 27+ return htmlspecialchars($tag);
 28+ }
 29+ return $msg;
 30+ }
 31+
 32+ ## Basic utility method to add tags to a particular change, given its rc_id, rev_id and/or log_id.
 33+ static function addTags( $tags, $rc_id=null, $rev_id=null, $log_id=null, $params = null ) {
 34+ if ( !is_array($tags) ) {
 35+ $tags = array( $tags );
 36+ }
 37+
 38+ $tags = array_filter( $tags ); // Make sure we're submitting all tags...
 39+
 40+ if (!$rc_id && !$rev_id && !$log_id) {
 41+ throw new MWException( "At least one of: RCID, revision ID, and log ID MUST be specified when adding a tag to a change!" );
 42+ }
 43+
 44+ $dbr = wfGetDB( DB_SLAVE );
 45+
 46+ // Might as well look for rcids and so on.
 47+ if (!$rc_id) {
 48+ $dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
 49+ if ($log_id) {
 50+ $rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_logid' => $log_id ), __METHOD__ );
 51+ } elseif ($rev_id) {
 52+ $rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_this_oldid' => $rev_id ), __METHOD__ );
 53+ }
 54+ } elseif (!$log_id && !$rev_id) {
 55+ $dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
 56+ $log_id = $dbr->selectField( 'recentchanges', 'rc_logid', array( 'rc_id' => $rc_id ), __METHOD__ );
 57+ $rev_id = $dbr->selectField( 'recentchanges', 'rc_this_oldid', array( 'rc_id' => $rc_id ), __METHOD__ );
 58+ }
 59+
 60+ $tsConds = array_filter( array( 'ts_rc_id' => $rc_id, 'ts_rev_id' => $rev_id, 'ts_log_id' => $log_id ) );
 61+
 62+ ## Update the summary row.
 63+ $prevTags = $dbr->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
 64+ $prevTags = $prevTags ? $prevTags : '';
 65+ $prevTags = array_filter( explode( ',', $prevTags ) );
 66+ $newTags = array_unique( array_merge( $prevTags, $tags ) );
 67+ sort($prevTags);
 68+ sort($newTags);
 69+
 70+ if ( $prevTags == $newTags ) {
 71+ // No change.
 72+ return false;
 73+ }
 74+
 75+ $dbw = wfGetDB( DB_MASTER );
 76+ $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__ );
 77+
 78+ // Insert the tags rows.
 79+ $tagsRows = array();
 80+ foreach( $tags as $tag ) { // Filter so we don't insert NULLs as zero accidentally.
 81+ $tagsRows[] = array_filter( array( 'ct_tag' => $tag, 'ct_rc_id' => $rc_id, 'ct_log_id' => $log_id, 'ct_rev_id' => $rev_id, 'ct_params' => $params ) );
 82+ }
 83+
 84+ $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array('IGNORE') );
 85+
 86+ return true;
 87+ }
 88+
 89+ /**
 90+ * Applies all tags-related changes to a query.
 91+ * Handles selecting tags, and filtering.
 92+ * Needs $tables to be set up properly, so we can figure out which join conditions to use.
 93+ */
 94+ static function modifyDisplayQuery( &$tables, &$fields, &$conds, &$join_conds, $filter_tag = false ) {
 95+ global $wgRequest;
 96+
 97+ if ($filter_tag === false) {
 98+ $filter_tag = $wgRequest->getVal( 'tagfilter' );
 99+ }
 100+
 101+ // Figure out which conditions can be done.
 102+ $join_field = '';
 103+ if ( in_array('recentchanges', $tables) ) {
 104+ $join_cond = 'rc_id';
 105+ } elseif( in_array('logging', $tables) ) {
 106+ $join_cond = 'log_id';
 107+ } elseif ( in_array('revision', $tables) ) {
 108+ $join_cond = 'rev_id';
 109+ } else {
 110+ throw new MWException( "Unable to determine appropriate JOIN condition for tagging." );
 111+ }
 112+
 113+ // JOIN on tag_summary
 114+ $tables[] = 'tag_summary';
 115+ $join_conds['tag_summary'] = array( 'LEFT JOIN', "ts_$join_cond=$join_cond" );
 116+ $fields[] = 'ts_tags';
 117+
 118+ if ($filter_tag) {
 119+ // Somebody wants to filter on a tag.
 120+ // Add an INNER JOIN on change_tag
 121+
 122+ $tables[] = 'change_tag';
 123+ $join_conds['change_tag'] = array( 'INNER JOIN', "ct_$join_cond=$join_cond" );
 124+ $conds['ct_tag'] = $filter_tag;
 125+ }
 126+ }
 127+
 128+ /**
 129+ * If $fullForm is set to false, then it returns an array of (label, form).
 130+ * If $fullForm is true, it returns an entire form.
 131+ */
 132+ static function buildTagFilterSelector( $selected='', $fullForm = false /* used to put a full form around the selector */ ) {
 133+ global $wgTitle;
 134+
 135+ $data = array( wfMsgExt( 'tag-filter', 'parseinline' ), Xml::input( 'tagfilter', 20, $selected ) );
 136+
 137+ if (!$fullForm) {
 138+ return $data;
 139+ }
 140+
 141+ $html = implode( '&nbsp;', $data );
 142+ $html .= "\n" . Xml::element( 'input', array( 'type' => 'submit', 'value' => wfMsg( 'tag-filter-submit' ) ) );
 143+ $html .= "\n" . Xml::hidden( 'title', $wgTitle-> getPrefixedText() );
 144+ $html = Xml::tags( 'form', array( 'action' => $wgTitle->getLocalURL(), 'method' => 'get' ), $html );
 145+
 146+ return $html;
 147+ }
 148+
 149+ /** Basically lists defined tags which count even if they aren't applied to anything */
 150+ static function listDefinedTags() {
 151+ $emptyTags = array();
 152+
 153+ // Some DB stuff
 154+ $dbr = wfGetDB( DB_SLAVE );
 155+ $res = $dbr->select( 'valid_tag', 'vt_tag', array(), __METHOD__ );
 156+ while( $row = $res->fetchObject() ) {
 157+ $emptyTags[] = $row->vt_tag;
 158+ }
 159+
 160+ wfRunHooks( 'ListDefinedTags', array(&$emptyTags) );
 161+
 162+ return array_filter( array_unique( $emptyTags ) );
 163+ }
 164+}
\ No newline at end of file
Index: trunk/phase3/includes/LogEventsList.php
@@ -68,7 +68,7 @@
6969 * @param $filter Boolean
7070 */
7171 public function showOptions( $type = '', $user = '', $page = '', $pattern = '', $year = '',
72 - $month = '', $filter = null )
 72+ $month = '', $filter = null, $tagFilter='' )
7373 {
7474 global $wgScript, $wgMiserMode;
7575 $action = htmlspecialchars( $wgScript );
@@ -83,7 +83,8 @@
8484 $this->getTitleInput( $page ) . "\n" .
8585 ( !$wgMiserMode ? ($this->getTitlePattern( $pattern )."\n") : "" ) .
8686 "<p>" . $this->getDateMenu( $year, $month ) . "\n" .
87 - ( $filter ? "</p><p>".$this->getFilterLinks( $type, $filter )."\n" : "" ) .
 87+ Xml::tags( 'p', null, implode( '&nbsp;', ChangeTags::buildTagFilterSelector( $tagFilter ) ) ) . "\n" .
 88+ ( $filter ? "</p><p>".$this->getFilterLinks( $type, $filter )."\n" : "" ) . "\n" .
8889 Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "</p>\n" .
8990 "</fieldset></form>"
9091 );
@@ -230,6 +231,7 @@
231232 global $wgLang, $wgUser, $wgContLang;
232233
233234 $title = Title::makeTitle( $row->log_namespace, $row->log_title );
 235+ $classes = array( "mw-logline-{$row->log_type}" );
234236 $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->log_timestamp), true );
235237 // User links
236238 if( self::isDeleted($row,LogPage::DELETED_USER) ) {
@@ -357,12 +359,16 @@
358360 $this->skin, $paramArray, true );
359361 }
360362
 363+ // Any tags...
 364+ list($tagDisplay, $newClasses) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' );
 365+ $classes = array_merge( $classes, $newClasses );
 366+
361367 if( $revert != '' ) {
362368 $revert = '<span class="mw-logevent-actionlink">' . $revert . '</span>';
363369 }
364370
365 - return Xml::tags( 'li', array( "class" => "mw-logline-$row->log_type" ),
366 - $del . $time . ' ' . $userLink . ' ' . $action . ' ' . $comment . ' ' . $revert ) . "\n";
 371+ return Xml::tags( 'li', array( "class" => implode( ' ', $classes ) ),
 372+ $del . $time . ' ' . $userLink . ' ' . $action . ' ' . $comment . ' ' . $revert . " $tagDisplay" ) . "\n";
367373 }
368374
369375 /**
@@ -508,7 +514,7 @@
509515 * @param $month Integer
510516 */
511517 public function __construct( $list, $type = '', $user = '', $title = '', $pattern = '',
512 - $conds = array(), $year = false, $month = false )
 518+ $conds = array(), $year = false, $month = false, $tagFilter = '' )
513519 {
514520 parent::__construct();
515521 $this->mConds = $conds;
@@ -519,6 +525,7 @@
520526 $this->limitUser( $user );
521527 $this->limitTitle( $title, $pattern );
522528 $this->getDateCond( $year, $month );
 529+ $this->mTagFilter = $tagFilter;
523530 }
524531
525532 public function getDefaultQuery() {
@@ -643,13 +650,18 @@
644651 } else {
645652 $index = array( 'USE INDEX' => array( 'logging' => 'times' ) );
646653 }
647 - return array(
 654+ $info = array(
648655 'tables' => array( 'logging', 'user' ),
649656 'fields' => array( 'log_type', 'log_action', 'log_user', 'log_namespace', 'log_title', 'log_params',
650657 'log_comment', 'log_id', 'log_deleted', 'log_timestamp', 'user_name', 'user_editcount' ),
651658 'conds' => $this->mConds,
652 - 'options' => $index
 659+ 'options' => $index,
 660+ 'join_conds' => array( 'user' => array( 'INNER JOIN', 'user_id=log_user' ) ),
653661 );
 662+
 663+ ChangeTags::modifyDisplayQuery( $info['tables'], $info['fields'], $info['conds'], $info['join_conds'], $this->mTagFilter );
 664+
 665+ return $info;
654666 }
655667
656668 function getIndexField() {
@@ -700,6 +712,10 @@
701713 public function getMonth() {
702714 return $this->mMonth;
703715 }
 716+
 717+ public function getTagFilter() {
 718+ return $this->mTagFilter;
 719+ }
704720 }
705721
706722 /**
@@ -721,6 +737,7 @@
722738 $pattern = $request->getBool( 'pattern' );
723739 $year = $request->getIntOrNull( 'year' );
724740 $month = $request->getIntOrNull( 'month' );
 741+ $tagFilter = $request->getVal( 'tagfilter' );
725742 # Don't let the user get stuck with a certain date
726743 $skip = $request->getText( 'offset' ) || $request->getText( 'dir' ) == 'prev';
727744 if( $skip ) {
@@ -729,7 +746,7 @@
730747 }
731748 # Use new list class to output results
732749 $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, 0 );
733 - $this->pager = new LogPager( $loglist, $type, $user, $title, $pattern, $year, $month );
 750+ $this->pager = new LogPager( $loglist, $type, $user, $title, $pattern, $year, $month, $tagFilter );
734751 }
735752
736753 /**
Index: trunk/phase3/includes/AutoLoader.php
@@ -28,6 +28,7 @@
2929 'CategoryViewer' => 'includes/CategoryPage.php',
3030 'ChangesList' => 'includes/ChangesList.php',
3131 'ChangesFeed' => 'includes/ChangesFeed.php',
 32+ 'ChangeTags' => 'includes/ChangeTags.php',
3233 'ChannelFeed' => 'includes/Feed.php',
3334 'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
3435 'ConstantDependency' => 'includes/CacheDependency.php',
@@ -495,6 +496,7 @@
496497 'SpecialSearch' => 'includes/specials/SpecialSearch.php',
497498 'SpecialSearchOld' => 'includes/specials/SpecialSearch.php',
498499 'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
 500+ 'SpecialTags' => 'includes/specials/SpecialTags.php',
499501 'SpecialVersion' => 'includes/specials/SpecialVersion.php',
500502 'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
501503 'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
Index: trunk/phase3/includes/ChangesList.php
@@ -338,6 +338,12 @@
339339 }
340340 }
341341 }
 342+
 343+ protected function insertTags( &$s, &$rc, &$classes ) {
 344+ list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' );
 345+ $classes = array_merge( $classes, $newClasses );
 346+ $s .= ' ' . $tagSummary;
 347+ }
342348 }
343349
344350
@@ -358,6 +364,7 @@
359365 $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
360366
361367 $s = '';
 368+ $classes = array();
362369 // Moved pages
363370 if( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
364371 $this->insertMove( $s, $rc );
@@ -394,6 +401,8 @@
395402 $this->insertAction( $s, $rc );
396403 # Edit or log comment
397404 $this->insertComment( $s, $rc );
 405+ # Tags
 406+ $this->insertTags( $s, $rc, $classes );
398407 # Rollback
399408 $this->insertRollback( $s, $rc );
400409 # Mark revision as deleted if so
@@ -409,7 +418,7 @@
410419 wfRunHooks( 'OldChangesListRecentChangesLine', array(&$this, &$s, $rc) );
411420
412421 wfProfileOut( __METHOD__ );
413 - return "$dateheader<li>$s</li>\n";
 422+ return "$dateheader<li class=\"".implode( ' ', $classes )."\">$s</li>\n";
414423 }
415424 }
416425
Index: trunk/phase3/includes/DefaultSettings.php
@@ -1455,7 +1455,7 @@
14561456 * to ensure that client-side caches don't keep obsolete copies of global
14571457 * styles.
14581458 */
1459 -$wgStyleVersion = '201';
 1459+$wgStyleVersion = '202';
14601460
14611461
14621462 # Server-side caching:
Index: trunk/phase3/includes/specials/SpecialRecentchangeslinked.php
@@ -15,6 +15,7 @@
1616 $opts = parent::getDefaultOptions();
1717 $opts->add( 'target', '' );
1818 $opts->add( 'showlinkedto', false );
 19+ $opts->add( 'tagfilter', '' );
1920 return $opts;
2021 }
2122
@@ -83,6 +84,8 @@
8485 $join_conds['watchlist'] = array( 'LEFT JOIN', "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace" );
8586 }
8687
 88+ ChangeTags::modifyDisplayQuery( $tables, $select, $conds, $join_conds, $opts['tagfilter'] );
 89+
8790 // XXX: parent class does this, should we too?
8891 // wfRunHooks('SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts ) );
8992
@@ -169,6 +172,7 @@
170173 Xml::input( 'target', 40, str_replace('_',' ',$opts['target']) ) .
171174 Xml::check( 'showlinkedto', $opts['showlinkedto'], array('id' => 'showlinkedto') ) . ' ' .
172175 Xml::label( wfMsg("recentchangeslinked-to"), 'showlinkedto' ) );
 176+ $extraOpts['tagfilter'] = ChangeTags::buildTagFilterSelector( $opts['tagfilter'] );
173177 return $extraOpts;
174178 }
175179
Index: trunk/phase3/includes/specials/SpecialNewpages.php
@@ -32,6 +32,7 @@
3333 $opts->add( 'namespace', '0' );
3434 $opts->add( 'username', '' );
3535 $opts->add( 'feed', '' );
 36+ $opts->add( 'tagfilter', '' );
3637
3738 // Set values
3839 $opts->fetchValuesFromRequest( $wgRequest );
@@ -176,6 +177,8 @@
177178 }
178179 $hidden = implode( "\n", $hidden );
179180
 181+ list( $tagFilterLabel, $tagFilterSelector ) = ChangeTags::buildTagFilterSelector( $this->opts['tagfilter'] );
 182+
180183 $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) .
181184 Xml::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
182185 Xml::fieldset( wfMsg( 'newpages' ) ) .
@@ -188,6 +191,14 @@
189192 Xml::namespaceSelector( $namespace, 'all' ) .
190193 "</td>
191194 </tr>" .
 195+ "<tr>
 196+ <td class='mw-label'>" .
 197+ $tagFilterLabel .
 198+ "</td>
 199+ <td class='mw-input'>" .
 200+ $tagFilterSelector .
 201+ "</td>
 202+ </tr>" .
192203 ($wgEnableNewpagesUserFilter ?
193204 "<tr>
194205 <td class='mw-label'>" .
@@ -235,6 +246,9 @@
236247 */
237248 public function formatRow( $result ) {
238249 global $wgLang, $wgContLang, $wgUser;
 250+
 251+ $classes = array();
 252+
239253 $dm = $wgContLang->getDirMark();
240254
241255 $title = Title::makeTitleSafe( $result->rc_namespace, $result->rc_title );
@@ -247,9 +261,17 @@
248262 $ulink = $this->skin->userLink( $result->rc_user, $result->rc_user_text ) . ' ' .
249263 $this->skin->userToolLinks( $result->rc_user, $result->rc_user_text );
250264 $comment = $this->skin->commentBlock( $result->rc_comment );
251 - $css = $this->patrollable( $result ) ? " class='not-patrolled'" : '';
 265+
 266+ if ( $this->patrollable( $result ) )
 267+ $classes[] = 'not-patrolled';
252268
253 - return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment}</li>\n";
 269+ # Tags, if any.
 270+ list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $result->ts_tags, 'newpages' );
 271+ $classes = array_merge( $classes, $newClasses );
 272+
 273+ $css = count($classes) ? ' class="'.implode( " ", $classes).'"' : '';
 274+
 275+ return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment} {$tagDisplay}</li>\n";
254276 }
255277
256278 /**
@@ -378,7 +400,6 @@
379401 } else {
380402 $rcIndexes = array( 'rc_timestamp' );
381403 }
382 - $conds[] = 'page_id = rc_cur_id';
383404
384405 # $wgEnableNewpagesUserFilter - temp WMF hack
385406 if( $wgEnableNewpagesUserFilter && $user ) {
@@ -400,13 +421,24 @@
401422 $conds['page_is_redirect'] = 0;
402423 }
403424
404 - return array(
 425+ $info = array(
405426 'tables' => array( 'recentchanges', 'page' ),
406427 'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
407 - rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id',
 428+ rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id, ts_tags',
408429 'conds' => $conds,
409 - 'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) )
 430+ 'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) ),
 431+ 'join_conds' => array(
 432+ 'page' => array('INNER JOIN', 'page_id=rc_cur_id'),
 433+ ),
410434 );
 435+
 436+ ## Empty array for fields, it'll be set by us anyway.
 437+ $fields = array();
 438+
 439+ ## Modify query for tags
 440+ ChangeTags::modifyDisplayQuery( $info['tables'], $fields, $info['conds'], $info['join_conds'], $this->opts['tagfilter'] );
 441+
 442+ return $info;
411443 }
412444
413445 function getIndexField() {
Index: trunk/phase3/includes/specials/SpecialLog.php
@@ -45,6 +45,7 @@
4646 $pattern = $wgRequest->getBool( 'pattern' );
4747 $y = $wgRequest->getIntOrNull( 'year' );
4848 $m = $wgRequest->getIntOrNull( 'month' );
 49+ $tagFilter = $wgRequest->getVal( 'tagfilter' );
4950 # Don't let the user get stuck with a certain date
5051 $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev';
5152 if( $skip ) {
@@ -53,12 +54,12 @@
5455 }
5556 # Create a LogPager item to get the results and a LogEventsList item to format them...
5657 $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, 0 );
57 - $pager = new LogPager( $loglist, $type, $user, $title, $pattern, array(), $y, $m );
 58+ $pager = new LogPager( $loglist, $type, $user, $title, $pattern, array(), $y, $m, $tagFilter );
5859 # Set title and add header
5960 $loglist->showHeader( $pager->getType() );
6061 # Show form options
6162 $loglist->showOptions( $pager->getType(), $pager->getUser(), $pager->getPage(), $pager->getPattern(),
62 - $pager->getYear(), $pager->getMonth(), $pager->getFilterParams() );
 63+ $pager->getYear(), $pager->getMonth(), $pager->getFilterParams(), $tagFilter );
6364 # Insert list
6465 $logBody = $pager->getBody();
6566 if( $logBody ) {
Index: trunk/phase3/includes/specials/SpecialRecentchanges.php
@@ -35,6 +35,7 @@
3636
3737 $opts->add( 'categories', '' );
3838 $opts->add( 'categories_any', false );
 39+ $opts->add( 'tagfilter', '' );
3940 return $opts;
4041 }
4142
@@ -275,13 +276,19 @@
276277 $namespace = $opts['namespace'];
277278 $invert = $opts['invert'];
278279
 280+ $join_conds = array();
 281+
279282 // JOIN on watchlist for users
280283 if( $uid ) {
281284 $tables[] = 'watchlist';
282 - $join_conds = array( 'watchlist' => array('LEFT JOIN',
283 - "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace") );
 285+ $join_conds['watchlist'] = array('LEFT JOIN',
 286+ "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace");
284287 }
285288
 289+ // Tag stuff.
 290+ $fields = array(); // Fields are * in this case, so let the function modify an empty array to keep it happy.
 291+ ChangeTags::modifyDisplayQuery( &$tables, $fields, &$conds, &$join_conds, $opts['tagfilter'] );
 292+
286293 wfRunHooks('SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts ) );
287294
288295 // Is there either one namespace selected or excluded?
@@ -454,6 +461,8 @@
455462 $extraOpts['category'] = $this->categoryFilterForm( $opts );
456463 }
457464
 465+ $extraOpts['tagfilter'] = ChangeTags::buildTagFilterSelector( $opts['tagfilter'] );
 466+
458467 wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) );
459468 return $extraOpts;
460469 }
Index: trunk/phase3/includes/specials/SpecialContributions.php
@@ -63,6 +63,8 @@
6464 } else {
6565 $this->opts['namespace'] = '';
6666 }
 67+
 68+ $this->opts['tagfilter'] = $wgRequest->getVal( 'tagfilter' );
6769
6870 // Allows reverts to have the bot flag in recent changes. It is just here to
6971 // be passed in the form at the top of the page
@@ -256,6 +258,7 @@
257259 Xml::label( wfMsg( 'namespace' ), 'namespace' ) . ' ' .
258260 Xml::namespaceSelector( $this->opts['namespace'], '' ) .
259261 '</span>' .
 262+ Xml::tags( 'p', null, implode( '&nbsp;', ChangeTags::buildTagFilterSelector( $this->opts['tagfilter'] ) ) ) .
260263 Xml::openElement( 'p' ) .
261264 '<span style="white-space: nowrap">' .
262265 Xml::label( wfMsg( 'year' ), 'year' ) . ' '.
@@ -307,7 +310,7 @@
308311 $target = $this->opts['target'] == 'newbies' ? 'newbies' : $nt->getText();
309312
310313 $pager = new ContribsPager( $target, $this->opts['namespace'],
311 - $this->opts['year'], $this->opts['month'] );
 314+ $this->opts['year'], $this->opts['month'], $this->opts['tagfilter'] );
312315
313316 $pager->mLimit = min( $this->opts['limit'], $wgFeedLimit );
314317
@@ -371,13 +374,14 @@
372375 var $messages, $target;
373376 var $namespace = '', $mDb;
374377
375 - function __construct( $target, $namespace = false, $year = false, $month = false ) {
 378+ function __construct( $target, $namespace = false, $year = false, $month = false, $tagFilter = false ) {
376379 parent::__construct();
377380 foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist newpageletter minoreditletter' ) as $msg ) {
378381 $this->messages[$msg] = wfMsgExt( $msg, array( 'escape') );
379382 }
380383 $this->target = $target;
381384 $this->namespace = $namespace;
 385+ $this->tagFilter = $tagFilter;
382386
383387 $this->getDateCond( $year, $month );
384388
@@ -392,7 +396,10 @@
393397
394398 function getQueryInfo() {
395399 list( $tables, $index, $userCond, $join_cond ) = $this->getUserCond();
396 - $conds = array_merge( array('page_id=rev_page'), $userCond, $this->getNamespaceCond() );
 400+
 401+ $conds = array_merge( $userCond, $this->getNamespaceCond() );
 402+ $join_cond['page'] = array( 'INNER JOIN', 'page_id=rev_page' );
 403+
397404 $queryInfo = array(
398405 'tables' => $tables,
399406 'fields' => array(
@@ -404,6 +411,9 @@
405412 'options' => array( 'USE INDEX' => array('revision' => $index) ),
406413 'join_conds' => $join_cond
407414 );
 415+
 416+ ChangeTags::modifyDisplayQuery( $queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $this->tagFilter );
 417+
408418 wfRunHooks( 'ContribsPager::getQueryInfo', array( &$this, &$queryInfo ) );
409419 return $queryInfo;
410420 }
@@ -463,6 +473,7 @@
464474
465475 $sk = $this->getSkin();
466476 $rev = new Revision( $row );
 477+ $classes = array();
467478
468479 $page = Title::newFromRow( $row );
469480 $page->resetArticleId( $row->rev_page ); // use process cache
@@ -521,10 +532,17 @@
522533 if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
523534 $ret .= ' ' . wfMsgHtml( 'deletedrev' );
524535 }
 536+
 537+ # Tags, if any.
 538+ list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow( $row->ts_tags, 'contributions' );
 539+ $classes = array_merge( $classes, $newClasses );
 540+ $ret .= " $tagSummary";
 541+
525542 // Let extensions add data
526543 wfRunHooks( 'ContributionsLineEnding', array( &$this, &$ret, $row ) );
527 -
528 - $ret = "<li>$ret</li>\n";
 544+
 545+ $classes = implode( ' ', $classes );
 546+ $ret = "<li class=\"$classes\">$ret</li>\n";
529547 wfProfileOut( __METHOD__ );
530548 return $ret;
531549 }
Index: trunk/phase3/includes/specials/SpecialTags.php
@@ -0,0 +1,75 @@
 2+<?php
 3+
 4+if (!defined('MEDIAWIKI'))
 5+ die;
 6+
 7+class SpecialTags extends SpecialPage {
 8+
 9+ function __construct() {
 10+ parent::__construct( 'Tags' );
 11+ }
 12+
 13+ function execute() {
 14+ global $wgOut, $wgUser, $wgMessageCache;
 15+
 16+ $wgMessageCache->loadAllMessages();
 17+
 18+ $sk = $wgUser->getSkin();
 19+ $wgOut->setPageTitle( wfMsg( 'tags-title' ) );
 20+ $wgOut->addWikiMsg( 'tags-intro' );
 21+
 22+ // Write the headers
 23+ $html = '';
 24+ $html = Xml::tags( 'tr', null, Xml::tags( 'th', null, wfMsgExt( 'tags-tag', 'parseinline' ) ) .
 25+ Xml::tags( 'th', null, wfMsgExt( 'tags-display-header', 'parseinline' ) ) .
 26+ Xml::tags( 'th', null, wfMsgExt( 'tags-description-header', 'parseinline' ) ) .
 27+ Xml::tags( 'th', null, wfMsgExt( 'tags-hitcount-header', 'parseinline' ) )
 28+ );
 29+ $dbr = wfGetDB( DB_SLAVE );
 30+ $res = $dbr->select( 'change_tag', array( 'ct_tag', 'count(*) as hitcount' ), array(), __METHOD__, array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' ) );
 31+
 32+ while ( $row = $res->fetchObject() ) {
 33+ $html .= $this->doTagRow( $row->ct_tag, $row->hitcount );
 34+ }
 35+
 36+ foreach( ChangeTags::listDefinedTags() as $tag ) {
 37+ $html .= $this->doTagRow( $tag, 0 );
 38+ }
 39+
 40+ $html = "<table style='width: 80%'><tbody>$html</tbody></table>";
 41+
 42+ $wgOut->addHTML( $html );
 43+ }
 44+
 45+ function doTagRow( $tag, $hitcount ) {
 46+ static $sk=null, $doneTags=array();
 47+ if (!$sk) {
 48+ global $wgUser;
 49+ $sk = $wgUser->getSkin();
 50+ }
 51+
 52+ if ( in_array( $tag, $doneTags ) ) {
 53+ return '';
 54+ }
 55+
 56+ $newRow = '';
 57+ $newRow .= Xml::tags( 'td', null, Xml::element( 'tt', null, $tag ) );
 58+
 59+ $disp = ChangeTags::tagDescription( $tag );
 60+ $disp .= ' (' . $sk->link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag" ), wfMsg( 'tag-edit' ) ) . ')';
 61+ $newRow .= Xml::tags( 'td', null, $disp );
 62+
 63+ $desc = wfMsgExt( "tag-$tag-description", 'parseinline' );
 64+ $desc = wfEmptyMsg( "tag-$tag-description", $desc ) ? '' : $desc;
 65+ $desc .= ' (' . $sk->link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag-description" ), wfMsg( 'tag-edit' ) ) . ')';
 66+ $newRow .= Xml::tags( 'td', null, $desc );
 67+
 68+ $hitcount = wfMsg( 'tags-hitcount', $hitcount );
 69+ $hitcount = $sk->link( SpecialPage::getTitleFor( 'RecentChanges' ), $hitcount, array(), array( 'tagfilter' => $tag ) );
 70+ $newRow .= Xml::tags( 'td', null, $hitcount );
 71+
 72+ $doneTags[] = $tag;
 73+
 74+ return Xml::tags( 'tr', null, $newRow ) . "\n";
 75+ }
 76+}
\ No newline at end of file
Index: trunk/phase3/includes/specials/SpecialWatchlist.php
@@ -218,7 +218,8 @@
219219 $tables[] = 'page';
220220 $join_conds['page'] = array('LEFT JOIN','rc_cur_id=page_id');
221221 }
222 -
 222+
 223+ ChangeTags::modifyDisplayQuery( $tables, $fields, $conds, $join_conds, '' );
223224 wfRunHooks('SpecialWatchlistQuery', array(&$conds,&$tables,&$join_conds,&$fields) );
224225
225226 $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options, $join_conds );
Index: trunk/phase3/includes/PageHistory.php
@@ -112,6 +112,7 @@
113113 */
114114 $year = $wgRequest->getInt( 'year' );
115115 $month = $wgRequest->getInt( 'month' );
 116+ $tagFilter = $wgRequest->getVal( 'tagfilter' );
116117
117118 $action = htmlspecialchars( $wgScript );
118119 $wgOut->addHTML(
@@ -120,6 +121,7 @@
121122 Xml::hidden( 'title', $this->mTitle->getPrefixedDBKey() ) . "\n" .
122123 Xml::hidden( 'action', 'history' ) . "\n" .
123124 $this->getDateMenu( $year, $month ) . '&nbsp;' .
 125+ implode( '&nbsp;', ChangeTags::buildTagFilterSelector( $tagFilter ) ) . '&nbsp;' .
124126 Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
125127 '</fieldset></form>'
126128 );
@@ -129,7 +131,7 @@
130132 /**
131133 * Do the list
132134 */
133 - $pager = new PageHistoryPager( $this, $year, $month );
 135+ $pager = new PageHistoryPager( $this, $year, $month, $tagFilter );
134136 $this->linesonpage = $pager->getNumRows();
135137 $wgOut->addHTML(
136138 $pager->getNavigationBar() .
@@ -287,6 +289,7 @@
288290 $lastlink = $this->lastLink( $rev, $next, $counter );
289291 $arbitrary = $this->diffButtons( $rev, $firstInList, $counter );
290292 $link = $this->revLink( $rev );
 293+ $classes = array();
291294
292295 $s = "($curlink) ($lastlink) $arbitrary";
293296
@@ -355,9 +358,16 @@
356359 $s .= ' (' . implode( ' | ', $tools ) . ')';
357360 }
358361
 362+ # Tags
 363+ list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow( $row->ts_tags, 'history' );
 364+ $classes = array_merge( $classes, $newClasses );
 365+ $s .= " $tagSummary";
 366+
359367 wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row , &$s ) );
360368
361 - return "<li>$s</li>\n";
 369+ $classes = implode( ' ', $classes );
 370+
 371+ return "<li class=\"$classes\">$s</li>\n";
362372 }
363373
364374 /**
@@ -589,20 +599,23 @@
590600 class PageHistoryPager extends ReverseChronologicalPager {
591601 public $mLastRow = false, $mPageHistory, $mTitle;
592602
593 - function __construct( $pageHistory, $year='', $month='' ) {
 603+ function __construct( $pageHistory, $year='', $month='', $tagFilter = '' ) {
594604 parent::__construct();
595605 $this->mPageHistory = $pageHistory;
596606 $this->mTitle =& $this->mPageHistory->mTitle;
 607+ $this->tagFilter = $tagFilter;
597608 $this->getDateCond( $year, $month );
598609 }
599610
600611 function getQueryInfo() {
601612 $queryInfo = array(
602613 'tables' => array('revision'),
603 - 'fields' => Revision::selectFields(),
 614+ 'fields' => array_merge( Revision::selectFields(), array('ts_tags') ),
604615 'conds' => array('rev_page' => $this->mPageHistory->mTitle->getArticleID() ),
605 - 'options' => array( 'USE INDEX' => array('revision' => 'page_timestamp') )
 616+ 'options' => array( 'USE INDEX' => array('revision' => 'page_timestamp') ),
 617+ 'join_conds' => array( 'tag_summary' => array( 'LEFT JOIN', 'ts_rev_id=rev_id' ) ),
606618 );
 619+ ChangeTags::modifyDisplayQuery( $queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $this->tagFilter );
607620 wfRunHooks( 'PageHistoryPager::getQueryInfo', array( &$this, &$queryInfo ) );
608621 return $queryInfo;
609622 }
Index: trunk/phase3/includes/SpecialPage.php
@@ -159,6 +159,7 @@
160160 'Randomredirect' => 'SpecialRandomredirect',
161161 'Withoutinterwiki' => array( 'SpecialPage', 'Withoutinterwiki' ),
162162 'Filepath' => array( 'SpecialPage', 'Filepath' ),
 163+ 'Tags' => 'SpecialTags',
163164
164165 'Mypage' => array( 'SpecialMypage' ),
165166 'Mytalk' => array( 'SpecialMytalk' ),
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -3801,4 +3801,15 @@
38023802
38033803 #Put all regex fragments above this line. Leave this line exactly as it is</pre>',
38043804
 3805+## Taggng-related stuff
 3806+'tag-filter' => '[[Special:Tags|Tag]] filter:',
 3807+'tag-filter-submit' => 'Filter',
 3808+'tags-title' => 'Tags',
 3809+'tags-intro' => 'This page lists the tags that the software may mark an edit with, and their meaning.',
 3810+'tags-tag' => 'Internal tag name',
 3811+'tags-display-header' => 'Appearance on change lists',
 3812+'tags-description-header' => 'Full description of meaning',
 3813+'tags-hitcount-header' => 'Tagged edits',
 3814+'tags-edit' => 'edit',
 3815+'tags-hitcount' => '$1 changes',
38053816 );
Index: trunk/extensions/TorBlock/TorBlock.i18n.php
@@ -15,6 +15,8 @@
1616 'torblock-blocked' => 'Your IP address, <tt>$1</tt>, has been automatically identified as a tor exit node.
1717 Editing through tor is blocked to prevent abuse.',
1818 'right-torunblocked' => 'Bypass automatic blocks of tor exit nodes',
 19+ 'tag-tor-description' => 'If this tag is set, an edit was made from a Tor exit node.',
 20+ 'tag-tor' => 'Made through tor',
1921 );
2022
2123 /** Message documentation (Message documentation)
Index: trunk/extensions/TorBlock/TorBlock.php
@@ -33,6 +33,8 @@
3434 $wgHooks['GetAutoPromoteGroups'][] = 'TorBlock::onGetAutoPromoteGroups';
3535 $wgHooks['GetBlockedStatus'][] = 'TorBlock::onGetBlockedStatus';
3636 $wgHooks['AutopromoteCondition'][] = 'TorBlock::onAutopromoteCondition';
 37+$wgHooks['RecentChange_save'][] = 'TorBlock::onRecentChangeSave';
 38+$wgHooks['ListDefinedTags'][] = 'TorBlock::onListDefinedTags';
3739
3840 // Define new autopromote condition
3941 define('APCOND_TOR', 'tor'); // Numbers won't work, we'll get collisions
Index: trunk/extensions/TorBlock/TorBlock.class.php
@@ -168,4 +168,16 @@
169169
170170 return true;
171171 }
 172+
 173+ public static function onRecentChangeSave( $recentChange ) {
 174+ if ( class_exists('ChangeTags') && self::isExitNode() ) {
 175+ ChangeTags::addTags( 'tor', $recentChange->mAttribs['rc_id'], $recentChange->mAttribs['rc_this_oldid'], $recentChange->mAttribs['rc_logid'] );
 176+ }
 177+ return true;
 178+ }
 179+
 180+ public static function onListDefinedTags( &$emptyTags ) {
 181+ $emptyTags[] = 'tor';
 182+ return true;
 183+ }
172184 }
Index: trunk/extensions/AbuseFilter/AbuseFilter.php
@@ -52,8 +52,8 @@
5353 $wgHooks['ArticleDelete'][] = 'AbuseFilterHooks::onArticleDelete';
5454 $wgHooks['LoadExtensionSchemaUpdates'][] = 'AbuseFilterHooks::onSchemaUpdate';
5555 $wgHooks['AbortDeleteQueueNominate'][] = 'AbuseFilterHooks::onAbortDeleteQueueNominate';
56 -// $wgHooks['RecentChange_save'][] = 'AbuseFilterHooks::onRecentChangeSave';
57 -// $wgHooks['ListDefinedTags'][] = 'AbuseFilterHooks::onListDefinedTags';
 56+$wgHooks['RecentChange_save'][] = 'AbuseFilterHooks::onRecentChangeSave';
 57+$wgHooks['ListDefinedTags'][] = 'AbuseFilterHooks::onListDefinedTags';
5858
5959 $wgAvailableRights[] = 'abusefilter-modify';
6060 $wgAvailableRights[] = 'abusefilter-log-detail';
@@ -63,7 +63,7 @@
6464 $wgAvailableRights[] = 'abusefilter-modify-restricted';
6565 $wgAvailableRights[] = 'abusefilter-revert';
6666
67 -$wgAbuseFilterAvailableActions = array( 'flag', 'throttle', 'warn', 'disallow', 'blockautopromote', 'block', 'degroup', /* Disabled because it's ridiculously excessive 'rangeblock'*/ /*, 'tag' Disabled for now to avoid trunk changes. */ );
 67+$wgAbuseFilterAvailableActions = array( 'flag', 'throttle', 'warn', 'disallow', 'blockautopromote', 'block', 'degroup', 'tag' );
6868
6969 $wgAbuseFilterConditionLimit = 1000;
7070
Index: trunk/extensions/AbuseFilter/Views/AbuseFilterViewEdit.php
@@ -328,21 +328,20 @@
329329 $warnFields['abusefilter-edit-warn-message'] = Xml::input( 'wpFilterWarnMessage', 45, $warnMsg );
330330 $output .= Xml::tags( 'p', null, Xml::buildForm( $warnFields ) );
331331 return $output;
332 - // Commented out to avoid trunk changes for now.
333 -// case 'tag':
334 -// if ($set) {
335 -// $tags = $parameters;
336 -// } else {
337 -// $tags = array();
338 -// }
339 -// $output = '';
340 -//
341 -// $checkbox = Xml::checkLabel( wfMsg('abusefilter-edit-action-tag'), 'wpFilterActionTag', 'wpFilterActionTag', $set );
342 -// $output .= Xml::tags( 'p', null, $checkbox );
343 -//
344 -// $tagFields['abusefilter-edit-tag-tag'] = Xml::textarea( 'wpFilterTags', implode( "\n", $tags ) );
345 -// $output .= Xml::tags( 'p', null, Xml::buildForm( $tagFields ) );
346 -// return $output;
 332+ case 'tag':
 333+ if ($set) {
 334+ $tags = $parameters;
 335+ } else {
 336+ $tags = array();
 337+ }
 338+ $output = '';
 339+
 340+ $checkbox = Xml::checkLabel( wfMsg('abusefilter-edit-action-tag'), 'wpFilterActionTag', 'wpFilterActionTag', $set );
 341+ $output .= Xml::tags( 'p', null, $checkbox );
 342+
 343+ $tagFields['abusefilter-edit-tag-tag'] = Xml::textarea( 'wpFilterTags', implode( "\n", $tags ) );
 344+ $output .= Xml::tags( 'p', null, Xml::buildForm( $tagFields ) );
 345+ return $output;
347346 default:
348347 $message = 'abusefilter-edit-action-'.$action;
349348 $form_field = 'wpFilterAction' . ucfirst($action);
Index: trunk/extensions/AbuseFilter/AbuseFilter.class.php
@@ -439,16 +439,18 @@
440440 // Do nothing. Here for completeness.
441441 break;
442442
443 -// case 'tag':
444 -// // Mark with a tag on recentchanges.
445 -// global $wgUser;
446 -//
447 -// $actionID = implode( '-', array(
448 -// $title->getPrefixedText(), $wgUser->getName(), $vars['ACTION']
449 -// ) );
450 -//
451 -// AbuseFilter::$tagsToSet[$actionID] = $parameters;
452 -// break;
 443+ case 'tag':
 444+ // Mark with a tag on recentchanges.
 445+ global $wgUser;
 446+
 447+ $actionID = implode( '-', array(
 448+ $title->getPrefixedText(), $wgUser->getName(), $vars['ACTION']
 449+ ) );
 450+
 451+ AbuseFilter::$tagsToSet[$actionID] = $parameters;
 452+ break;
 453+ default:
 454+ throw new MWException( "Unrecognised action $action" );
453455 }
454456
455457 return $display;
Index: trunk/extensions/AbuseFilter/AbuseFilter.hooks.php
@@ -172,31 +172,30 @@
173173 return $filter_result == '' || $filter_result === true;
174174 }
175175
176 -// Commented out to avoid trunk changes for now.
177 -// public static function onRecentChangeSave( $recentChange ) {
178 -// $title = Title::makeTitle( $recentChange->mAttribs['rc_namespace'], $recentChange->mAttribs['rc_title'] );
179 -// $action = $recentChange->mAttribs['rc_log_type'] ? $recentChange->mAttribs['rc_log_type'] : 'edit';
180 -// $actionID = implode( '-', array(
181 -// $title->getPrefixedText(), $recentChange->mAttribs['rc_user_text'], $action
182 -// ) );
183 -//
184 -// if ( !empty( AbuseFilter::$tagsToSet[$actionID] ) && count( $tags = AbuseFilter::$tagsToSet[$actionID]) ) {
185 -// ChangeTags::addTags( $tags, $recentChange->mAttribs['rc_id'], $recentChange->mAttribs['rc_this_oldid'], $recentChange->mAttribs['rc_logid'] );
186 -// }
187 -//
188 -// return true;
189 -// }
 176+ public static function onRecentChangeSave( $recentChange ) {
 177+ $title = Title::makeTitle( $recentChange->mAttribs['rc_namespace'], $recentChange->mAttribs['rc_title'] );
 178+ $action = $recentChange->mAttribs['rc_log_type'] ? $recentChange->mAttribs['rc_log_type'] : 'edit';
 179+ $actionID = implode( '-', array(
 180+ $title->getPrefixedText(), $recentChange->mAttribs['rc_user_text'], $action
 181+ ) );
190182
191 -// public static function onListDefinedTags( &$emptyTags ) {
192 -// ## This is a pretty awful hack.
193 -// $dbr = wfGetDB( DB_SLAVE );
194 -//
195 -// $res = $dbr->select( 'abuse_filter_action', 'afa_parameters', array( 'afa_consequence' => 'tag' ), __METHOD__ );
196 -//
197 -// while( $row = $res->fetchObject() ) {
198 -// $emptyTags = array_filter( array_merge( explode( "\n", $row->afa_parameters ), $emptyTags ) );
199 -// }
200 -//
201 -// return true;
202 -// }
 183+ if ( !empty( AbuseFilter::$tagsToSet[$actionID] ) && count( $tags = AbuseFilter::$tagsToSet[$actionID]) ) {
 184+ ChangeTags::addTags( $tags, $recentChange->mAttribs['rc_id'], $recentChange->mAttribs['rc_this_oldid'], $recentChange->mAttribs['rc_logid'] );
 185+ }
 186+
 187+ return true;
 188+ }
 189+
 190+ public static function onListDefinedTags( &$emptyTags ) {
 191+ ## This is a pretty awful hack.
 192+ $dbr = wfGetDB( DB_SLAVE );
 193+
 194+ $res = $dbr->select( 'abuse_filter_action', 'afa_parameters', array( 'afa_consequence' => 'tag' ), __METHOD__ );
 195+
 196+ while( $row = $res->fetchObject() ) {
 197+ $emptyTags = array_filter( array_merge( explode( "\n", $row->afa_parameters ), $emptyTags ) );
 198+ }
 199+
 200+ return true;
 201+ }
203202 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r46462Follow up r46460: Add new messages to messages.inc...raymond19:19, 28 January 2009
r46875Update Postgres schema per r46460ialex20:55, 5 February 2009
r46941Fix for r46460: use "CREATE INDEX ... ON ..." rather than inside "CREATE TABL...ialex21:17, 6 February 2009

Comments

#Comment by Siebrand (talk | contribs)   23:11, 28 January 2009
  1. Should the tag filter field maybe be hidden if no labels are defined? See betawiki:Special:Contributions/Lockal and betawiki:Special:Tags. As I have no idea what tags are, and there are no tags defined, this is highly confusing.
  2. there's an issue with the link description for Special:Tags (displays as "&lt;tags&gt;"). See betawiki:Special:SpecialPages at the bottom of the page.
  3. The Special:Tags should get a wgSpecialPageGroups
#Comment by Werdna (talk | contribs)   23:03, 5 February 2009

All fixed on svn. Thanks for the feedback.

#Comment by Werdna (talk | contribs)   22:55, 11 February 2009

This commit is 'resolved' as-is, but it's such a huge change that I'm leaving it as 'new' until somebody has a good look at it and says that those were the only problems.

#Comment by Werdna (talk | contribs)   01:20, 12 February 2009

Fix in r46674.

#Comment by Werdna (talk | contribs)   01:21, 12 February 2009

Fix in r46675.

Status & tagging log