Index: trunk/extensions/AbuseFilter/edit.js |
— | — | @@ -0,0 +1,47 @@ |
| 2 | +function doSyntaxCheck() |
| 3 | +{ |
| 4 | + var filter = document.getElementById('wpFilterRules').value; |
| 5 | + injectSpinner( document.getElementById( 'mw-abusefilter-syntaxcheck' ), 'abusefilter-syntaxcheck' ); |
| 6 | + sajax_do_call( 'AbuseFilter::ajaxCheckSyntax', [filter], processSyntaxResult ); |
| 7 | +} |
| 8 | +function processSyntaxResult( request ) { |
| 9 | + var response = request.responseText; |
| 10 | + |
| 11 | + removeSpinner( 'abusefilter-syntaxcheck' ); |
| 12 | + |
| 13 | + if (response.match( /OK/ )) { |
| 14 | + // Successful |
| 15 | + jsMsg( 'No syntax errors.', 'mw-abusefilter-syntaxresult' ); |
| 16 | + } else { |
| 17 | + var error = response.substr(4); |
| 18 | + jsMsg( 'Syntax error: '+error, 'mw-abusefilter-syntaxresult' ); |
| 19 | + } |
| 20 | +} |
| 21 | +function addText() { |
| 22 | + if (document.getElementById('wpFilterBuilder').selectedIndex == 0) { |
| 23 | + return; |
| 24 | + } |
| 25 | + |
| 26 | + insertAtCursor(document.getElementById('wpFilterRules'), document.getElementById('wpFilterBuilder').value); |
| 27 | + document.getElementById('wpFilterBuilder').selectedIndex = 0; |
| 28 | +} |
| 29 | + |
| 30 | +//From http://clipmarks.com/clipmark/CEFC94CB-94D6-4495-A7AA-791B7355E284/ |
| 31 | +function insertAtCursor(myField, myValue) { |
| 32 | + //IE support |
| 33 | + if (document.selection) { |
| 34 | + myField.focus(); |
| 35 | + sel = document.selection.createRange(); |
| 36 | + sel.text = myValue; |
| 37 | + } |
| 38 | + //MOZILLA/NETSCAPE support |
| 39 | + else if (myField.selectionStart || myField.selectionStart == '0') { |
| 40 | + var startPos = myField.selectionStart; |
| 41 | + var endPos = myField.selectionEnd; |
| 42 | + myField.value = myField.value.substring(0, startPos) |
| 43 | + + myValue |
| 44 | + + myField.value.substring(endPos, myField.value.length); |
| 45 | + } else { |
| 46 | + myField.value += myValue; |
| 47 | + } |
| 48 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/AbuseFilter/edit.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 49 | + native |
Index: trunk/extensions/AbuseFilter/parser_native/aftypes.cpp |
— | — | @@ -201,6 +201,13 @@ |
202 | 202 | } |
203 | 203 | }; |
204 | 204 | |
| 205 | +template<typename T> |
| 206 | +struct afppower { |
| 207 | + T operator() (T const &a, T const &b) const { |
| 208 | + return std::pow(a,b); |
| 209 | + } |
| 210 | +}; |
| 211 | + |
205 | 212 | /* |
206 | 213 | * A visitor that performs an arithmetic operation on its arguments, |
207 | 214 | * after doing appropriate int->double promotion. |
— | — | @@ -343,6 +350,13 @@ |
344 | 351 | return datum(a) %= b; |
345 | 352 | } |
346 | 353 | |
| 354 | +datum |
| 355 | +pow(datum const &a, datum const &b) { |
| 356 | + datum result = datum(pow(a.toFloat(),b.toFloat())); |
| 357 | + |
| 358 | + return result; |
| 359 | +} |
| 360 | + |
347 | 361 | bool |
348 | 362 | operator==(datum const &a, datum const &b) { |
349 | 363 | return a.compare(b); |
Index: trunk/extensions/AbuseFilter/parser_native/aftypes.h |
— | — | @@ -110,6 +110,8 @@ |
111 | 111 | bool operator<=(datum const &a, datum const &b); |
112 | 112 | bool operator>=(datum const &a, datum const &b); |
113 | 113 | |
| 114 | +datum pow(datum const &a, datum const &b); |
| 115 | + |
114 | 116 | template<typename char_type, typename traits> |
115 | 117 | std::basic_ostream<char_type, traits> & |
116 | 118 | operator<<(std::basic_ostream<char_type, traits> &s, datum const &d) { |
Index: trunk/extensions/AbuseFilter/parser_native/parser.cpp |
— | — | @@ -166,6 +166,7 @@ |
167 | 167 | '*' >> in_expr[mult_expr.val *= arg1] |
168 | 168 | | '/' >> in_expr[mult_expr.val /= arg1] |
169 | 169 | | '%' >> in_expr[mult_expr.val %= arg1] |
| 170 | + | "**" >> in_expr[mult_expr.val = bind(&afp::pow)(mult_expr.val,arg1)] |
170 | 171 | ) |
171 | 172 | ; |
172 | 173 | |
Index: trunk/extensions/AbuseFilter/AbuseFilter.i18n.php |
— | — | @@ -139,6 +139,69 @@ |
140 | 140 | 'abusefilter-edit-history' => 'History', |
141 | 141 | 'abusefilter-edit-check' => 'Check syntax', |
142 | 142 | |
| 143 | + // Filter editing helpers |
| 144 | + 'abusefilter-edit-builder-select' => 'Select an option to add it at the cursor', |
| 145 | + 'abusefilter-edit-builder-group-op-arithmetic' => 'Arithmetic operators', |
| 146 | + 'abusefilter-edit-builder-op-arithmetic-addition' => 'Addition (+)', |
| 147 | + 'abusefilter-edit-builder-op-arithmetic-subtraction' => 'Subtraction (-)', |
| 148 | + 'abusefilter-edit-builder-op-arithmetic-multiplication' => 'Multiplication (*)', |
| 149 | + 'abusefilter-edit-builder-op-arithmetic-divide' => 'Division (/)', |
| 150 | + 'abusefilter-edit-builder-op-arithmetic-modulo' => 'Modulo (%)', |
| 151 | + 'abusefilter-edit-builder-op-arithmetic-pow' => 'Power (**)', |
| 152 | + 'abusefilter-edit-builder-group-op-comparison' => 'Comparison operators', |
| 153 | + 'abusefilter-edit-builder-op-comparison-equal' => 'Equal to (==)', |
| 154 | + 'abusefilter-edit-builder-op-comparison-notequal' => 'Not equal to (!=)', |
| 155 | + 'abusefilter-edit-builder-op-comparison-lt' => 'Less than (<)', |
| 156 | + 'abusefilter-edit-builder-op-comparison-gt' => 'Greater than (>)', |
| 157 | + 'abusefilter-edit-builder-op-comparison-lte' => 'Less than or equal to (<=)', |
| 158 | + 'abusefilter-edit-builder-op-comparison-gte' => 'Greater than or equal to (>=)', |
| 159 | + 'abusefilter-edit-builder-group-op-bool' => 'Boolean operators', |
| 160 | + 'abusefilter-edit-builder-op-bool-not' => 'Not (!)', |
| 161 | + 'abusefilter-edit-builder-op-bool-and' => 'And (&)', |
| 162 | + 'abusefilter-edit-builder-op-bool-or' => 'Or (|)', |
| 163 | + 'abusefilter-edit-builder-op-bool-xor' => 'XOR (^)', |
| 164 | + 'abusefilter-edit-builder-group-misc' => 'Miscellaneous', |
| 165 | + 'abusefilter-edit-builder-misc-ternery' => 'Ternery operator (1 ? 2 : 3)', |
| 166 | + 'abusefilter-edit-builder-misc-in' => 'contained in string (in)', |
| 167 | + 'abusefilter-edit-builder-misc-like' => 'Matches regex (like)', |
| 168 | + 'abusefilter-edit-builder-group-funcs' => 'Functions', |
| 169 | + 'abusefilter-edit-builder-funcs-length' => 'String length (length)', |
| 170 | + 'abusefilter-edit-builder-funcs-lcase' => 'To lower case (lcase)', |
| 171 | + 'abusefilter-edit-builder-funcs-ccnorm' => 'Normalise confusable characters (ccnorm)', |
| 172 | + 'abusefilter-edit-builder-funcs-rmdoubles' => 'Remove double-characters (rmdoubles)', |
| 173 | + 'abusefilter-edit-builder-funcs-specialratio' => 'Special ratio (specialratio)', |
| 174 | + 'abusefilter-edit-builder-funcs-norm' => 'Normalise (norm)', |
| 175 | + 'abusefilter-edit-builder-funcs-count' => 'Number of times string X appears in string Y (count)', |
| 176 | + 'abusefilter-edit-builder-group-vars' => 'Variables', |
| 177 | + 'abusefilter-edit-builder-vars-accountname' => 'Account name (on account creation)', |
| 178 | + 'abusefilter-edit-builder-vars-action' => 'Action', |
| 179 | + 'abusefilter-edit-builder-vars-addedlines' => 'Lines added in edit', |
| 180 | + 'abusefilter-edit-builder-vars-delta' => 'Size change in edit', |
| 181 | + 'abusefilter-edit-builder-vars-diff' => 'Unified diff of changes made by edit', |
| 182 | + 'abusefilter-edit-builder-vars-newsize' => 'New page size', |
| 183 | + 'abusefilter-edit-builder-vars-oldsize' => 'Old page size', |
| 184 | + 'abusefilter-edit-builder-vars-removedlines' => 'Lines removed in edit', |
| 185 | + 'abusefilter-edit-builder-vars-summary' => 'Edit summary/reason', |
| 186 | + 'abusefilter-edit-builder-vars-article-id' => 'Article ID', |
| 187 | + 'abusefilter-edit-builder-vars-article-ns' => 'Article namespace', |
| 188 | + 'abusefilter-edit-builder-vars-article-text' => 'Article title (without namespace)', |
| 189 | + 'abusefilter-edit-builder-vars-article-prefixedtext' => 'Full article title', |
| 190 | + 'abusefilter-edit-builder-vars-movedfrom-id' => 'Article ID of move source page', |
| 191 | + 'abusefilter-edit-builder-vars-movedfrom-ns' => 'Namespace of move source page', |
| 192 | + 'abusefilter-edit-builder-vars-movedfrom-text' => 'Title of move source page', |
| 193 | + 'abusefilter-edit-builder-vars-movedfrom-prefixedtext' => 'Full title of move source page', |
| 194 | + 'abusefilter-edit-builder-vars-movedto-id' => 'Article ID of move destination page', |
| 195 | + 'abusefilter-edit-builder-vars-movedto-ns' => 'Namespace of move destination page', |
| 196 | + 'abusefilter-edit-builder-vars-movedto-text' => 'Title of move destination page', |
| 197 | + 'abusefilter-edit-builder-vars-movedto-prefixedtext' => 'Full title of move destination page', |
| 198 | + 'abusefilter-edit-builder-vars-user-editcount' => 'Edit count of user', |
| 199 | + 'abusefilter-edit-builder-vars-user-age' => 'Age of user account', |
| 200 | + 'abusefilter-edit-builder-vars-user-name' => 'Name of user account', |
| 201 | + 'abusefilter-edit-builder-vars-user-groups' => 'Groups (including implicit) user is in', |
| 202 | + 'abusefilter-edit-builder-vars-user-emailconfirm' => 'Time email address was confirmed', |
| 203 | + |
| 204 | + |
| 205 | + |
143 | 206 | // Filter history |
144 | 207 | 'abusefilter-history' => 'History for filter $1', |
145 | 208 | 'abusefilter-history-hidden' => 'hidden', |
Index: trunk/extensions/AbuseFilter/SpecialAbuseFilter.php |
— | — | @@ -306,34 +306,8 @@ |
307 | 307 | ); |
308 | 308 | } |
309 | 309 | } |
310 | | - |
311 | | - $rules = Xml::textarea( 'wpFilterRules', ( isset( $row->af_pattern ) ? $row->af_pattern."\n" : "\n" ) ); |
312 | | - $rules .= Xml::element( 'input', array( 'type' => 'button', 'onclick' => 'doSyntaxCheck()', 'value' => wfMsg( 'abusefilter-edit-check' ), 'id' => 'mw-abusefilter-syntaxcheck' ) ); |
313 | | - |
314 | | - // Add syntax-checking script |
315 | | - $scScript = "function doSyntaxCheck() |
316 | | - { |
317 | | - var filter = document.getElementById('wpFilterRules').value; |
318 | | - injectSpinner( document.getElementById( 'mw-abusefilter-syntaxcheck' ), 'abusefilter-syntaxcheck' ); |
319 | | - sajax_do_call( 'AbuseFilter::ajaxCheckSyntax', [filter], processSyntaxResult ); |
320 | | - } |
321 | | - function processSyntaxResult( request ) { |
322 | | - var response = request.responseText; |
323 | | - |
324 | | - removeSpinner( 'abusefilter-syntaxcheck' ); |
325 | | - |
326 | | - if (response.match( /OK/ )) { |
327 | | - // Successful |
328 | | - jsMsg( 'No syntax errors.', 'mw-abusefilter-syntaxresult' ); |
329 | | - } else { |
330 | | - var error = response.substr(4); |
331 | | - jsMsg( 'Syntax error: '+error, 'mw-abusefilter-syntaxresult' ); |
332 | | - } |
333 | | - }"; |
334 | | - |
335 | | - $wgOut->addInlineScript( $scScript ); |
336 | 310 | |
337 | | - $fields['abusefilter-edit-rules'] = $rules; |
| 311 | + $fields['abusefilter-edit-rules'] = $this->buildEditBox($row); |
338 | 312 | $fields['abusefilter-edit-notes'] = Xml::textarea( 'wpFilterNotes', ( isset( $row->af_comments ) ? $row->af_comments."\n" : "\n" ) ); |
339 | 313 | |
340 | 314 | // Build checkboxen |
— | — | @@ -381,6 +355,48 @@ |
382 | 356 | return $output; |
383 | 357 | } |
384 | 358 | |
| 359 | + function buildEditBox( $row ) { |
| 360 | + global $wgOut; |
| 361 | + |
| 362 | + $rules = Xml::textarea( 'wpFilterRules', ( isset( $row->af_pattern ) ? $row->af_pattern."\n" : "\n" ) ); |
| 363 | + |
| 364 | + $dropDown = array( |
| 365 | + 'op-arithmetic' => array('+' => 'addition', '-' => 'subtraction', '*' => 'multiplication', '/' => 'divide', '%' => 'modulo', '**' => 'pow'), |
| 366 | + 'op-comparison' => array('==' => 'equal', '!=' => 'notequal', '<' => 'lt', '>' => 'gt', '<=' => 'lte', '>=' => 'gte'), |
| 367 | + 'op-bool' => array( '!' => 'not', '&' => 'and', '|' => 'or', '^' => 'xor' ), |
| 368 | + 'misc' => array( 'val1 ? iftrue : iffalse' => 'ternery', 'in' => 'in', 'like' => 'like' ), |
| 369 | + 'funcs' => array( 'length(string)' => 'length', 'lcase(string)' => 'lcase', 'ccnorm(string)' => 'ccnorm', 'rmdoubles(string)' => 'rmdoubles', 'specialratio(string)' => 'specialratio', 'norm(string)' => 'norm', 'count(needle,haystack)' => 'count' ), |
| 370 | + 'vars' => array( 'ACCOUNTNAME' => 'accountname', 'ACTION' => 'action', 'ADDED_LINES' => 'addedlines', 'EDIT_DELTA' => 'delta', 'EDIT_DIFF' => 'diff', 'NEW_SIZE' => 'newsize', 'OLD_SIZE' => 'oldsize', 'REMOVED_LINES' => 'removedlines', 'SUMMARY' => 'summary', 'ARTICLE_ARTICLEID' => 'article-id', 'ARTICLE_NAMESPACE' => 'article-ns', 'ARTICLE_TEXT' => 'article-text', 'ARTICLE_PREFIXEDTEXT' => 'article-prefixedtext', 'MOVED_FROM_ARTICLEID' => 'movedfrom-id', 'MOVED_FROM_NAMESPACE' => 'movedfrom-ns', 'MOVED_FROM_TEXT' => 'movedfrom-text', 'MOVED_FROM_PREFIXEDTEXT' => 'movedfrom-prefixedtext', 'MOVED_TO_ARTICLEID' => 'movedto-id', 'MOVED_TO_NAMESPACE' => 'movedto-ns', 'MOVED_TO_TEXT' => 'movedto-text', 'MOVED_TO_PREFIXEDTEXT' => 'movedto-prefixedtext', 'USER_EDITCOUNT' => 'user-editcount', 'USER_AGE' => 'user-age', 'USER_NAME' => 'user-name', 'USER_GROUPS' => 'user-groups', 'USER_EMAILCONFIRM' => 'user-emailconfirm'), |
| 371 | + ); |
| 372 | + |
| 373 | + // Generate builder drop-down |
| 374 | + $builder = ''; |
| 375 | + |
| 376 | + $builder .= Xml::option( wfMsg( "abusefilter-edit-builder-select") ); |
| 377 | + |
| 378 | + foreach( $dropDown as $group => $values ) { |
| 379 | + $builder .= Xml::openElement( 'optgroup', array( 'label' => wfMsg( "abusefilter-edit-builder-group-$group" ) ) ) . "\n"; |
| 380 | + |
| 381 | + foreach( $values as $content => $name ) { |
| 382 | + $builder .= Xml::option( wfMsg( "abusefilter-edit-builder-$group-$name" ), $content ) . "\n"; |
| 383 | + } |
| 384 | + |
| 385 | + $builder .= Xml::closeElement( 'optgroup' ) . "\n"; |
| 386 | + } |
| 387 | + |
| 388 | + $rules .= Xml::tags( 'select', array( 'id' => 'wpFilterBuilder', 'onchange' => 'addText();' ), $builder ); |
| 389 | + |
| 390 | + // Add syntax checking |
| 391 | + $rules .= Xml::element( 'input', array( 'type' => 'button', 'onclick' => 'doSyntaxCheck()', 'value' => wfMsg( 'abusefilter-edit-check' ), 'id' => 'mw-abusefilter-syntaxcheck' ) ); |
| 392 | + |
| 393 | + // Add script |
| 394 | + $scScript = file_get_contents(dirname(__FILE__)."/edit.js"); |
| 395 | + |
| 396 | + $wgOut->addInlineScript( $scScript ); |
| 397 | + |
| 398 | + return $rules; |
| 399 | + } |
| 400 | + |
385 | 401 | function buildConsequenceEditor( $row, $actions ) { |
386 | 402 | global $wgAbuseFilterAvailableActions; |
387 | 403 | $setActions = array(); |