Index: trunk/extensions/AbuseFilter/special/SpecialAbuseLog.php |
— | — | @@ -252,7 +252,7 @@ |
253 | 253 | return; |
254 | 254 | } |
255 | 255 | |
256 | | - if ( $row->afl_deleted && !self::canSeeHidden() ) { |
| 256 | + if ( $this->isHidden($row) && !self::canSeeHidden() ) { |
257 | 257 | $out->addWikiMsg( 'abusefilter-log-details-hidden' ); |
258 | 258 | return; |
259 | 259 | } |
— | — | @@ -375,10 +375,30 @@ |
376 | 376 | |
377 | 377 | $title = Title::makeTitle( $row->afl_namespace, $row->afl_title ); |
378 | 378 | |
| 379 | + $diffLink = false; |
| 380 | + |
| 381 | + if ( self::isHidden($row) && ! $this->canSeeHidden() ) { |
| 382 | + return ''; |
| 383 | + } |
| 384 | + |
379 | 385 | if ( !$row->afl_wiki ) { |
380 | 386 | $pageLink = $sk->link( $title ); |
| 387 | + if ( $row->afl_rev_id ) { |
| 388 | + $diffLink = $sk->link( $title, |
| 389 | + wfMessage('abusefilter-log-diff')->parse(), array(), |
| 390 | + array( 'diff' => 'prev', 'oldid' => $row->afl_rev_id ) ); |
| 391 | + } |
381 | 392 | } else { |
382 | 393 | $pageLink = WikiMap::makeForeignLink( $row->afl_wiki, $row->afl_title ); |
| 394 | + |
| 395 | + if ( $row->afl_rev_id ) { |
| 396 | + $diffUrl = WikiMap::getForeignURL( $row->afl_wiki, $row->afl_title ); |
| 397 | + $diffUrl = wfAppendQuery( $diffUrl, |
| 398 | + array( 'diff' => 'prev', 'oldid' => $row->afl_rev_id ) ); |
| 399 | + |
| 400 | + $diffLink = Linker::makeExternalLink( $diffUrl, |
| 401 | + wfMessage('abusefilter-log-diff')->parse() ); |
| 402 | + } |
383 | 403 | } |
384 | 404 | |
385 | 405 | if ( !$row->afl_wiki ) { |
— | — | @@ -433,6 +453,9 @@ |
434 | 454 | $actionLinks[] = $detailsLink; |
435 | 455 | $actionLinks[] = $examineLink; |
436 | 456 | |
| 457 | + if ($diffLink) |
| 458 | + $actionLinks[] = $diffLink; |
| 459 | + |
437 | 460 | if ( $user->isAllowed( 'abusefilter-hide-log' ) ) { |
438 | 461 | $hideLink = $sk->link( |
439 | 462 | $this->getTitle(), |
— | — | @@ -485,9 +508,12 @@ |
486 | 509 | ); |
487 | 510 | } |
488 | 511 | |
489 | | - if ( $row->afl_deleted ) { |
| 512 | + if ( $this->isHidden($row) === true ) { |
490 | 513 | $description .= ' '. |
491 | 514 | wfMsgExt( 'abusefilter-log-hidden', 'parseinline' ); |
| 515 | + } elseif ( $this->isHidden($row) === 'implicit' ) { |
| 516 | + $description .= ' '. |
| 517 | + wfMsgExt( 'abusefilter-log-hidden-implicit', 'parseinline' ); |
492 | 518 | } |
493 | 519 | |
494 | 520 | return $li ? Xml::tags( 'li', null, $description ) : $description; |
— | — | @@ -507,6 +533,25 @@ |
508 | 534 | |
509 | 535 | return $notDeletedCond; |
510 | 536 | } |
| 537 | + |
| 538 | + /** |
| 539 | + * Given a log entry row, decides whether or not it can be viewed by the public. |
| 540 | + * |
| 541 | + * @param $row The abuse_filter_log row object. |
| 542 | + * |
| 543 | + * @return Mixed true if the item is explicitly hidden, false if it is not. |
| 544 | + * The string 'implicit' if it is hidden because the corresponding revision is hidden. |
| 545 | + */ |
| 546 | + public static function isHidden( $row ) { |
| 547 | + if ( $row->afl_rev_id ) { |
| 548 | + $revision = Revision::newFromId( $row->afl_rev_id ); |
| 549 | + if ( $revision && $revision->getVisibility() != 0 ) { |
| 550 | + return 'implicit'; |
| 551 | + } |
| 552 | + } |
| 553 | + |
| 554 | + return (bool)$row->afl_deleted; |
| 555 | + } |
511 | 556 | } |
512 | 557 | |
513 | 558 | class AbuseLogPager extends ReverseChronologicalPager { |
Index: trunk/extensions/AbuseFilter/AbuseFilter.parser.php |
— | — | @@ -338,6 +338,39 @@ |
339 | 339 | /** Convert shorteners */ |
340 | 340 | |
341 | 341 | /** |
| 342 | + * @return mixed |
| 343 | + */ |
| 344 | + public function toNative() { |
| 345 | + switch( $this->type ) { |
| 346 | + case self::DBool: |
| 347 | + return $this->toBool(); |
| 348 | + break; |
| 349 | + case self::DString: |
| 350 | + return $this->toString(); |
| 351 | + break; |
| 352 | + case self::DFloat: |
| 353 | + return $this->toFloat(); |
| 354 | + break; |
| 355 | + case self::DInt: |
| 356 | + return $this->toInt(); |
| 357 | + break; |
| 358 | + case self::DList: |
| 359 | + $input = $this->toList(); |
| 360 | + $output = array(); |
| 361 | + foreach( $input as $item ) { |
| 362 | + $output[] = $item->toNative(); |
| 363 | + } |
| 364 | + return $output; |
| 365 | + break; |
| 366 | + case self::DNull: |
| 367 | + return null; |
| 368 | + break; |
| 369 | + default: |
| 370 | + throw new MWException( "Unknown type" ); |
| 371 | + } |
| 372 | + } |
| 373 | + |
| 374 | + /** |
342 | 375 | * @return bool |
343 | 376 | */ |
344 | 377 | public function toBool() { |
Index: trunk/extensions/AbuseFilter/AbuseFilter.php |
— | — | @@ -78,6 +78,7 @@ |
79 | 79 | $wgHooks['ContributionsToolLinks'][] = 'AbuseFilterHooks::onContributionsToolLinks'; |
80 | 80 | $wgHooks['UploadVerification'][] = 'AbuseFilterHooks::onUploadVerification'; |
81 | 81 | $wgHooks['MakeGlobalVariablesScript'][] = 'AbuseFilterHooks::onMakeGlobalVariablesScript'; |
| 82 | +$wgHooks['ArticleSaveComplete'][] = 'AbuseFilterHooks::onArticleSaveComplete'; |
82 | 83 | |
83 | 84 | $wgAvailableRights[] = 'abusefilter-modify'; |
84 | 85 | $wgAvailableRights[] = 'abusefilter-log-detail'; |
Index: trunk/extensions/AbuseFilter/db_patches/patch-afl_action_id.sql |
— | — | @@ -0,0 +1,10 @@ |
| 2 | +-- Store the ID of successful actions in the abuse_filter_log table. |
| 3 | +ALTER TABLE /*_*/abuse_filter_log |
| 4 | + ADD COLUMN afl_rev_id int unsigned; |
| 5 | +ALTER TABLE /*_*/abuse_filter_log |
| 6 | + ADD KEY (afl_rev_id); |
| 7 | + |
| 8 | +ALTER TABLE /*_*/abuse_filter_log |
| 9 | + ADD COLUMN afl_log_id int unsigned; |
| 10 | +ALTER TABLE /*_*/abuse_filter_log |
| 11 | + ADD KEY (afl_log_id); |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/db_patches/patch-afl_action_id.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 12 | + native |
Index: trunk/extensions/AbuseFilter/AbuseFilter.class.php |
— | — | @@ -302,8 +302,12 @@ |
303 | 303 | return $parser->evaluateExpression( $expr ); |
304 | 304 | } |
305 | 305 | |
306 | | - public static function checkConditions( $conds, $vars, $ignoreError = true, |
307 | | - $keepVars = 'resetvars' ) { |
| 306 | + public static function checkConditions( |
| 307 | + $conds, |
| 308 | + $vars, |
| 309 | + $ignoreError = true, |
| 310 | + $keepVars = 'resetvars' |
| 311 | + ) { |
308 | 312 | global $wgAbuseFilterParserClass; |
309 | 313 | |
310 | 314 | static $parser; |
— | — | @@ -401,8 +405,12 @@ |
402 | 406 | |
403 | 407 | // Check conditions... |
404 | 408 | $pattern = trim( $row->af_pattern ); |
405 | | - if ( self::checkConditions( $pattern, $vars, true /* ignore errors */, |
406 | | - 'keepvars' ) ) { |
| 409 | + if ( self::checkConditions( |
| 410 | + $pattern, |
| 411 | + $vars, |
| 412 | + true /* ignore errors */, |
| 413 | + 'keepvars' |
| 414 | + ) ) { |
407 | 415 | // Record match. |
408 | 416 | $result = true; |
409 | 417 | } else { |
— | — | @@ -682,8 +690,10 @@ |
683 | 691 | |
684 | 692 | $filter_matched = self::checkAllFilters( $vars ); |
685 | 693 | |
| 694 | + $matched_filters = array_keys( array_filter( $filter_matched ) ); |
| 695 | + |
686 | 696 | // Short-cut any remaining code if no filters were hit. |
687 | | - if ( count( array_filter( $filter_matched ) ) == 0 ) { |
| 697 | + if ( count( $matched_filters ) == 0 ) { |
688 | 698 | wfProfileOut( __METHOD__ ); |
689 | 699 | return true; |
690 | 700 | } |
— | — | @@ -691,7 +701,7 @@ |
692 | 702 | wfProfileIn( __METHOD__ . '-block' ); |
693 | 703 | |
694 | 704 | list( $actions_taken, $error_msg ) = self::executeFilterActions( |
695 | | - array_keys( array_filter( $filter_matched ) ), $title, $vars ); |
| 705 | + $matched_filters, $title, $vars ); |
696 | 706 | |
697 | 707 | $action = $vars->getVar( 'ACTION' )->toString(); |
698 | 708 | |
— | — | @@ -784,7 +794,11 @@ |
785 | 795 | // Increment trigger counter |
786 | 796 | $wgMemc->incr( self::filterMatchesKey() ); |
787 | 797 | |
788 | | - $dbw->insert( 'abuse_filter_log', $log_rows, __METHOD__ ); |
| 798 | + $local_log_ids = array(); |
| 799 | + foreach( $log_rows as $row ) { |
| 800 | + $dbw->insert( 'abuse_filter_log', $row, __METHOD__ ); |
| 801 | + $local_log_ids[] = $dbw->insertId(); |
| 802 | + } |
789 | 803 | |
790 | 804 | if ( count( $logged_local_filters ) ) { |
791 | 805 | // Update hit-counter. |
— | — | @@ -795,6 +809,8 @@ |
796 | 810 | ); |
797 | 811 | } |
798 | 812 | |
| 813 | + $global_log_ids = array(); |
| 814 | + |
799 | 815 | // Global stuff |
800 | 816 | if ( count( $logged_global_filters ) ) { |
801 | 817 | $vars->computeDBVars(); |
— | — | @@ -807,7 +823,9 @@ |
808 | 824 | global $wgAbuseFilterCentralDB; |
809 | 825 | $fdb = wfGetDB( DB_MASTER, array(), $wgAbuseFilterCentralDB ); |
810 | 826 | |
811 | | - $fdb->insert( 'abuse_filter_log', $central_log_rows, __METHOD__ ); |
| 827 | + foreach( $central_log_rows as $row ) { |
| 828 | + $fdb->insert( 'abuse_filter_log', $row, __METHOD__ ); |
| 829 | + } |
812 | 830 | |
813 | 831 | $fdb->update( 'abuse_filter', |
814 | 832 | array( 'af_hit_count=af_hit_count+1' ), |
— | — | @@ -816,6 +834,9 @@ |
817 | 835 | ); |
818 | 836 | } |
819 | 837 | |
| 838 | + $vars->setVar( 'global_log_ids', $global_log_ids ); |
| 839 | + $vars->setVar( 'local_log_ids', $local_log_ids ); |
| 840 | + |
820 | 841 | // Check for emergency disabling. |
821 | 842 | $total = $wgMemc->get( AbuseFilter::filterUsedKey() ); |
822 | 843 | self::checkEmergencyDisable( $logged_local_filters, $total ); |
Index: trunk/extensions/AbuseFilter/AbuseFilter.i18n.php |
— | — | @@ -91,6 +91,7 @@ |
92 | 92 | 'abusefilter-log-detailedentry-global' => 'global filter $1', |
93 | 93 | 'abusefilter-log-detailedentry-local' => 'filter $1', |
94 | 94 | 'abusefilter-log-detailslink' => 'details', |
| 95 | + 'abusefilter-log-diff' => 'diff', |
95 | 96 | 'abusefilter-log-hidelink' => 'adjust visibility', |
96 | 97 | 'abusefilter-log-details-legend' => 'Details for log entry $1', |
97 | 98 | 'abusefilter-log-details-var' => 'Variable', |
— | — | @@ -103,7 +104,7 @@ |
104 | 105 | 'abusefilter-log-linkoncontribs' => 'abuse log', |
105 | 106 | 'abusefilter-log-linkoncontribs-text' => 'Abuse log for this user', |
106 | 107 | 'abusefilter-log-hidden' => '(entry hidden)', |
107 | | - 'abusefilter-log-hide' => 'hide or unhide', // @todo FIXME: Message unused? |
| 108 | + 'abusefilter-log-hidden-implicit' => '(hidden because revision has been deleted)', |
108 | 109 | 'abusefilter-log-cannot-see-details' => 'You do not have permission to see details of this entry.', |
109 | 110 | 'abusefilter-log-details-hidden' => 'You cannot view the details for this entry because it is hidden from public view.', |
110 | 111 | |
— | — | @@ -842,6 +843,8 @@ |
843 | 844 | * $2 is new user link or old user link. Link description is a user name', |
844 | 845 | 'abusefilter-diff-info' => "Header for the box containing the basic information about a user account, displayed on the 'user profile' tab of the [[Special:Preferences|user preferences]] special page.", |
845 | 846 | 'abusefilter-import-intro' => "Do not ''translate'' <nowiki>{{int:abusefilter-edit-export}}</nowiki>, <nowiki>{{int:abusefilter-tools-subtitle}}</nowiki>, and <nowiki>{{int:abusefilter-import-submit}}</nowiki> unless you absolute must substitute any of them.", |
| 847 | + 'abusefilter-log-diff' => 'Diff link text to a revision associated with an AbuseFilter log entry', |
| 848 | + 'abusefilter-log-hidden-implicit' => 'Explanatory text to be shown beside an abuse filter log entry if it cannot be viewed due to its corresponding revision being hidden', |
846 | 849 | ); |
847 | 850 | |
848 | 851 | /** Faeag Rotuma (Faeag Rotuma) |
Index: trunk/extensions/AbuseFilter/api/ApiQueryAbuseLog.php |
— | — | @@ -73,6 +73,7 @@ |
74 | 74 | $this->addFieldsIf( 'afl_var_dump', $fld_details ); |
75 | 75 | $this->addFieldsIf( 'afl_actions', $fld_result ); |
76 | 76 | $this->addFieldsIf( 'afl_deleted', $fld_hidden ); |
| 77 | + $this->addFields( 'afl_rev_id' ); |
77 | 78 | |
78 | 79 | if ( $fld_filter ) { |
79 | 80 | $this->addTables( 'abuse_filter' ); |
— | — | @@ -110,6 +111,11 @@ |
111 | 112 | $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->afl_timestamp ) ); |
112 | 113 | break; |
113 | 114 | } |
| 115 | + if ( SpecialAbuseLog::isHidden($row) && |
| 116 | + !SpecialAbuseLog::canSeeHidden( $user ) |
| 117 | + ) { |
| 118 | + continue; |
| 119 | + } |
114 | 120 | $entry = array(); |
115 | 121 | if ( $fld_ids ) { |
116 | 122 | $entry['id'] = intval( $row->afl_id ); |
— | — | @@ -147,7 +153,10 @@ |
148 | 154 | } |
149 | 155 | |
150 | 156 | if ( $fld_hidden ) { |
151 | | - $entry['hidden'] = $row->afl_deleted; |
| 157 | + $val = SpecialAbuseLog::isHidden($row); |
| 158 | + if ( $val ) { |
| 159 | + $entry['hidden'] = $val; |
| 160 | + } |
152 | 161 | } |
153 | 162 | |
154 | 163 | if ( $entry ) { |
Index: trunk/extensions/AbuseFilter/AbuseFilter.hooks.php |
— | — | @@ -4,6 +4,7 @@ |
5 | 5 | } |
6 | 6 | |
7 | 7 | class AbuseFilterHooks { |
| 8 | + static $successful_action_vars = false; |
8 | 9 | // So far, all of the error message out-params for these hooks accept HTML. |
9 | 10 | // Hooray! |
10 | 11 | |
— | — | @@ -20,6 +21,8 @@ |
21 | 22 | // Load vars |
22 | 23 | $vars = new AbuseFilterVariableHolder; |
23 | 24 | |
| 25 | + self::$successful_action_vars = false; |
| 26 | + |
24 | 27 | // Check for null edits. |
25 | 28 | $oldtext = ''; |
26 | 29 | |
— | — | @@ -41,9 +44,9 @@ |
42 | 45 | |
43 | 46 | global $wgUser; |
44 | 47 | $vars->addHolder( AbuseFilter::generateUserVars( $wgUser ) ); |
45 | | - $vars->addHolder( AbuseFilter::generateTitleVars( $title , 'ARTICLE' ) ); |
46 | | - $vars->setVar( 'ACTION', 'edit' ); |
47 | | - $vars->setVar( 'SUMMARY', $summary ); |
| 48 | + $vars->addHolder( AbuseFilter::generateTitleVars( $title , 'article' ) ); |
| 49 | + $vars->setVar( 'action', 'edit' ); |
| 50 | + $vars->setVar( 'summary', $summary ); |
48 | 51 | $vars->setVar( 'minor_edit', $editor->minoredit ); |
49 | 52 | |
50 | 53 | $vars->setVar( 'old_wikitext', $oldtext ); |
— | — | @@ -59,9 +62,60 @@ |
60 | 63 | $editor->showEditForm(); |
61 | 64 | return false; |
62 | 65 | } |
| 66 | + |
| 67 | + self::$successful_action_vars = $vars; |
| 68 | + |
63 | 69 | return true; |
64 | 70 | } |
65 | 71 | |
| 72 | + public static function onArticleSaveComplete( |
| 73 | + &$article, &$user, $text, $summary, $minoredit, $watchthis, $sectionanchor, |
| 74 | + &$flags, $revision |
| 75 | + ) { |
| 76 | + if ( ! self::$successful_action_vars ) { |
| 77 | + return true; |
| 78 | + } |
| 79 | + |
| 80 | + $vars = self::$successful_action_vars; |
| 81 | + |
| 82 | + if ( $vars->getVar('article_prefixedtext')->toString() !== |
| 83 | + $article->getTitle()->getPrefixedText() |
| 84 | + ) { |
| 85 | + return true; |
| 86 | + } |
| 87 | + |
| 88 | + if ( $vars->getVar('local_log_ids') ) { |
| 89 | + // Now actually do our storage |
| 90 | + $log_ids = $vars->getVar('local_log_ids')->toNative(); |
| 91 | + $dbw = wfGetDB( DB_MASTER ); |
| 92 | + |
| 93 | + if ( count($log_ids) ) { |
| 94 | + $dbw->update( 'abuse_filter_log', |
| 95 | + array( 'afl_rev_id' => $revision->getId() ), |
| 96 | + array( 'afl_id' => $log_ids ), |
| 97 | + __METHOD__ |
| 98 | + ); |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + if ( $vars->getVar('global_log_ids') ) { |
| 103 | + $log_ids = $vars->getVar('global_log_ids')->toNative(); |
| 104 | + |
| 105 | + global $wgAbuseFilterCentralDB; |
| 106 | + $fdb = wfGetDB( DB_MASTER, array(), $wgAbuseFilterCentralDB ); |
| 107 | + |
| 108 | + if ( count($log_ids) ) { |
| 109 | + $dbw->update( 'abuse_filter_log', |
| 110 | + array( 'afl_rev_id' => $revision->getId() ), |
| 111 | + array( 'afl_id' => $log_ids, 'afl_wiki' => wfWikiId() ), |
| 112 | + __METHOD__ |
| 113 | + ); |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + return true; |
| 118 | + } |
| 119 | + |
66 | 120 | public static function onGetAutoPromoteGroups( $user, &$promote ) { |
67 | 121 | global $wgMemc; |
68 | 122 | |
— | — | @@ -201,6 +255,7 @@ |
202 | 256 | $updater->addExtensionUpdate( array( 'addField', 'abuse_filter', 'af_deleted', "$dir/db_patches/patch-af_deleted.sql", true ) ); |
203 | 257 | $updater->addExtensionUpdate( array( 'addField', 'abuse_filter', 'af_actions', "$dir/db_patches/patch-af_actions.sql", true ) ); |
204 | 258 | $updater->addExtensionUpdate( array( 'addField', 'abuse_filter', 'af_global', "$dir/db_patches/patch-global_filters.sql", true ) ); |
| 259 | + $updater->addExtensionUpdate( array( 'addField', 'abuse_filter_log', 'afl_rev_id', "$dir/db_patches/patch-afl_action_id.sql", true ) ); |
205 | 260 | if ( $updater->getDB()->getType() == 'mysql' ) { |
206 | 261 | $updater->addExtensionUpdate( array( 'addIndex', 'abuse_filter_log', 'filter_timestamp', "$dir/db_patches/patch-fix-indexes.sql", true ) ); |
207 | 262 | } else { |