View Source

This tool shows you the source code for the latest version of Tangotango's tools.


Showing source code for rfalib2.php

<?php
/*
RfA Analysis Library 2.05
This version breaks compatibility with the 1.x series
Copyright (C) 2006 Tangotango (tangotango.wp _at_ gmail _dot_ com)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

define('TSREGEX',"[0-2][0-9]\:[0-5][0-9], [1-3]?[0-9] (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{4} \(UTC\)");
function 
rfa_getver() { return '2.05'; }

/*
 * An RFA object contains the parsed information for an RFA
 */
class RFA {
    var 
$username False;
    var 
$enddate  False;
    var 
$support  = array();
    var 
$oppose   = array();
    var 
$neutral  = array();
    var 
$duplicates = array();
    var 
$lasterror '';

    function 
reset() {
        
$username False;
        
$enddate  False;
        
$support  = array();
        
$oppose   = array();
        
$neutral  = array();
        
$duplicates = array();
        
$lasterror '';
    }
    
    
/*
     * Attempts to find a signature in $input using the default regex. Returns matches.
     */
    
function findsig($input, &$matches) {
        
//Supports User: and User talk: wikilinks, {{fullurl}}, unsubsted {{unsigned}}, unsubsted {{unsigned2}}, anything that looks like a custom sig template
        
return preg_match_all(
         
"/\[\[[Uu]ser(?:[\s_][Tt]alk)?\:([^\]\|\/]*)(?:\|[^\]]*)?\]\]"                                     //1: Normal [[User:XX]] and [[User talk:XX]]
        
."|\{\{(?:[Ff]ullurl\:[Uu]ser(?:[\s_][Tt]alk)?\:|[Uu]nsigned\|)([^\}\|]*)(?:|[\|\}]*)?\}\}"    //2: {{fullurl}} and {{unsigned}} templates
        
."|(?:\{\{)[Uu]ser(?:[\s_][Tt]alk)?\:([^\}\/\|]*)"                                                  //3: {{User:XX/sig}} templates
        
."|\{\{[Uu]nsigned2\|[^\|]*\|([^\}]*)\}\}"                                                                //4: {{unsigned2|Date|XX}} templates
        
."|(?:\[\[)[Uu]ser\:([^\]\/\|]*)\/[Ss]ig[\|\]]/"                                                        //5: [[User:XX/sig]] links (compromise measure)
        
,$input,$matches,PREG_OFFSET_CAPTURE);
    }

    
/*
     * Attempts to find a signature in $input using a different regex. Returns matches.
     */
    
function findsig2($input, &$matches) {
        return 
preg_match_all
         
"/\[\[[Uu]ser(?:[\s_][Tt]alk)?\:([^\]\/\|]*)"        //5: "[[User:XX/PageAboutMe" links (notice no end tag)
        
."|\[\[[Ss]pecial\:[Cc]ontributions\/([^\|\]]*)/"
        
,$input,$matches,PREG_OFFSET_CAPTURE);
    }

    
/*
     * Attempts to find a signature in $input. Returns the name of the user, false on failure.
     */
    
function findsiginline($input, &$iffy) {
        
$iffy 0;
        
        
$parsee_array explode("\n",$input);
        for (
$n 0$n count($parsee_array); $n++) { //This for will terminate when a sig is found.
            
$parsee $parsee_array[$n];
            
//Okay, let's try and remove "copied from above" messages. If the line has more than one timestamp, we'll disregard anything after the first.
            //Note: we're ignoring people who use custom timestamps - if these peoples' votes are moved, the mover's name will show up as having voted.
            
            //If more than one timestamp is found in the first portion of the vote:
            
$tsmatches = array();
            
$dummymatches = array();
            if (
preg_match_all('/'.TSREGEX.'/',$parsee,$tsmatches,PREG_OFFSET_CAPTURE) > 1) {
                
//Go through each timestamp-section, looking for a signature
                
foreach ($tsmatches[0] as $minisection) {
                    
$temp substr($parsee,0,$minisection[1]);
                    
//If a signature is found, stop and use it as voter
                    
if ($this->findsig($temp,$dummymatches) != 0) {            //KNOWN ISSUE: Write description later
                        
$parsee $temp;
                        break;
                    }
                }
            }
            
            
//Start the main signature-finding:        
            
$matches = array();
            if (
$this->findsig($parsee,$matches) == 0) {
                
//Okay, signature not found. Let's try the backup regex
                
if ($this->findsig2($parsee,$matches) == 0) {
                    
//Signature was not found in this iteration of the main loop :(
                    
continue; //Go on to next newline (may be iffy)
                
} else {
                    
$merged array_merge($matches[1],$matches[2]);
                }
            } else {
                
//Merge the match arrays:
                
$merged array_merge($matches[5],$matches[1],$matches[3],$matches[2],$matches[4]);
            }
            
//Remove blank values and arrays of the form ('',-1):
            
foreach($merged as $key => $value) {
                if (
is_array($value) && ($value[0] == '') && ($value[1] == -1)) {
                    unset(
$merged[$key]);
                } elseif (
$value == "") {
                    unset(
$merged[$key]);
                }
            }
            
            
//Let's find out the real signature
            
$keys = array();
            
$values = array();
            foreach (
$merged as $mergee) {
                
$keys[] = $mergee[0];
                
$values[] = $mergee[1];
            }
            
//Now sort:
            
array_multisort($valuesSORT_DESCSORT_NUMERIC$keys);
            
//Now we should have the most relevant match (i.e., the sig) at the top of $keys
            
$i 0$foundsig '';
            while (
$foundsig == '') {
                
$foundsig trim($keys[$i++]);
                if (
$i == count($keys)) break;    //If we can only find blank usernames in the sig, catch overflow
                                                //Also fires when the first sig is also the last sig, so not an error
            
}
            
            
//Set iffy flag (level 1) if went beyond first line
            
if ($n 0) {
                
$iffy 1;
            }
            return 
$foundsig//Found!        
        
}
        
        return 
False//Signature not found
    
}

    
/*
     * Analyzes an RFA section. Returns an array of parsed signatures on success. Undefined behaviour on failure.
     */
    
function analyze_section($input) {
        
//Remove trailing sharp, if any
        
$input preg_replace('/#\s*$/','',$input);

        
//Old preg_split regex: "/(^|\n)\s*\#[^\#\:\*]/"
        
$parsed preg_split("/(^|\n)\#/",$input);
        
//Shift off first empty element:
        
array_shift($parsed);
        
        foreach (
$parsed as &$parsee) { //Foreach line
            //If the line is empty for some reason, ignore
            
$parsee trim($parsee);
            if (empty(
$parsee)) continue;
                    
            
//If the line has been indented (disabled), or is a comment, ignore
            
if (($parsee[0] == ':') || ($parsee[0] == '*') || ($parsee[0] == '#')) { $parsee '///###///'; continue; }; //struck-out vote or comment
            
            
$parsedsig $this->findsiginline($parsee$iffy); //Find signature
            
$orgsig $parsee;
            
$parsee = array();
            
$parsee['context'] = $orgsig;
            if (
$parsedsig === False) {
                
$parsee['error'] = 'Signature not found';
            } else {
                
$parsee['name'] = $parsedsig;
            }
            if (@
$iffy == 1) {
                
$parsee['iffy'] = '1';
            }            
        } 
//Foreach line

        
if ( (count($parsed) == 1) && (@trim($parsed[0]['sig']) == '') ) { //filters out placeholder sharp sign used in empty sections
            
$parsed = array();
        }
        
        
//Delete struck-out keys "continued" in foreach
        
foreach($parsed as $key => $value) {
            if(
$value == '///###///') {
                unset(
$parsed[$key]);
            }
        }
        
        return 
$parsed;
    }

    
/*
     * Analyzes an RFA. Returns TRUE on success, FALSE on failure
     */
    
function analyze($rawwikitext) {
        
$split  preg_split("/^(?:(?:'''|(?:<includeonly><noin<\/includeonly><includeonly>clude><\/includeonly>)?={4,5}(?:<includeonly><\/noin<\/includeonly><includeonly>clude><\/includeonly>''')?)"
        
."\s*?(Support|Oppose|Neutral|Comments)\s*?(?:'''|(?:'''<includeonly><noin<\/includeonly><includeonly>clude><\/includeonly>)?={4,5}(?:<includeonly><\/noin<\/includeonly><includeonly>clude><\/includeonly>)?)|;\s*(Support|Oppose|Neutral|Comments))\s*(?:<br>|<br \/>)?\s*$/im"
         
,$rawwikitext, -1PREG_SPLIT_DELIM_CAPTURE);
        
        
$header array_shift($split);
        
        
//=== Deal with the header ===//
        
$header str_ireplace(array('<nowiki>','</nowiki>'),'',$header);
        
        if (
preg_match("/===\s*\[\[User:(.*?)\|.*?\]\]\s*===/",$header,$matches)) {
            
$this->username $matches[1];
        } elseif (
preg_match("/===\s*\[\[.*?\|(.*?)\]\]\s*===/",$header,$matches)) {
            
$this->username $matches[1];
        }
        
        
$header str_replace(array('[[',']]'),'',$header);

        if (
preg_match("/end(?:ing|ed)?(?: no earlier than)? (.*?) \(UTC\)/i",$header,$matches)) {
            
$this->enddate $matches[1];
        }
        
//=== End header stuff ===//
        
        //Now parse through each non-header section, figuring out what they are
        //Nothing expected = 0, Support = 1, Oppose = 2, Neutral = 3
        
$nextsection 0;

        foreach (
$split as $splut) {
            
$splut trim($splut);
            if (empty(
$splut)) {
                continue;
            }
            
            if (
strcasecmp($splut,'Support') == 0) {
                
$nextsection 1;
            } elseif (
strcasecmp($splut,'Oppose') == 0) {
                
$nextsection 2;
            } elseif (
strcasecmp($splut,'Neutral') == 0) {
                
$nextsection 3;
            } else {
                switch (
$nextsection) {
                    case 
1:
                        
$support $splut;
                        break;
                    case 
2:
                        
$oppose $splut;
                        break;
                    case 
3:
                        
$neutral $splut;
                        break;
                }
                
$nextsection 0;
            }
        }
        
        if (!isset(
$support)) { $this->lasterror "Support section not found"; return false; }
        if (!isset(
$oppose))  { $this->lasterror "Oppose section not found";  return false; }
        if (!isset(
$neutral)) { $this->lasterror "Neutral section not found"; return false; }
        
        
$this->support $this->analyze_section($support);
        
$this->oppose  $this->analyze_section($oppose);
        
$this->neutral $this->analyze_section($neutral);
        
        
//Merge all votes in one array and sort:
        
$m = array();
        foreach (
$this->support as $s) { if (isset($s['name'])) $m[] = $s['name']; }
        foreach (
$this->oppose  as $o) { if (isset($o['name'])) $m[] = $o['name']; }
        foreach (
$this->neutral as $n) { if (isset($n['name'])) $m[] = $n['name']; }
        
sort($m);
        
//Find duplicates:
        
for ($i 0$i count($m); $i++) {
            if (
$i != count($m)-1) {
                if (
$m[$i] == $m[$i+1]) {
                    
$this->duplicates[] = $m[$i];
                }
            }
        }
        
        return 
True;
    }
}
?>