Index: trunk/extensions/AbuseFilter/AbuseFilter.class.php |
— | — | @@ -0,0 +1,496 @@ |
| 2 | +<?php |
| 3 | +if ( ! defined( 'MEDIAWIKI' ) ) |
| 4 | + die(); |
| 5 | + |
| 6 | +class AbuseFilter { |
| 7 | + |
| 8 | + public static function generateUserVars( $user ) { |
| 9 | + $vars = array(); |
| 10 | + |
| 11 | + // Load all the data we want. |
| 12 | + $user->load(); |
| 13 | + |
| 14 | + $vars['USER_EDITCOUNT'] = $user->getEditCount(); |
| 15 | + $vars['USER_AGE'] = time() - wfTimestampOrNull( TS_UNIX, $user->getRegistration() ); |
| 16 | + $vars['USER_NAME'] = $user->getName(); |
| 17 | + $vars['USER_GROUPS'] = implode(',', $user->getEffectiveGroups() ); |
| 18 | + $vars['USER_EMAILCONFIRM'] = $user->getEmailAuthenticationTimestamp(); |
| 19 | + |
| 20 | + // More to come |
| 21 | + |
| 22 | + return $vars; |
| 23 | + } |
| 24 | + |
| 25 | + public static function generateTitleVars( $title, $prefix ) { |
| 26 | + $vars = array(); |
| 27 | + |
| 28 | + $vars[$prefix."_NAMESPACE"] = $title->getNamespace(); |
| 29 | + $vars[$prefix."_TEXT"] = $title->getText(); |
| 30 | + $vars[$prefix."_PREFIXEDTEXT"] = $title->getPrefixedText(); |
| 31 | + |
| 32 | + if ($title->mRestrictionsLoaded) { |
| 33 | + // Don't bother if they're unloaded |
| 34 | + foreach( $title->mRestrictions as $action => $rights ) { |
| 35 | + $rights = count($rights) ? $rights : array(); |
| 36 | + $vars[$prefix."_RESTRICTIONS_".$action] = implode(',', $rights ); |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + return $vars; |
| 41 | + } |
| 42 | + |
| 43 | + public static function checkConditions( $conds, $vars ) { |
| 44 | + $modifierWords = array( 'norm', 'supernorm', 'lcase', 'length' ); |
| 45 | + $operatorWords = array( 'eq', 'neq', 'gt', 'lt', 'regex' ); |
| 46 | + $validJoinConditions = array( '!', '|', '&' ); |
| 47 | + |
| 48 | + // Remove leading/trailing spaces |
| 49 | + $conds = trim($conds); |
| 50 | + |
| 51 | + // Is it a set? |
| 52 | + if (substr( $conds, 0, 1 ) == '(' && substr( $conds, -1, 1 ) == ')' ) { |
| 53 | + // We should have a set here. |
| 54 | + $setInternal = substr($conds,1,-1); |
| 55 | + |
| 56 | + // Get the join condition ( &, | or ! ) |
| 57 | + list($setJoinCondition,$conditionList) = explode(':', $setInternal, 2 ); |
| 58 | + $setJoinCondition = trim($setJoinCondition); |
| 59 | + |
| 60 | + if (!in_array( $setJoinCondition, $validJoinConditions )) { |
| 61 | + // Bad join condition |
| 62 | + return false; |
| 63 | + } |
| 64 | + |
| 65 | + // Tokenise. |
| 66 | + $allConditions = self::tokeniseList( $conditionList ); |
| 67 | + |
| 68 | + foreach( $allConditions as $thisCond ) { |
| 69 | + |
| 70 | + if (trim($thisCond)=='') { |
| 71 | + // Ignore it |
| 72 | + $result = true; |
| 73 | + } else { |
| 74 | + $result = self::checkConditions( $thisCond, $vars ); |
| 75 | + } |
| 76 | + |
| 77 | + // Need we short-circuit? |
| 78 | + if ($setJoinCondition == '|' && $result) { |
| 79 | + // Definite yes. |
| 80 | + return true; |
| 81 | + } elseif ($setJoinCondition == '&' && !$result) { |
| 82 | + // Definite no. |
| 83 | + return false; |
| 84 | + } elseif ($setJoinCondition == '!' && $result) { |
| 85 | + // Definite no. |
| 86 | + return false; |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + // Return the default result. |
| 91 | + return ($setJoinCondition != '|'); // Only OR returns false after checking all conditions. |
| 92 | + } |
| 93 | + |
| 94 | + // Grab the first word. |
| 95 | + list ($thisWord) = explode( ' ', $conds ); |
| 96 | + $wordNum = 0; |
| 97 | + |
| 98 | + // Check for modifiers |
| 99 | + $modifier = ''; |
| 100 | + if (in_array( $thisWord, $modifierWords ) ) { |
| 101 | + $modifier = $thisWord; |
| 102 | + $wordNum++; |
| 103 | + $thisWord = explode( ' ', $conds ); |
| 104 | + $thisWord = $thisWord[$wordNum]; |
| 105 | + } |
| 106 | + |
| 107 | + if ( in_array( $thisWord, array_keys($vars ) ) ) { |
| 108 | + |
| 109 | + $value = $vars[$thisWord]; |
| 110 | + if ($modifier) { |
| 111 | + $value = self::modifyValue( $modifier, $value ); |
| 112 | + } |
| 113 | + |
| 114 | + // We have a variable. Now read the next word to see what we're doing with it. |
| 115 | + $wordNum++; |
| 116 | + $thisWord = explode( ' ', $conds ); |
| 117 | + $thisWord = $thisWord[$wordNum]; |
| 118 | + |
| 119 | + if ( in_array( $thisWord, $operatorWords ) ) { |
| 120 | + // Get the rest of the string after the operator. |
| 121 | + $parameters = explode( ' ', $conds, $wordNum+2); |
| 122 | + $parameters = $parameters[$wordNum+1]; |
| 123 | + |
| 124 | + return self::checkOperator( $thisWord, $value, $parameters ); |
| 125 | + } |
| 126 | + } else { |
| 127 | + print "Word $thisWord is not a variable (".implode(',', array_keys($vars)).")\n"; |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + public static function tokeniseList( $list ) { |
| 132 | + // Parse it, character by character. |
| 133 | + $escapeNext = false; |
| 134 | + $listLevel = 0; |
| 135 | + $thisToken = ''; |
| 136 | + $allTokens = array(); |
| 137 | + for( $i=0;$i<strlen($list);$i++ ) { |
| 138 | + $char = substr( $list, $i, 1 ); |
| 139 | + |
| 140 | + $suppressAdd = false; |
| 141 | + |
| 142 | + // We don't care about semicolons and so on unless it's |
| 143 | + if ($listLevel == 0) { |
| 144 | + if ($char == "\\") { |
| 145 | + if ($escapeNext) { // Escaped backslash |
| 146 | + $escapeNext = false; |
| 147 | + } else { |
| 148 | + $escapeNext = true; |
| 149 | + $suppressAdd = true; |
| 150 | + } |
| 151 | + } elseif ($char == ';') { |
| 152 | + if ($escapeNext) { |
| 153 | + $escapeNext = false; // Escaped semicolon |
| 154 | + } else { // Next token, plz |
| 155 | + $escapeNext = false; |
| 156 | + $allTokens[] = $thisToken; |
| 157 | + $thisToken = ''; |
| 158 | + $suppressAdd = true; |
| 159 | + } |
| 160 | + } elseif ($escapeNext) { |
| 161 | + $escapeNext = false; |
| 162 | + $thisToken .= "\\"; // The backslash wasn't intended to escape. |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + if ($char == '(' && $lastChar == ';') { |
| 167 | + // A list! |
| 168 | + $listLevel++; |
| 169 | + } elseif ($char == ')' && ($lastChar == ';' || $lastChar == ')' || $lastChar = '') ) { |
| 170 | + $listLevel--; // End of a list. |
| 171 | + } |
| 172 | + |
| 173 | + if (!$suppressAdd) { |
| 174 | + $thisToken .= $char; |
| 175 | + } |
| 176 | + |
| 177 | + // Ignore whitespace. |
| 178 | + if ($char != ' ') { |
| 179 | + $lastChar = $char; |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + // Put any leftovers in |
| 184 | + $allTokens[] = $thisToken; |
| 185 | + |
| 186 | + return $allTokens; |
| 187 | + } |
| 188 | + |
| 189 | + public static function modifyValue( $modifier, $value ) { |
| 190 | + if ($modifier == 'norm') { |
| 191 | + return self::normalise( $value ); |
| 192 | + } elseif ($modifier == 'supernorm') { |
| 193 | + return self::superNormalise( $value ); |
| 194 | + } elseif ($modifier == 'lcase') { |
| 195 | + return strtolower($value); |
| 196 | + } elseif ($modifier == 'length') { |
| 197 | + return strlen($value); |
| 198 | + } elseif ($modifier == 'specialratio') { |
| 199 | + $specialsonly = preg_replace('/\w/', '', $value ); |
| 200 | + return (strlen($specialsonly) / strlen($value)); |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + public static function checkOperator( $operator, $value, $parameters ) { |
| 205 | + if ($operator == 'eq') { |
| 206 | + return $value == $parameters; |
| 207 | + } elseif ($operator == 'neq') { |
| 208 | + return $value != $parameters; |
| 209 | + } elseif ($operator == 'gt') { |
| 210 | + return $value > $parameters; |
| 211 | + } elseif ($operator == 'lt') { |
| 212 | + return $value < $parameters; |
| 213 | + } elseif ($operator == 'regex') { |
| 214 | + return preg_match( $parameters, $value ); |
| 215 | + } else { |
| 216 | + return false; |
| 217 | + } |
| 218 | + } |
| 219 | + |
| 220 | + public static function superNormalise( $text ) { |
| 221 | + $text = self::normalise( $text ); |
| 222 | + $text = preg_split( '//', $text, -1, PREG_SPLIT_NO_EMPTY ); // Split to a char array. |
| 223 | + sort($text); |
| 224 | + $text = array_unique( $text ); // Remove duplicate characters. |
| 225 | + $text = implode( '', $text ); |
| 226 | + |
| 227 | + return $text; |
| 228 | + } |
| 229 | + |
| 230 | + public static function normalise( $text ) { |
| 231 | + $old_text = $text; |
| 232 | + $text = preg_replace( '/\W/', '', $text ); // Remove any special characters. |
| 233 | + $text = strtolower($text); |
| 234 | + $text = preg_split( '//', $text, -1, PREG_SPLIT_NO_EMPTY ); // Split to a char array. |
| 235 | + $text = AntiSpoof::equivString( $text ); // Normalise |
| 236 | + |
| 237 | + // Remove repeated characters, but not all duplicates. |
| 238 | + $oldText = $text; |
| 239 | + $text = array($oldText[0]); |
| 240 | + for ($i=1;$i<count($oldText);$i++) { |
| 241 | + if ($oldText[$i] != $oldText[$i-1]) { |
| 242 | + $text[] = $oldText[$i]; |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + $text = implode('', $text ); // Sort in alphabetical order, put back as it was. |
| 247 | + |
| 248 | + return $text; |
| 249 | + } |
| 250 | + |
| 251 | + public static function filterAction( $vars, $title ) { |
| 252 | + global $wgUser; |
| 253 | + |
| 254 | + // Fetch from the database. |
| 255 | + $dbr = wfGetDB( DB_SLAVE ); |
| 256 | + $res = $dbr->select( 'abuse_filter', '*', array( ) ); |
| 257 | + |
| 258 | + $blocking_filters = array(); |
| 259 | + $log_entries = array(); |
| 260 | + $log_template = array( 'afl_user' => $wgUser->getId(), 'afl_user_text' => $wgUser->getName(), |
| 261 | + 'afl_var_dump' => serialize( $vars ), 'afl_timestamp' => $dbr->timestamp(wfTimestampNow()), |
| 262 | + 'afl_namespace' => $title->getNamespace(), 'afl_title' => $title->getDbKey(), 'afl_ip' => wfGetIp() ); |
| 263 | + $doneActionsByFilter = array(); |
| 264 | + |
| 265 | + while ( $row = $dbr->fetchObject( $res ) ) { |
| 266 | + if ( self::checkConditions( $row->af_pattern, $vars ) ) { |
| 267 | + $blocking_filters[$row->af_id] = $row; |
| 268 | + |
| 269 | + $newLog = $log_template; |
| 270 | + $newLog['afl_filter'] = $row->af_id; |
| 271 | + $newLog['afl_action'] = $vars['ACTION']; |
| 272 | + $log_entries[] = $newLog; |
| 273 | + |
| 274 | + $doneActionsByFilter[$row->af_id] = array(); |
| 275 | + } |
| 276 | + } |
| 277 | + |
| 278 | + if (count($blocking_filters) == 0 ) { |
| 279 | + // No problems. |
| 280 | + return true; |
| 281 | + } |
| 282 | + |
| 283 | + // Retrieve the consequences. |
| 284 | + $res = $dbr->select( 'abuse_filter_action', '*', array( 'afa_filter' => array_keys( $blocking_filters ) ), __METHOD__, array( "ORDER BY" => " (afa_consequence in ('throttle','warn')) desc" ) ); |
| 285 | + // We want throttles, warnings first, as they have a bit of a special treatment. |
| 286 | + |
| 287 | + $actions_done = array(); |
| 288 | + $throttled_filters = array(); |
| 289 | + |
| 290 | + $display = ''; |
| 291 | + |
| 292 | + while ( $row = $dbr->fetchObject( $res ) ) { |
| 293 | + $action_key = md5( $row->afa_consequence . $row->afa_parameters ); |
| 294 | + if (!in_array( $action_key, $actions_done ) && !in_array( $row->afa_filter, $throttled_filters ) ) { |
| 295 | + $parameters = explode( "\n", $row->afa_parameters ); |
| 296 | + $result = self::takeConsequenceAction( $row->afa_consequence, $parameters, $title, $vars, &$display, &$continue ); |
| 297 | + $doneActionsByFilter[$row->afa_filter][] = $row->afa_consequence; |
| 298 | + if (!$result) { |
| 299 | + $throttled_filters[] = $row->afa_filter; // Only execute other actions for a filter if that filter's rate limiter has been tripped. |
| 300 | + } |
| 301 | + $actions_done[] = $action_key; |
| 302 | + } else { |
| 303 | + // Ignore it, until we hit the rate limit. |
| 304 | + } |
| 305 | + } |
| 306 | + |
| 307 | + $dbw = wfGetDB( DB_MASTER ); |
| 308 | + |
| 309 | + // Log it |
| 310 | + foreach( $log_entries as $index => $entry ) { |
| 311 | + $log_entries[$index]['afl_actions'] = implode( ',', $doneActionsByFilter[$entry['afl_filter']] ); |
| 312 | + |
| 313 | + // Increment the hit counter |
| 314 | + $dbw->update( 'abuse_filter', array( 'af_hit_count=af_hit_count+1' ), array( 'af_id' => $entry['afl_filter'] ), __METHOD__ ); |
| 315 | + } |
| 316 | + |
| 317 | + $dbw->insert( 'abuse_filter_log', $log_entries, __METHOD__ ); |
| 318 | + |
| 319 | + return $display; |
| 320 | + } |
| 321 | + |
| 322 | + public static function takeConsequenceAction( $action, $parameters, $title, $vars, &$display, &$continue ) { |
| 323 | + switch ($action) { |
| 324 | + case 'warn': |
| 325 | + wfLoadExtensionMessages( 'AbuseFilter' ); |
| 326 | + |
| 327 | + if (!$_SESSION['abusefilter-warned']) { |
| 328 | + $_SESSION['abusefilter-warned'] = true; |
| 329 | + |
| 330 | + // Threaten them a little bit |
| 331 | + if (strlen($parameters[0])) { |
| 332 | + $display .= call_user_func_array( 'wfMsg', $parameters ) . "\n"; |
| 333 | + } else { |
| 334 | + // Generic message. |
| 335 | + $display .= wfMsg( 'abusefilter-warning' ); |
| 336 | + } |
| 337 | + |
| 338 | + return false; // Don't apply the other stuff yet. |
| 339 | + } else { |
| 340 | + // We already warned them |
| 341 | + $_SESSION['abusefilter-warned'] = false; |
| 342 | + } |
| 343 | + break; |
| 344 | + |
| 345 | + case 'disallow': |
| 346 | + wfLoadExtensionMessages( 'AbuseFilter' ); |
| 347 | + |
| 348 | + // Don't let them do it |
| 349 | + if (strlen($parameters[0])) { |
| 350 | + $display .= call_user_func_array( 'wfMsg', $parameters ) . "\n"; |
| 351 | + } else { |
| 352 | + // Generic message. |
| 353 | + $display .= wfMsg( 'abusefilter-disallowed' ); |
| 354 | + } |
| 355 | + break; |
| 356 | + |
| 357 | + case 'block': |
| 358 | + wfLoadExtensionMessages( 'AbuseFilter' ); |
| 359 | + |
| 360 | + global $wgUser; |
| 361 | + |
| 362 | + // Create a block. |
| 363 | + $block = new Block; |
| 364 | + $block->mAddress = $wgUser->getName(); |
| 365 | + $block->mUser = $wgUser->getId(); |
| 366 | + $block->mByName = wfMsgForContent( 'abusefilter-blocker' ); |
| 367 | + $block->mReason = wfMsgForContent( 'abusefilter-blockreason' ); |
| 368 | + $block->mTimestamp = wfTimestampNow(); |
| 369 | + $block->mEnableAutoblock = 1; |
| 370 | + $block->mAngryAutoblock = 1; // Block lots of IPs |
| 371 | + $block->mCreateAccount = 1; |
| 372 | + $block->mExpiry = 'infinity'; |
| 373 | + |
| 374 | + $block->insert(); |
| 375 | + |
| 376 | + $display .= wfMsg( 'abusefilter-blocked-display' ); |
| 377 | + break; |
| 378 | + case 'throttle': |
| 379 | + $throttleId = array_shift( $parameters ); |
| 380 | + list( $rateCount, $ratePeriod ) = explode( ',', array_shift( $parameters ) ); |
| 381 | + |
| 382 | + $hitThrottle = false; |
| 383 | + |
| 384 | + // The rest are throttle-types. |
| 385 | + foreach( $parameters as $throttleType ) { |
| 386 | + $hitThrottle = $hitThrottle || self::isThrottled( $throttleId, $throttleType, $title, $rateCount, $ratePeriod ); |
| 387 | + } |
| 388 | + |
| 389 | + return $hitThrottle; |
| 390 | + break; |
| 391 | + case 'degroup': |
| 392 | + wfLoadExtensionMessages( 'AbuseFilter' ); |
| 393 | + |
| 394 | + global $wgUser; |
| 395 | + |
| 396 | + // Remove all groups from the user. Ouch. |
| 397 | + $groups = $wgUser->getGroups(); |
| 398 | + |
| 399 | + foreach( $groups as $group ) { |
| 400 | + $wgUser->removeGroup( $group ); |
| 401 | + } |
| 402 | + |
| 403 | + $display = wfMsg( 'abusefilter-degrouped' ); |
| 404 | + |
| 405 | + break; |
| 406 | + case 'blockautopromote': |
| 407 | + wfLoadExtensionMessages( 'AbuseFilter' ); |
| 408 | + |
| 409 | + global $wgUser, $wgMemc; |
| 410 | + |
| 411 | + $blockPeriod = (int)mt_rand( 3*86400, 7*86400 ); // Block for 3-7 days. |
| 412 | + $wgMemc->set( self::autoPromoteBlockKey( $wgUser ), true, $blockPeriod ); |
| 413 | + |
| 414 | + $display = wfMsg( 'abusefilter-autopromote-blocked' ); |
| 415 | + |
| 416 | + break; |
| 417 | + |
| 418 | + case 'flag': |
| 419 | + // Do nothing. Here for completeness. |
| 420 | + break; |
| 421 | + } |
| 422 | + |
| 423 | + return true; |
| 424 | + } |
| 425 | + |
| 426 | + public static function isThrottled( $throttleId, $types, $title, $rateCount, $ratePeriod ) { |
| 427 | + global $wgMemc; |
| 428 | + |
| 429 | + $key = self::throttleKey( $throttleId, $types, $title ); |
| 430 | + $count = $wgMemc->get( $key ); |
| 431 | + |
| 432 | + if ($count > 0) { |
| 433 | + $wgMemc->incr( $key ); |
| 434 | + if ($count > $rateCount) { |
| 435 | + //die( "Hit rate limiter: $count actions, against limit of $rateCount actions in $ratePeriod seconds (key is $key).\n" ); |
| 436 | + $wgMemc->delete( $key ); |
| 437 | + return true; // THROTTLED |
| 438 | + } |
| 439 | + } else { |
| 440 | + $wgMemc->add( $key, 1, $ratePeriod ); |
| 441 | + } |
| 442 | + |
| 443 | + return false; // NOT THROTTLED |
| 444 | + } |
| 445 | + |
| 446 | + public static function throttleIdentifier( $type, $title ) { |
| 447 | + global $wgUser; |
| 448 | + |
| 449 | + switch ($type) { |
| 450 | + case 'ip': |
| 451 | + $identifier = wfGetIp(); |
| 452 | + break; |
| 453 | + case 'user': |
| 454 | + $identifier = $wgUser->getId(); |
| 455 | + break; |
| 456 | + case 'range': |
| 457 | + $identifier = substr(IP::toHex(wfGetIp()),0,4); |
| 458 | + break; |
| 459 | + case 'creationdate': |
| 460 | + $reg = $wgUser->getRegistration(); |
| 461 | + $identifier = $reg - ($reg % 86400); |
| 462 | + break; |
| 463 | + case 'editcount': |
| 464 | + // Hack for detecting different single-purpose accounts. |
| 465 | + $identifier = $wgUser->getEditCount(); |
| 466 | + break; |
| 467 | + case 'site': |
| 468 | + return 1; |
| 469 | + break; |
| 470 | + case 'page': |
| 471 | + return $title->getPrefixedText(); |
| 472 | + break; |
| 473 | + } |
| 474 | + |
| 475 | + return $identifier; |
| 476 | + } |
| 477 | + |
| 478 | + public static function throttleKey( $throttleId, $type, $title ) { |
| 479 | + $identifier = ''; |
| 480 | + |
| 481 | + $types = explode(',', $type); |
| 482 | + |
| 483 | + $identifiers = array(); |
| 484 | + |
| 485 | + foreach( $types as $subtype ) { |
| 486 | + $identifiers[] = self::throttleIdentifier( $subtype, $title ); |
| 487 | + } |
| 488 | + |
| 489 | + $identifier = implode( ':', $identifiers ); |
| 490 | + |
| 491 | + return wfMemcKey( 'abusefilter', 'throttle', $throttleId, $type, $identifier ); |
| 492 | + } |
| 493 | + |
| 494 | + public static function autoPromoteBlockKey( $user ) { |
| 495 | + return wfMemcKey( 'abusefilter', 'block-autopromote', $user->getId() ); |
| 496 | + } |
| 497 | +} |
Property changes on: trunk/extensions/AbuseFilter/AbuseFilter.class.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 498 | + native |
Index: trunk/extensions/AbuseFilter/SpecialAbuseLog.php |
— | — | @@ -0,0 +1,220 @@ |
| 2 | +<?php |
| 3 | +if ( ! defined( 'MEDIAWIKI' ) ) |
| 4 | + die(); |
| 5 | + |
| 6 | +class SpecialAbuseLog extends SpecialPage { |
| 7 | + |
| 8 | + function __construct() { |
| 9 | + wfLoadExtensionMessages('AbuseFilter'); |
| 10 | + parent::__construct( 'AbuseLog', 'abusefilter-log' ); |
| 11 | + } |
| 12 | + |
| 13 | + function execute( $parameter ) { |
| 14 | + global $wgUser,$wgOut,$wgRequest; |
| 15 | + |
| 16 | + $this->setHeaders(); |
| 17 | + $this->loadParameters(); |
| 18 | + |
| 19 | + $wgOut->setPageTitle( wfMsg( 'abusefilter-log' ) ); |
| 20 | + $wgOut->setRobotpolicy( "noindex,nofollow" ); |
| 21 | + $wgOut->setArticleRelated( false ); |
| 22 | + $wgOut->enableClientCache( false ); |
| 23 | + |
| 24 | + // Are we allowed? |
| 25 | + if ( count( $errors = $this->getTitle()->getUserPermissionsErrors( 'abusefilter-log', $wgUser, true, array( 'ns-specialprotected' ) ) ) ) { |
| 26 | + // Go away. |
| 27 | + $wgOut->showPermissionsErrorPage( $errors, 'abusefilter-log' ); |
| 28 | + return; |
| 29 | + } |
| 30 | + |
| 31 | + // Show the search form. |
| 32 | + $this->searchForm(); |
| 33 | + |
| 34 | + // Show the log itself. |
| 35 | + $this->showList(); |
| 36 | + } |
| 37 | + |
| 38 | + function loadParameters() { |
| 39 | + global $wgRequest; |
| 40 | + |
| 41 | + $this->mSearchUser = $wgRequest->getText( 'wpSearchUser' ); |
| 42 | + $this->mSearchTitle = $wgRequest->getText( 'wpSearchTitle' ); |
| 43 | + if ($this->canSeeDetails()) |
| 44 | + $this->mSearchFilter = $wgRequest->getIntOrNull( 'wpSearchFilter' ); |
| 45 | + |
| 46 | + $detailsid = $wgRequest->getIntOrNull( 'details' ); |
| 47 | + |
| 48 | + if ($detailsid) { |
| 49 | + $this->showDetails( $detailsid ); |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + function searchForm() { |
| 54 | + global $wgOut, $wgUser; |
| 55 | + |
| 56 | + $output = Xml::element( 'legend', null, wfMsg( 'abusefilter-log-search' ) ); |
| 57 | + $fields = array(); |
| 58 | + |
| 59 | + // Search conditions |
| 60 | + $fields['abusefilter-log-search-user'] = wfInput( 'wpSearchUser', 45, $this->mSearchUser ); |
| 61 | + if ($this->canSeeDetails()) |
| 62 | + $fields['abusefilter-log-search-filter'] = wfInput( 'wpSearchFilter', 45, $this->mSearchFilter ); |
| 63 | + $fields['abusefilter-log-search-title'] = wfInput( 'wpSearchTitle', 45, $this->mSearchTitle ); |
| 64 | + |
| 65 | + $form = Xml::hidden( 'title', $this->getTitle()->getPrefixedText() ); |
| 66 | + |
| 67 | + $form .= Xml::buildForm( $fields, 'abusefilter-log-search-submit' ); |
| 68 | + $output .= Xml::tags( 'form', array( 'method' => 'GET', 'action' => $this->getTitle()->getLocalURL() ), $form ); |
| 69 | + $output = Xml::tags( 'fieldset', null, $output ); |
| 70 | + |
| 71 | + $wgOut->addHtml( $output ); |
| 72 | + } |
| 73 | + |
| 74 | + function showList() { |
| 75 | + global $wgOut; |
| 76 | + |
| 77 | + // Generate conditions list. |
| 78 | + $conds = array(); |
| 79 | + |
| 80 | + if ($this->mSearchUser) |
| 81 | + $conds['afl_user_text'] = $this->mSearchUser; |
| 82 | + if ($this->mSearchFilter) |
| 83 | + $conds['afl_filter'] = $this->mSearchFilter; |
| 84 | + |
| 85 | + $searchTitle = Title::newFromText( $this->mSearchTitle ); |
| 86 | + if ($this->mSearchTitle && $searchTitle) { |
| 87 | + $conds['afl_namespace'] = $searchTitle->getNamespace(); |
| 88 | + $conds['afl_title'] = $searchTitle->getDbKey(); |
| 89 | + } |
| 90 | + |
| 91 | + $pager = new AbuseLogPager( $this, $conds ); |
| 92 | + |
| 93 | + $wgOut->addHtml( $pager->getNavigationBar() . |
| 94 | + Xml::tags( 'ul', null, $pager->getBody() ) . |
| 95 | + $pager->getNavigationBar() ); |
| 96 | + } |
| 97 | + |
| 98 | + function showDetails( $id ) { |
| 99 | + if (!$this->canSeeDetails()) { |
| 100 | + return; |
| 101 | + } |
| 102 | + |
| 103 | + $dbr = wfGetDB( DB_SLAVE ); |
| 104 | + |
| 105 | + $row = $dbr->selectRow( array('abuse_filter_log','abuse_filter'), '*', array( 'afl_id' => $id, 'af_id=afl_filter' ), __METHOD__ ); |
| 106 | + |
| 107 | + if (!$row) |
| 108 | + return; |
| 109 | + |
| 110 | + $output = ''; |
| 111 | + |
| 112 | + $output .= Xml::element( 'legend', null, wfMsg( 'abusefilter-log-details-legend', $id ) ); |
| 113 | + |
| 114 | + $output .= Xml::tags( 'p', null, $this->formatRow( $row ) ); |
| 115 | + |
| 116 | + $output .= Xml::element( 'h3', null, wfMsg( 'abusefilter-log-details-vars' ) ); |
| 117 | + |
| 118 | + // Build a table. |
| 119 | + $vars = unserialize( $row->afl_var_dump ); |
| 120 | + |
| 121 | + $output .= Xml::openElement( 'table', array( 'class' => 'wikitable' ) ) . Xml::openElement( 'tbody' ); |
| 122 | + |
| 123 | + $header = Xml::element( 'th', null, wfMsg( 'abusefilter-log-details-var' ) ) . Xml::element( 'th', null, wfMsg( 'abusefilter-log-details-val' ) ); |
| 124 | + $output .= Xml::tags( 'tr', null, $header ); |
| 125 | + |
| 126 | + // Now, build the body of the table. |
| 127 | + |
| 128 | + foreach( $vars as $key => $value ) { |
| 129 | + $trow = Xml::element( 'td', null, $key ) . Xml::element( 'td', null, $value ); |
| 130 | + $output .= Xml::tags( 'tr', null, $trow ); |
| 131 | + } |
| 132 | + |
| 133 | + $output .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); |
| 134 | + |
| 135 | + // Private stuff, like IPs. |
| 136 | + $output .= Xml::element( 'h3', null, wfMsg( 'abusefilter-log-details-private' ) ); |
| 137 | + $output .= Xml::openElement( 'table', array( 'class' => 'wikitable' ) ) . Xml::openElement( 'tbody' ); |
| 138 | + $output .= $header; |
| 139 | + |
| 140 | + // IP address |
| 141 | + $output .= Xml::tags( 'tr', null, Xml::element( 'td', null, wfMsg('abusefilter-log-details-ip' ) ) . Xml::element( 'td', null, $row->afl_ip ) ); |
| 142 | + |
| 143 | + $output .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); |
| 144 | + |
| 145 | + $output = Xml::tags( 'fieldset', null, $output ); |
| 146 | + |
| 147 | + global $wgOut; |
| 148 | + $wgOut->addHTML( $output ); |
| 149 | + } |
| 150 | + |
| 151 | + function canSeeDetails() { |
| 152 | + global $wgUser; |
| 153 | + return !count($this->getTitle()->getUserPermissionsErrors( 'abusefilter-log-detail', $wgUser, true, array( 'ns-specialprotected' ) )); |
| 154 | + } |
| 155 | + |
| 156 | + function formatRow( $row ) { |
| 157 | + global $wgLang,$wgUser; |
| 158 | + |
| 159 | + ## One-time setup |
| 160 | + static $sk=null; |
| 161 | + |
| 162 | + if (is_null($sk)) { |
| 163 | + $sk = $wgUser->getSkin(); |
| 164 | + } |
| 165 | + |
| 166 | + $title = Title::makeTitle( $row->afl_namespace, $row->afl_title ); |
| 167 | + |
| 168 | + $user = $sk->userLink( $row->afl_user, $row->afl_user_text ) . |
| 169 | + $sk->userToolLinks( $row->afl_user, $row->afl_user_text ); |
| 170 | + |
| 171 | + $description = ''; |
| 172 | + |
| 173 | + $timestamp = $wgLang->timeanddate( $row->afl_timestamp ); |
| 174 | + |
| 175 | + $actions_taken = $row->afl_actions; |
| 176 | + if (!strlen(trim($actions_taken))) { |
| 177 | + $actions_taken = wfMsg( 'abusefilter-log-noactions' ); |
| 178 | + } |
| 179 | + |
| 180 | + if ($this->canSeeDetails()) { |
| 181 | + $detailsLink = $sk->makeKnownLinkObj( $this->getTitle( ), wfMsg( 'abusefilter-log-detailslink' ), 'details='.$row->afl_id ); |
| 182 | + |
| 183 | + $description = wfMsg( 'abusefilter-log-detailedentry', $timestamp, $user, $row->afl_filter, $row->afl_action, $sk->makeKnownLinkObj( $title ), $actions_taken, $row->af_public_comments, $detailsLink ); |
| 184 | + } else { |
| 185 | + $description = wfMsg( 'abusefilter-log-entry', $timestamp, $user, $row->afl_action, $sk->makeKnownLinkObj( $title ), $actions_taken, $row->af_public_comments ); |
| 186 | + } |
| 187 | + |
| 188 | + return Xml::tags( 'li', null, $description ); |
| 189 | + } |
| 190 | + |
| 191 | +} |
| 192 | + |
| 193 | +class AbuseLogPager extends ReverseChronologicalPager { |
| 194 | + public $mForm, $mConds; |
| 195 | + |
| 196 | + function __construct( $form, $conds = array(), $details = false ) { |
| 197 | + $this->mForm = $form; |
| 198 | + $this->mConds = $conds; |
| 199 | + parent::__construct(); |
| 200 | + } |
| 201 | + |
| 202 | + function formatRow( $row ) { |
| 203 | + return $this->mForm->formatRow( $row ); |
| 204 | + } |
| 205 | + |
| 206 | + function getQueryInfo() { |
| 207 | + $conds = $this->mConds; |
| 208 | + |
| 209 | + $conds[] = 'af_id=afl_filter'; |
| 210 | + |
| 211 | + return array( |
| 212 | + 'tables' => array('abuse_filter_log','abuse_filter'), |
| 213 | + 'fields' => '*', |
| 214 | + 'conds' => $conds, |
| 215 | + ); |
| 216 | + } |
| 217 | + |
| 218 | + function getIndexField() { |
| 219 | + return 'afl_timestamp'; |
| 220 | + } |
| 221 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/SpecialAbuseLog.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 222 | + native |
Index: trunk/extensions/AbuseFilter/AbuseFilter.i18n.php |
— | — | @@ -0,0 +1,94 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for extension AbuseFilter. |
| 5 | + * |
| 6 | + * @addtogroup Extensions |
| 7 | + */ |
| 8 | + |
| 9 | +$messages = array(); |
| 10 | + |
| 11 | +/** English |
| 12 | + * @author Andrew Garrett |
| 13 | + */ |
| 14 | +$messages['en'] = array( |
| 15 | + // Hooks |
| 16 | + 'abusefilter-warning' => "<big>'''Warning'''</big>: This action has been automatically identified as harmful. Unconstructive edits will be quickly reverted, and egregious or repeated unconstructive editing will result in your account or computer being blocked. If you believe this edit to be constructive, you may click Submit again to confirm it.", |
| 17 | + 'abusefilter-disallowed' => "This action has been automatically identified as harmful, and therefore disallowed. If you believe your edit was constructive, please contact an administrator, and inform them of what you were trying to do.\n", |
| 18 | + 'abusefilter-blocked-display' => "This action has been automatically identified as harmful, and you have been prevented from executing it. In addition, to protect {{SITENAME}}, your user account and all associated IP addresses have been blocked from editing. If this has occurred in error, please contact an administrator.", |
| 19 | + 'abusefilter-degrouped' => "This action has been automatically identified as harmful. Consequently, it has been disallowed, and, since your account is suspected of being compromised, all rights have been revoked. If you believe this to have been in error, please contact a bureaucrat with an explanation of this action, and your rights may be restored.", |
| 20 | + 'abusefilter-autopromote-blocked' => "This action has been automatically identified as harmful, and it has been disallowed. In addition, as a security measure, some privileges routinely granted to established accounts have been temporarily revoked from your account.", |
| 21 | + 'abusefilter-blocker' => 'Abuse Filter', |
| 22 | + 'abusefilter-blockreason' => 'Automatically blocked for attempting to make edits identified as harmful.', |
| 23 | + |
| 24 | + // Permissions |
| 25 | + 'right-abusefilter-modify' => 'Modify abuse filters', |
| 26 | + 'right-abusefilter-view' => 'View abuse filters', |
| 27 | + 'right-abusefilter-log' => 'View the abuse log', |
| 28 | + 'right-abusefilter-log-detail' => 'View detailed abuse log entries', |
| 29 | + 'right-abusefilter-private' => 'View private data in the abuse log', |
| 30 | + |
| 31 | + // Abuse Log |
| 32 | + 'abusefilter-log' => 'Abuse Filter Log', |
| 33 | + 'abusefilter-log-search' => 'Search the abuse log', |
| 34 | + 'abusefilter-log-search-user' => 'User:', |
| 35 | + 'abusefilter-log-search-filter' => 'Filter ID:', |
| 36 | + 'abusefilter-log-search-title' => 'Title:', |
| 37 | + 'abusefilter-log-search-submit' => 'Search', |
| 38 | + 'abusefilter-log-entry' => '$1: $2 triggered an abuse filter, making a $3 on $4. Actions taken: $5; Filter description: $6', |
| 39 | + 'abusefilter-log-detailedentry' => '$1: $2 triggered filter $3, making a $4 on $5. Actions taken: $6; Filter description: $7 ($8)', |
| 40 | + 'abusefilter-log-detailslink' => 'details', |
| 41 | + 'abusefilter-log-details-legend' => 'Details for log entry $1', |
| 42 | + 'abusefilter-log-details-var' => 'Variable', |
| 43 | + 'abusefilter-log-details-val' => 'Value', |
| 44 | + 'abusefilter-log-details-vars' => 'Action parameters', |
| 45 | + 'abusefilter-log-details-private' => 'Private data', |
| 46 | + 'abusefilter-log-details-ip' => 'Originating IP address', |
| 47 | + 'abusefilter-log-noactions' => 'none', |
| 48 | + |
| 49 | + // Abuse filter management |
| 50 | + 'abusefilter-management' => 'Abuse Filter Management', |
| 51 | + 'abusefilter-list' => 'All filters', |
| 52 | + 'abusefilter-list-id' => 'Filter ID', |
| 53 | + 'abusefilter-list-status' => 'Status', |
| 54 | + 'abusefilter-list-public' => 'Public description', |
| 55 | + 'abusefilter-list-consequences' => 'Consequences', |
| 56 | + 'abusefilter-list-visibility' => 'Visibility', |
| 57 | + 'abusefilter-list-hitcount' => 'Hit count', |
| 58 | + 'abusefilter-list-edit' => 'Edit', |
| 59 | + 'abusefilter-list-details' => 'Details', |
| 60 | + 'abusefilter-hidden' => 'Private', |
| 61 | + 'abusefilter-unhidden' => 'Public', |
| 62 | + 'abusefilter-enabled' => 'Enabled', |
| 63 | + 'abusefilter-disabled' => 'Disabled', |
| 64 | + 'abusefilter-hitcount' => '$1 {{PLURAL:$1|hit|hits}}', |
| 65 | + |
| 66 | + // The edit screen |
| 67 | + 'abusefilter-edit-subtitle' => 'Editing filter $1', |
| 68 | + 'abusefilter-edit-save' => 'Save Filter', |
| 69 | + 'abusefilter-edit-id' => 'Filter ID:', |
| 70 | + 'abusefilter-edit-description' => "Description:\n:''(publicly viewable)''", |
| 71 | + 'abusefilter-edit-flags' => 'Flags:', |
| 72 | + 'abusefilter-edit-enabled' => 'Enable this filter', |
| 73 | + 'abusefilter-edit-hidden' => 'Hide details of this filter from public view', |
| 74 | + 'abusefilter-edit-rules' => 'Ruleset:', |
| 75 | + 'abusefilter-edit-notes' => "Notes:\n:''(private)", |
| 76 | + 'abusefilter-edit-lastmod' => 'Filter last modified:', |
| 77 | + 'abusefilter-edit-lastuser' => 'Last user to modify this filter:', |
| 78 | + 'abusefilter-edit-hitcount' => 'Filter hits:', |
| 79 | + 'abusefilter-edit-consequences' => 'Actions taken on hit', |
| 80 | + 'abusefilter-edit-action-warn' => 'Trigger these actions after giving the user a warning', |
| 81 | + 'abusefilter-edit-action-disallow' => 'Disallow the action', |
| 82 | + 'abusefilter-edit-action-flag' => 'Flag the edit in the abuse log', |
| 83 | + 'abusefilter-edit-action-blockautopromote' => "Revoke the users' autoconfirmed status", |
| 84 | + 'abusefilter-edit-action-degroup' => 'Remove all privileged groups from the user', |
| 85 | + 'abusefilter-edit-action-block' => 'Block the user from editing', |
| 86 | + 'abusefilter-edit-action-throttle' => 'Trigger actions only if the user trips a rate limit', |
| 87 | + 'abusefilter-edit-throttle-count' => 'Number of actions to allow:', |
| 88 | + 'abusefilter-edit-throttle-period' => 'Period of time:', |
| 89 | + 'abusefilter-edit-throttle-seconds' => '$1 seconds', |
| 90 | + 'abusefilter-edit-throttle-groups' => "Group throttle by:\n:''(one per line, combine with commas)''", |
| 91 | + 'abusefilter-edit-denied' => "You may not view details of this filter, because it is hidden from public view", |
| 92 | + 'abusefilter-edit-main' => 'Filter parameters', |
| 93 | + 'abusefilter-edit-done-subtitle' => 'Filter edited', |
| 94 | + 'abusefilter-edit-done' => "You have successfully saved your changes to the filter.\n\n[[Special:AbuseFilter|Return]]", |
| 95 | +); |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/AbuseFilter.i18n.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 96 | + native |
Index: trunk/extensions/AbuseFilter/testTitles.php |
— | — | @@ -0,0 +1,12 @@ |
| 2 | +<? |
| 3 | + |
| 4 | +require( '/home/andrew/mediawiki/maintenance/commandLine.inc' ); |
| 5 | + |
| 6 | +while ( ( $line = readconsole( '> ' ) ) !== false ) { |
| 7 | + $line = trim($line); |
| 8 | + |
| 9 | + print "Testing $line...\n"; |
| 10 | + $result = AbuseFilter::checkTitleText( $line ); |
| 11 | + print "-Result: "; |
| 12 | + var_dump( $result ); |
| 13 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/testTitles.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 14 | + native |
Index: trunk/extensions/AbuseFilter/AbuseFilter.php |
— | — | @@ -0,0 +1,49 @@ |
| 2 | +<?php |
| 3 | +if ( ! defined( 'MEDIAWIKI' ) ) |
| 4 | + die(); |
| 5 | + |
| 6 | +/**#@+ |
| 7 | + * Automatically applies heuristics to edits. |
| 8 | + * @addtogroup Extensions |
| 9 | + * |
| 10 | + * @link http://www.mediawiki.org/wiki/Extension:AbuseFilter Documentation |
| 11 | + * |
| 12 | + * |
| 13 | + * @author Andrew Garrett <andrew@epstone.net> |
| 14 | + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
| 15 | + */ |
| 16 | + |
| 17 | +$dir = dirname(__FILE__); |
| 18 | +$wgExtensionCredits['other'][] = array( |
| 19 | + 'name' => 'Abuse Filter', |
| 20 | + 'author' => 'Andrew Garrett', |
| 21 | + 'svn-date' => '$LastChangedDate: 2008-06-08 20:48:19 +1000 (Sun, 08 Jun 2008) $', |
| 22 | + 'svn-revision' => '$LastChangedRevision: 36018 $', |
| 23 | + 'description' => 'Applies automatic heuristics to edits.', |
| 24 | + 'descriptionmsg' => 'abusefilter-desc', |
| 25 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:AbuseFilter', |
| 26 | +); |
| 27 | + |
| 28 | +$wgExtensionMessagesFiles['AbuseFilter'] = "$dir/AbuseFilter.i18n.php"; |
| 29 | + |
| 30 | +$wgAutoloadClasses[ 'AbuseFilter' ] = "$dir/AbuseFilter.class.php"; |
| 31 | +$wgAutoloadClasses[ 'AbuseFilterHooks' ] = "$dir/AbuseFilter.hooks.php"; |
| 32 | +$wgAutoloadClasses['SpecialAbuseLog'] = "$dir/SpecialAbuseLog.php"; |
| 33 | +$wgAutoloadClasses['SpecialAbuseFilter'] = "$dir/SpecialAbuseFilter.php"; |
| 34 | + |
| 35 | +$wgSpecialPages['AbuseLog'] = 'SpecialAbuseLog'; |
| 36 | +$wgSpecialPages['AbuseFilter'] = 'SpecialAbuseFilter'; |
| 37 | + |
| 38 | +$wgHooks['EditFilter'][] = 'AbuseFilterHooks::onEditFilter'; |
| 39 | +$wgHooks['GetAutoPromoteGroups'][] = 'AbuseFilterHooks::onGetAutoPromoteGroups'; |
| 40 | +$wgHooks['AbortMove'][] = 'AbuseFilterHooks::onAbortMove'; |
| 41 | +$wgHooks['AbortNewAccount'][] = 'AbuseFilterHooks::onAbortNewAccount'; |
| 42 | +$wgHooks['ArticleDelete'][] = 'AbuseFilterHooks::onArticleDelete'; |
| 43 | + |
| 44 | +$wgAvailableRights[] = 'abusefilter-modify'; |
| 45 | +$wgAvailableRights[] = 'abusefilter-log-detail'; |
| 46 | +$wgAvailableRights[] = 'abusefilter-view'; |
| 47 | +$wgAvailableRights[] = 'abusefilter-log'; |
| 48 | +$wgAvailableRights[] = 'abusefilter-private'; |
| 49 | + |
| 50 | +$wgAbuseFilterAvailableActions = array( 'flag', 'throttle', 'warn', 'disallow', 'blockautopromote', 'block', 'degroup' ); |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/AbuseFilter.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 51 | + native |
Index: trunk/extensions/AbuseFilter/SpecialAbuseFilter.php |
— | — | @@ -0,0 +1,355 @@ |
| 2 | +<?php |
| 3 | +if ( ! defined( 'MEDIAWIKI' ) ) |
| 4 | + die(); |
| 5 | + |
| 6 | +class SpecialAbuseFilter extends SpecialPage { |
| 7 | + |
| 8 | + var $mSkin; |
| 9 | + |
| 10 | + function __construct() { |
| 11 | + wfLoadExtensionMessages('AbuseFilter'); |
| 12 | + parent::__construct( 'AbuseFilter', 'abusefilter-view' ); |
| 13 | + } |
| 14 | + |
| 15 | + function execute( $subpage ) { |
| 16 | + global $wgUser,$wgOut,$wgRequest; |
| 17 | + |
| 18 | + $this->setHeaders(); |
| 19 | + |
| 20 | + $this->loadParameters( $subpage ); |
| 21 | + $wgOut->setPageTitle( wfMsg( 'abusefilter-management' ) ); |
| 22 | + $wgOut->setRobotpolicy( "noindex,nofollow" ); |
| 23 | + $wgOut->setArticleRelated( false ); |
| 24 | + $wgOut->enableClientCache( false ); |
| 25 | + |
| 26 | + // Are we allowed? |
| 27 | + if ( count( $errors = $this->getTitle()->getUserPermissionsErrors( 'abusefilter-view', $wgUser, true, array( 'ns-specialprotected' ) ) ) ) { |
| 28 | + // Go away. |
| 29 | + $wgOut->showPermissionsErrorPage( $errors, 'abusefilter-view' ); |
| 30 | + return; |
| 31 | + } |
| 32 | + |
| 33 | + $this->mSkin = $wgUser->getSkin(); |
| 34 | + |
| 35 | + if ($output = $this->doEdit( )) { |
| 36 | + $wgOut->addHtml( $output ); |
| 37 | + } else { |
| 38 | + // Show list of filters. |
| 39 | + $this->showList(); |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + function doEdit() { |
| 44 | + global $wgRequest, $wgUser; |
| 45 | + |
| 46 | + $filter = $this->mFilter; |
| 47 | + |
| 48 | + $editToken = $wgRequest->getVal( 'wpEditToken' ); |
| 49 | + $didEdit = $filter && $this->canEdit() && $wgUser->matchEditToken( $editToken, array( 'abusefilter', $filter ) ); |
| 50 | + |
| 51 | + if ($didEdit) { |
| 52 | + $dbw = wfGetDB( DB_MASTER ); |
| 53 | + $newRow = array(); |
| 54 | + |
| 55 | + // Load the toggles which go straight into the DB |
| 56 | + $dbToggles = array( 'enabled', 'hidden' ); |
| 57 | + foreach( $dbToggles as $toggle ) { |
| 58 | + $newRow['af_'.$toggle] = $wgRequest->getBool( 'wpFilter'.ucfirst($toggle) ); |
| 59 | + } |
| 60 | + |
| 61 | + // Text which can go straight into the DB |
| 62 | + $dbText = array( 'wpFilterDescription' => 'af_public_comments', 'wpFilterRules' => 'af_pattern', 'wpFilterNotes' => 'af_comments' ); |
| 63 | + foreach( $dbText as $key => $value ) { |
| 64 | + $newRow[$value] = trim($wgRequest->getText( $key )); |
| 65 | + } |
| 66 | + |
| 67 | + // Last modified details |
| 68 | + $newRow['af_timestamp'] = $dbw->timestamp( wfTimestampNow() ); |
| 69 | + $newRow['af_user'] = $wgUser->getId(); |
| 70 | + $newRow['af_user_text'] = $wgUser->getName(); |
| 71 | + |
| 72 | + // Actions |
| 73 | + global $wgAbuseFilterAvailableActions; |
| 74 | + $enabledActions = array(); |
| 75 | + $deadActions = array(); |
| 76 | + $actionsRows = array(); |
| 77 | + foreach( $wgAbuseFilterAvailableActions as $action ) { |
| 78 | + // Check if it's set |
| 79 | + $enabled = $wgRequest->getBool( 'wpFilterAction'.ucfirst($action) ); |
| 80 | + |
| 81 | + if (!$enabled) { |
| 82 | + $deadActions[] = $action; |
| 83 | + } |
| 84 | + |
| 85 | + if ($enabled) { |
| 86 | + $parameters = array(); |
| 87 | + |
| 88 | + if ($action == 'throttle') { |
| 89 | + // Grumble grumble. |
| 90 | + // We need to load the parameters |
| 91 | + $throttleCount = $wgRequest->getIntOrNull( 'wpFilterThrottleCount' ); |
| 92 | + $throttlePeriod = $wgRequest->getIntOrNull( 'wpFilterThrottlePeriod' ); |
| 93 | + $throttleGroups = explode("\n", trim( $wgRequest->getText( 'wpFilterThrottleGroups' ) ) ); |
| 94 | + |
| 95 | + $parameters[0] = $filter; // For now, anyway |
| 96 | + $parameters[1] = "$throttleCount,$throttlePeriod"; |
| 97 | + $parameters = array_merge( $parameters, $throttleGroups ); |
| 98 | + } |
| 99 | + |
| 100 | + $thisRow = array( 'afa_filter' => $filter, 'afa_consequence' => $action, 'afa_parameters' => implode( "\n", $parameters ) ); |
| 101 | + $actionsRows[] = $thisRow; |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + // Do the update |
| 106 | + |
| 107 | + $dbw->begin(); |
| 108 | + $dbw->update( 'abuse_filter', $newRow, array( 'af_id' => $filter ), __METHOD__ ); |
| 109 | + $dbw->delete( 'abuse_filter_action', array( 'afa_filter' => $filter, 'afa_consequence' => $deadActions ), __METHOD__ ); |
| 110 | + $dbw->replace( 'abuse_filter_action', array( array( 'afa_filter', 'afa_consequence' ) ), $actionsRows, __METHOD__ ); |
| 111 | + $dbw->commit(); |
| 112 | + |
| 113 | + global $wgOut; |
| 114 | + |
| 115 | + $wgOut->setSubtitle( wfMsg('abusefilter-edit-done-subtitle' ) ); |
| 116 | + return wfMsgExt( 'abusefilter-edit-done', array( 'parse' ) ); |
| 117 | + } else { |
| 118 | + return $this->buildFilterEditor(); |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + function buildFilterEditor( ) { |
| 123 | + if (!is_numeric($this->mFilter) || $this->mFilter<=0) { |
| 124 | + return false; |
| 125 | + } |
| 126 | + |
| 127 | + // Build the edit form |
| 128 | + global $wgOut,$wgLang,$wgUser; |
| 129 | + $sk = $this->mSkin; |
| 130 | + $wgOut->setSubtitle( wfMsg( 'abusefilter-edit-subtitle', $this->mFilter ) ); |
| 131 | + |
| 132 | + list ($row, $actions) = $this->loadFilterData(); |
| 133 | + |
| 134 | + if ($row->af_hidden && !$this->canEdit()) { |
| 135 | + return wfMsg( 'abusefilter-edit-hidden' ); |
| 136 | + } |
| 137 | + |
| 138 | + $output = ''; |
| 139 | + $fields = array(); |
| 140 | + |
| 141 | + $fields['abusefilter-edit-id'] = $this->mFilter; |
| 142 | + $fields['abusefilter-edit-description'] = Xml::input( 'wpFilterDescription', 20, $row->af_public_comments ); |
| 143 | + |
| 144 | + // Hit count display |
| 145 | + $count = $row->af_hit_count; |
| 146 | + $count_display = wfMsgExt( 'abusefilter-hitcount', array( 'parseinline' ), array( $count ) ); |
| 147 | + $hitCount = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'AbuseLog' ), $count_display, 'wpSearchFilter='.$row->af_id ); |
| 148 | + |
| 149 | + $fields['abusefilter-edit-hitcount'] = $hitCount; |
| 150 | + |
| 151 | + $fields['abusefilter-edit-rules'] = Xml::textarea( 'wpFilterRules', $row->af_pattern . "\n" ); |
| 152 | + $fields['abusefilter-edit-notes'] = Xml::textarea( 'wpFilterNotes', $row->af_comments ."\n" ); |
| 153 | + |
| 154 | + |
| 155 | + // Build checkboxen |
| 156 | + $checkboxes = array( 'hidden', 'enabled' ); |
| 157 | + $flags = ''; |
| 158 | + foreach( $checkboxes as $checkboxId ) { |
| 159 | + $message = "abusefilter-edit-$checkboxId"; |
| 160 | + $dbField = "af_$checkboxId"; |
| 161 | + $postVar = "wpFilter".ucfirst($checkboxId); |
| 162 | + |
| 163 | + $checkbox = Xml::checkLabel( wfMsg( $message ), $postVar, $postVar, $row->$dbField ); |
| 164 | + $checkbox = Xml::tags( 'p', null, $checkbox ); |
| 165 | + $flags .= $checkbox; |
| 166 | + } |
| 167 | + $fields['abusefilter-edit-flags'] = $flags; |
| 168 | + |
| 169 | + // Last modification details |
| 170 | + $fields['abusefilter-edit-lastmod'] = $wgLang->timeanddate( $row->af_timestamp ); |
| 171 | + $fields['abusefilter-edit-lastuser'] = $sk->userLink( $row->af_user, $row->af_user_text ) . $sk->userToolLinks( $row->af_user, $row->af_user_text ); |
| 172 | + |
| 173 | + $form = Xml::buildForm( $fields ); |
| 174 | + $form = Xml::fieldset( wfMsg( 'abusefilter-edit-main' ), $form ); |
| 175 | + $form .= Xml::fieldset( wfMsg( 'abusefilter-edit-consequences' ), $this->buildConsequenceEditor( $row, $actions ) ); |
| 176 | + |
| 177 | + if ($this->canEdit()) { |
| 178 | + $form .= Xml::submitButton( wfMsg( 'abusefilter-edit-save' ) ); |
| 179 | + $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( array( 'abusefilter', $this->mFilter )) ); |
| 180 | + } |
| 181 | + |
| 182 | + $form = Xml::tags( 'form', array( 'action' => $this->getTitle( $this->mFilter )->getFullURL(), 'method' => 'POST' ), $form ); |
| 183 | + |
| 184 | + $output .= $form; |
| 185 | + |
| 186 | + return $output; |
| 187 | + } |
| 188 | + |
| 189 | + function buildConsequenceEditor( $row, $actions ) { |
| 190 | + global $wgAbuseFilterAvailableActions; |
| 191 | + $setActions = array(); |
| 192 | + foreach( $wgAbuseFilterAvailableActions as $action ) { |
| 193 | + $setActions[$action] = in_array( $action, array_keys( $actions ) ); |
| 194 | + } |
| 195 | + |
| 196 | + $output = ''; |
| 197 | + |
| 198 | + // Special case: flagging - always on. |
| 199 | + $checkbox = Xml::checkLabel( wfMsg( 'abusefilter-edit-action-flag' ), 'wpFilterActionFlag', 'wpFilterActionFlag', true, array( 'disabled' => '1' ) ); |
| 200 | + $output .= Xml::tags( 'p', null, $checkbox ); |
| 201 | + |
| 202 | + // Special case: throttling |
| 203 | + $throttleSettings = Xml::checkLabel( wfMsg( 'abusefilter-edit-action-throttle' ), 'wpFilterActionThrottle', 'wpFilterActionThrottle', $setActions['throttle'] ); |
| 204 | + $throttleFields = array(); |
| 205 | + |
| 206 | + if ($setActions['throttle']) { |
| 207 | + $throttleRate = explode(',',$actions['throttle']['parameters'][0]); |
| 208 | + $throttleCount = $throttleRate[0]; |
| 209 | + $throttlePeriod = $throttleRate[1]; |
| 210 | + |
| 211 | + $throttleGroups = implode("\n", array_slice($actions['throttle']['parameters'], 1 ) ); |
| 212 | + } else { |
| 213 | + $throttleCount = 3; |
| 214 | + $throttlePeriod = 60; |
| 215 | + |
| 216 | + $throttleGroups = "user\n"; |
| 217 | + } |
| 218 | + |
| 219 | + $throttleFields['abusefilter-edit-throttle-count'] = Xml::input( 'wpFilterThrottleCount', 20, $throttleCount ); |
| 220 | + $throttleFields['abusefilter-edit-throttle-period'] = wfMsgExt( 'abusefilter-edit-throttle-seconds', array( 'parseinline', 'replaceafter' ), array(Xml::input( 'wpFilterThrottlePeriod', 20, $throttlePeriod ) ) ); |
| 221 | + $throttleFields['abusefilter-edit-throttle-groups'] = Xml::textarea( 'wpFilterThrottleGroups', $throttleGroups."\n" ); |
| 222 | + $throttleSettings .= Xml::buildForm( $throttleFields ); |
| 223 | + $output .= Xml::tags( 'p', null, $throttleSettings ); |
| 224 | + |
| 225 | + // The remainder are just toggles |
| 226 | + $remainingActions = array_diff( $wgAbuseFilterAvailableActions, array( 'flag', 'throttle' ) ); |
| 227 | + |
| 228 | + foreach( $remainingActions as $action ) { |
| 229 | + $message = 'abusefilter-edit-action-'.$action; |
| 230 | + $form_field = 'wpFilterAction' . ucfirst($action); |
| 231 | + $status = $setActions[$action]; |
| 232 | + |
| 233 | + $thisAction = Xml::checkLabel( wfMsg( $message ), $form_field, $form_field, $status ); |
| 234 | + $thisAction = Xml::tags( 'p', null, $thisAction ); |
| 235 | + |
| 236 | + $output .= $thisAction; |
| 237 | + } |
| 238 | + |
| 239 | + return $output; |
| 240 | + } |
| 241 | + |
| 242 | + function loadFilterData() { |
| 243 | + $id = $this->mFilter; |
| 244 | + |
| 245 | + $dbr = wfGetDB( DB_SLAVE ); |
| 246 | + |
| 247 | + // Load the main row |
| 248 | + $row = $dbr->selectRow( 'abuse_filter', '*', array( 'af_id' => $id ), __METHOD__ ); |
| 249 | + |
| 250 | + // Load the actions |
| 251 | + $actions = array(); |
| 252 | + $res = $dbr->select( 'abuse_filter_action', '*', array( 'afa_filter' => $id), __METHOD__ ); |
| 253 | + while ( $actionRow = $dbr->fetchObject( $res ) ) { |
| 254 | + $thisAction = array(); |
| 255 | + $thisAction['action'] = $actionRow->afa_consequence; |
| 256 | + $thisAction['parameters'] = explode( "\n", $actionRow->afa_parameters ); |
| 257 | + |
| 258 | + $actions[$actionRow->afa_consequence] = $thisAction; |
| 259 | + } |
| 260 | + |
| 261 | + return array( $row, $actions ); |
| 262 | + } |
| 263 | + |
| 264 | + function loadParameters( $subpage ) { |
| 265 | + global $wgRequest,$wgUser; |
| 266 | + |
| 267 | + $filter = $subpage; |
| 268 | + |
| 269 | + if (!is_numeric($filter)) { |
| 270 | + $filter = $wgRequest->getIntOrNull( 'wpFilter' ); |
| 271 | + |
| 272 | + if (!$filter) { |
| 273 | + return; |
| 274 | + } |
| 275 | + } |
| 276 | + $this->mFilter = $filter; |
| 277 | + } |
| 278 | + |
| 279 | + function canEdit() { |
| 280 | + global $wgUser; |
| 281 | + static $canEdit = 'unset'; |
| 282 | + |
| 283 | + if ($canEdit == 'unset') { |
| 284 | + $canEdit = !count( $errors = $this->getTitle()->getUserPermissionsErrors( 'abusefilter-modify', $wgUser, true, array( 'ns-specialprotected' ) ) ); |
| 285 | + } |
| 286 | + |
| 287 | + return $canEdit; |
| 288 | + } |
| 289 | + |
| 290 | + function showList() { |
| 291 | + global $wgOut,$wgUser;; |
| 292 | + |
| 293 | + $this->mSkin = $wgUser->getSkin(); |
| 294 | + |
| 295 | + $output = ''; |
| 296 | + |
| 297 | + $output .= Xml::element( 'h2', null, wfMsgExt( 'abusefilter-list', array( 'parseinline' ) ) ); |
| 298 | + |
| 299 | + // We shouldn't have more than 100 filters, so don't bother paging. |
| 300 | + $dbr = wfGetDB( DB_SLAVE ); |
| 301 | + $res = $dbr->select( array('abuse_filter','abuse_filter_action'), '*,group_concat(afa_consequence) AS consequences', array( 'afa_filter=af_id' ), __METHOD__, array( 'LIMIT' => 100, 'GROUP BY' => 'af_id' ) ); |
| 302 | + $list = ''; |
| 303 | + $editLabel = $this->canEdit() ? 'abusefilter-list-edit' : 'abusefilter-list-details'; |
| 304 | + |
| 305 | + // Build in a table |
| 306 | + $headers = array( 'abusefilter-list-id', 'abusefilter-list-public', 'abusefilter-list-consequences', 'abusefilter-list-status', 'abusefilter-list-visibility', 'abusefilter-list-hitcount', $editLabel ); |
| 307 | + $header_row = ''; |
| 308 | + foreach( $headers as $header ) { |
| 309 | + $header_row .= Xml::element( 'th', null, wfMsgExt( $header, array( 'parseinline' ) ) ); |
| 310 | + } |
| 311 | + |
| 312 | + $list .= Xml::tags( 'tr', null, $header_row ); |
| 313 | + |
| 314 | + while ($row = $dbr->fetchObject( $res ) ) { |
| 315 | + $list .= $this->shortFormatFilter( $row ); |
| 316 | + } |
| 317 | + |
| 318 | + $output .= Xml::tags( 'table', array( 'class' => 'wikitable' ), Xml::tags( 'tbody', null, $list ) ); |
| 319 | + |
| 320 | + $wgOut->addHTML( $output ); |
| 321 | + } |
| 322 | + |
| 323 | + function shortFormatFilter ( $row ) { |
| 324 | + global $wgOut; |
| 325 | + |
| 326 | + $sk = $this->mSkin; |
| 327 | + |
| 328 | + $editLabel = $this->canEdit() ? 'abusefilter-list-edit' : 'abusefilter-list-details'; |
| 329 | + |
| 330 | + // Build a table row |
| 331 | + $trow = ''; |
| 332 | + |
| 333 | + $status = $row->af_enabled ? 'abusefilter-enabled' : 'abusefilter-disabled'; |
| 334 | + $status = wfMsgExt( $status, array( 'parseinline' ) ); |
| 335 | + |
| 336 | + $visibility = $row->af_hidden ? 'abusefilter-hidden' : 'abusefilter-unhidden'; |
| 337 | + $visibility = wfMsgExt( $visibility, array( 'parseinline' ) ); |
| 338 | + |
| 339 | + // Hit count |
| 340 | + $count = $row->af_hit_count; |
| 341 | + $count_display = wfMsgExt( 'abusefilter-hitcount', array( 'parseinline' ), array( $count ) ); |
| 342 | + $hitCount = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'AbuseLog' ), $count_display, 'wpSearchFilter='.$row->af_id ); |
| 343 | + |
| 344 | + $editLink = $sk->makeKnownLinkObj( $this->getTitle( $row->af_id ), wfMsg( $editLabel ) ); |
| 345 | + |
| 346 | + $values = array( $row->af_id, $wgOut->parse($row->af_public_comments), wfEscapeWikitext($row->consequences), $status, $visibility, $hitCount, $editLink ); |
| 347 | + |
| 348 | + foreach( $values as $value ) { |
| 349 | + $trow .= Xml::tags( 'td', null, $value ); |
| 350 | + } |
| 351 | + |
| 352 | + $trow = Xml::tags( 'tr', null, $trow ); |
| 353 | + |
| 354 | + return $trow; |
| 355 | + } |
| 356 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/SpecialAbuseFilter.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 357 | + native |
Index: trunk/extensions/AbuseFilter/abusefilter.tables.sql |
— | — | @@ -0,0 +1,46 @@ |
| 2 | +-- SQL tables for AbuseFilter extension |
| 3 | + |
| 4 | +CREATE TABLE /*$wgDBprefix*/abuse_filter ( |
| 5 | + af_id BIGINT unsigned NOT NULL AUTO_INCREMENT, |
| 6 | + af_pattern BLOB NOT NULL, |
| 7 | + af_user BIGINT unsigned NOT NULL, |
| 8 | + af_user_text varchar(255) binary NOT NULL, |
| 9 | + af_timestamp binary(14) NOT NULL, |
| 10 | + af_enabled tinyint(1) not null default 1, |
| 11 | + af_comments BLOB, |
| 12 | + af_public_comments TINYBLOB, |
| 13 | + af_hidden tinyint(1) not null default 0, |
| 14 | + af_hit_count bigint not null default 0, |
| 15 | + |
| 16 | + PRIMARY KEY (af_id), |
| 17 | + KEY (af_user) |
| 18 | +) /*$wgDBTableOptions*/; |
| 19 | + |
| 20 | +CREATE TABLE /*$wgDBprefix*/abuse_filter_action ( |
| 21 | + afa_filter BIGINT unsigned NOT NULL, |
| 22 | + afa_consequence varchar(255) NOT NULL, |
| 23 | + afa_parameters TINYBLOB NOT NULL, |
| 24 | + |
| 25 | + PRIMARY KEY (afa_filter), |
| 26 | + KEY (afa_consequence) |
| 27 | +) /*$wgDBTableOptions*/; |
| 28 | + |
| 29 | +CREATE TABLE /*$wgDBprefix*/abuse_filter_log ( |
| 30 | + afl_id BIGINT unsigned NOT NULL AUTO_INCREMENT, |
| 31 | + afl_filter BIGINT unsigned NOT NULL, |
| 32 | + afl_user BIGINT unsigned NOT NULL, |
| 33 | + afl_user_text varchar(255) binary NOT NULL, |
| 34 | + afl_ip varchar(255) not null, |
| 35 | + afl_action varbinary(255) not null, |
| 36 | + afl_var_dump BLOB NOT NULL, |
| 37 | + afl_timestamp binary(14) NOT NULL, |
| 38 | + afl_namespace tinyint NOT NULL, |
| 39 | + afl_title varchar(255) binary NOT NULL, |
| 40 | + |
| 41 | + PRIMARY KEY (afl_id), |
| 42 | + KEY (afl_filter), |
| 43 | + KEY (afl_user), |
| 44 | + KEY (afl_timestamp), |
| 45 | + KEY (afl_namespace, afl_title) |
| 46 | + KEY (afl_ip), |
| 47 | +) /*$wgDBTableOptions*/; |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/abusefilter.tables.sql |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 48 | + native |
Index: trunk/extensions/AbuseFilter/AbuseFilter.hooks.php |
— | — | @@ -0,0 +1,84 @@ |
| 2 | +<?php |
| 3 | +if ( ! defined( 'MEDIAWIKI' ) ) |
| 4 | + die(); |
| 5 | + |
| 6 | +class AbuseFilterHooks { |
| 7 | + |
| 8 | + public static function onEditFilter($editor, $text, $section, &$error, $summary) { |
| 9 | + // Load vars |
| 10 | + $vars = array(); |
| 11 | + |
| 12 | + global $wgUser; |
| 13 | + $vars = array_merge( $vars, AbuseFilter::generateUserVars( $wgUser ) ); |
| 14 | + $vars = array_merge( $vars, AbuseFilter::generateTitleVars( $editor->mTitle , 'ARTICLE' )); |
| 15 | + $vars['ACTION'] = 'edit'; |
| 16 | + $vars['SUMMARY'] = $summary; |
| 17 | + |
| 18 | + // TODO: |
| 19 | +// // Include added/removed lines in the vars. |
| 20 | + |
| 21 | + $filter_result = AbuseFilter::filterAction( $vars, $editor->mTitle ); |
| 22 | + |
| 23 | + $error = $filter_result; |
| 24 | + |
| 25 | + return true; |
| 26 | + } |
| 27 | + |
| 28 | + public static function onGetAutoPromoteGroups( $user, &$promote ) { |
| 29 | + global $wgMemc; |
| 30 | + |
| 31 | + $key = AbuseFilter::autoPromoteBlockKey( $user ); |
| 32 | + |
| 33 | + if ($wgMemc->get( $key ) ) { |
| 34 | + $promote = array(); |
| 35 | + } |
| 36 | + |
| 37 | + return true; |
| 38 | + } |
| 39 | + |
| 40 | + function onAbortMove( $oldTitle, $newTitle, $user, &$error, $reason ) { |
| 41 | + $vars = array(); |
| 42 | + |
| 43 | + global $wgUser; |
| 44 | + $vars = array_merge( $vars, AbuseFilter::generateUserVars( $wgUser ), |
| 45 | + AbuseFilter::generateTitleVars( $oldTitle, 'MOVED_FROM' ), |
| 46 | + AbuseFilter::generateTitleVars( $newTitle, 'MOVED_TO' ) ); |
| 47 | + $vars['SUMMARY'] = $reason; |
| 48 | + $vars['ACTION'] = 'move'; |
| 49 | + |
| 50 | + $filter_result = AbuseFilter::filterAction( $vars, $oldTitle ); |
| 51 | + |
| 52 | + $error = "BLAH\n$filter_result"; |
| 53 | + |
| 54 | + return $filter_result == ''; |
| 55 | + } |
| 56 | + |
| 57 | + function onArticleDelete( &$article, &$user, &$reason, &$error ) { |
| 58 | + $vars = array(); |
| 59 | + |
| 60 | + global $wgUser; |
| 61 | + $vars = array_merge( $vars, AbuseFilter::generateUserVars( $wgUser ), |
| 62 | + AbuseFilter::generateTitleVars( $article->mTitle, 'ARTICLE' ) ); |
| 63 | + $vars['SUMMARY'] = $reason; |
| 64 | + $vars['ACTION'] = 'delete'; |
| 65 | + |
| 66 | + $filter_result = AbuseFilter::filterAction( $vars, $user ); |
| 67 | + |
| 68 | + $error = "BLAH\n$filter_result"; |
| 69 | + |
| 70 | + return $filter_result == ''; |
| 71 | + } |
| 72 | + |
| 73 | + function onAbortNewAccount( $username, &$message ) { |
| 74 | + $vars = array(); |
| 75 | + |
| 76 | + $vars['ACTION'] = 'createaccount'; |
| 77 | + $vars['ACCOUNTNAME'] = $username; |
| 78 | + |
| 79 | + $filter_result = AbuseFilter::filterAction( $vars, $user ); |
| 80 | + |
| 81 | + $error = "BLAH\n$filter_result"; |
| 82 | + |
| 83 | + return $filter_result == ''; |
| 84 | + } |
| 85 | +} |
Property changes on: trunk/extensions/AbuseFilter/AbuseFilter.hooks.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 86 | + native |