MWX/SpamFerret/Special: Difference between revisions

From Woozle Writes Code
Jump to navigation Jump to search
(v0.1)
(10/6 minor improvements)
Line 9: Line 9:
2009-08-04 0.0 (Wzl) Started writing
2009-08-04 0.0 (Wzl) Started writing
2009-10-01 0.1 (Wzl) incremental improvements; clsMenu now a separate file
2009-10-01 0.1 (Wzl) incremental improvements; clsMenu now a separate file
2009-10-06 0.2 (Wzl) text-check now shows if matching filters are deactivated
*/
*/
$wgSpecialPages['SpamFerret'] = 'SpecialSpamFerret'; # Let MediaWiki know about your new special page.
$wgSpecialPages['SpamFerret'] = 'SpecialSpamFerret'; # Let MediaWiki know about your new special page.
Line 16: Line 17:
         'description' => 'special page for SpamFerret administration',
         'description' => 'special page for SpamFerret administration',
         'author' => 'Woozle (Nick) Staddon',
         'author' => 'Woozle (Nick) Staddon',
'version' => '0.1 2009-10-01 alpha'
'version' => '0.2 2009-10-06 alpha'
);
);
define('KS_CHAR_URL_ASSIGN',':'); // character used for encoding values in wiki-internal URLs
define('KS_CHAR_URL_ASSIGN',':'); // character used for encoding values in wiki-internal URLs
Line 303: Line 304:


$id = $objRows->ID;
$id = $objRows->ID;
$wtID = SelfLink('filter','id',$id,$id);
$wtID = SpIDSelfLink('filter','id',$id,$id);
$strPatt = '<nowiki>'.$objRows->Pattern.'</nowiki>';
$strPatt = '<nowiki>'.$objRows->Pattern.'</nowiki>';
$isActive = $objRows->isActive;
$isActive = $objRows->isActive;
Line 330: Line 331:
$inText = $wgRequest->getVal('sample');
$inText = $wgRequest->getVal('sample');


$wgOut->AddHTML('<form method=post action="./">');
$wgOut->AddHTML('<form method=post action="">');
$wgOut->AddHTML('Expression: <input name=expr size=80 value="'.$inExpr.'">');
$wgOut->AddHTML('Expression: <input name=expr size=80 value="'.htmlspecialchars($inExpr).'">');
$wgOut->AddHTML('<br>Text to check:<br><textarea name=sample cols=50 rows=10>'.$inText.'</textarea>');
$wgOut->AddHTML('<br>Text to check:<br><textarea name=sample cols=50 rows=10>'.htmlspecialchars($inText).'</textarea>');
$wgOut->AddHTML('<input name=go type=submit value="Evaluate">');
$wgOut->AddHTML('<input name=go type=submit value="Evaluate">');
$wgOut->AddHTML('</form>');
$wgOut->AddHTML('</form>');
Line 345: Line 346:
} else {
} else {
      $wgOut->AddWikiText("===Matches===");
      $wgOut->AddWikiText("===Matches===");
      $wgOut->AddHTML('<pre>'.var_export($matches,TRUE).'</pre>');
      $wgOut->AddHTML('<pre>'.htmlspecialchars(var_export($matches,TRUE)).'</pre>');
}
}
     }
     }
Line 354: Line 355:


$inText = $wgRequest->getVal('sample');
$inText = $wgRequest->getVal('sample');
$doEcho = $wgRequest->getVal('doShowText');
$htDoEcho = $doEcho?' checked':'';


$wgOut->AddHTML('Check sample text against all defined filters.');
$wgOut->AddHTML('Check sample text against all defined filters.');
Line 359: Line 362:
$wgOut->AddHTML('<br>Text to check:<br><textarea name=sample cols=50 rows=10>'.$inText.'</textarea>');
$wgOut->AddHTML('<br>Text to check:<br><textarea name=sample cols=50 rows=10>'.$inText.'</textarea>');
$wgOut->AddHTML('<input name=go type=submit value="Evaluate">');
$wgOut->AddHTML('<input name=go type=submit value="Evaluate">');
$wgOut->AddHTML('<input name=doShowText type=checkbox'.$htDoEcho.'>Echo input text');
$wgOut->AddHTML('</form>');
$wgOut->AddHTML('</form>');
if ($inText != '') {
if ($inText != '') {
    $wgOut->AddWikiText('==Results==');
    $wgOut->AddWikiText('==Results==');
    $wgOut->AddWikiText("'''checked''': ".htmlspecialchars($inText),TRUE);
    if ($doEcho) {
$wgOut->AddWikiText("'''checked''': ".htmlspecialchars($inText),TRUE);
    }


    $objSF = new SpamFerret();
    $objSF = new SpamFerret();
Line 372: Line 378:
    if (is_array($gFilterMatches)) {
    if (is_array($gFilterMatches)) {
$out = "{|\n|-\n! ID || length || filter";
$out = "{|\n|-\n! ID || length || filter";
$isOdd = FALSE;
foreach ($gFilterMatches as $id=>$text) {
foreach ($gFilterMatches as $id=>$text) {
    $wtStyle = $isOdd?'background:#ffffff;':'background:#eeeeee;';
    $isOdd = !$isOdd;
    $objFilt = $objSF->PatternTbl->GetItem($id);
    $objFilt = $objSF->PatternTbl->GetItem($id);
    $out .= "\n|-\n| $id || ".strlen($text)." || <nowiki>{$objFilt->Pattern}</nowiki>";
 
    $isActive = $objFilt->isActive;
    if (!$isActive) {
$wtStyle .= ' color: #888888;';
$wtStyle .= ' text-decoration: line-through;';
    }
 
    $out .= "\n|- style=\"$wtStyle\"\n| $id || ".strlen($text)." || <nowiki>{$objFilt->Pattern}</nowiki>";
}
}
$out .= "\n|}";
$out .= "\n|}";
Line 385: Line 403:
     }
     }
}
}
    function SpIDSelfLink($iDo,$iKey,$iVal,$iText) {
function SpIDSelfLink($iDo,$iKey,$iVal,$iText) {
    //    return '[[{{FULLPAGENAME}}/do'.KS_CHAR_URL_ASSIGN.$iDo.'/'.$iKey.KS_CHAR_URL_ASSIGN.$iVal.'|'.$iText.']]';
//    return '[[{{FULLPAGENAME}}/do'.KS_CHAR_URL_ASSIGN.$iDo.'/'.$iKey.KS_CHAR_URL_ASSIGN.$iVal.'|'.$iText.']]';
return '[[Special:AudioFerret/do'.KS_CHAR_URL_ASSIGN.$iDo.'/'.$iKey.KS_CHAR_URL_ASSIGN.$iVal.'|'.$iText.']]';
    return '[[Special:AudioFerret/do'.KS_CHAR_URL_ASSIGN.$iDo.'/'.$iKey.KS_CHAR_URL_ASSIGN.$iVal.'|'.$iText.']]';
    }
}
 
/*
function SelfLink($iPage,$iKey,$iVal,$iText) {
function SelfLink($iPage,$iKey,$iVal,$iText) {
     return '[[{{FULLPAGENAME}}/page'.KS_CHAR_URL_ASSIGN.$iPage.'/'.$iKey.KS_CHAR_URL_ASSIGN.$iVal.'|'.$iText.']]';
     return '[[{{FULLPAGENAME}}/page'.KS_CHAR_URL_ASSIGN.$iPage.'/'.$iKey.KS_CHAR_URL_ASSIGN.$iVal.'|'.$iText.']]';
}
}
*/
function ShowFlag($iName,$iVal,$iText) {
function ShowFlag($iName,$iVal,$iText) {
     $out = '<input type=checkbox name="'.$iName.'" value='.$iVal.'>'.$iText;
     $out = '<input type=checkbox name="'.$iName.'" value='.$iVal.'>'.$iText;
     return $out;
     return $out;
}
}
if (!function_exists('TimeStamp_HideTime')) {
//--
function TimeStamp_HideTime($iStamp) {
function TimeStamp_HideTime($iStamp) {


Line 411: Line 433:
return NULL;
return NULL;
     }
     }
}
//--
}
}
</php>
</php>

Revision as of 12:36, 6 October 2009

Code

<php><?php /*

NAME: SpecialSpamFerret
PURPOSE: Special page for administering the SpamFerret database
REQUIRES: SpamFerret (for now...)
AUTHOR: Woozle (Nick) Staddon
VERSION:

2009-08-04 0.0 (Wzl) Started writing 2009-10-01 0.1 (Wzl) incremental improvements; clsMenu now a separate file 2009-10-06 0.2 (Wzl) text-check now shows if matching filters are deactivated

  • /

$wgSpecialPages['SpamFerret'] = 'SpecialSpamFerret'; # Let MediaWiki know about your new special page. $wgExtensionCredits['other'][] = array(

       'name' => 'Special:SpamFerret',

'url' => 'http://htyp.org/SpamFerret',

       'description' => 'special page for SpamFerret administration',
       'author' => 'Woozle (Nick) Staddon',

'version' => '0.2 2009-10-06 alpha' ); define('KS_CHAR_URL_ASSIGN',':'); // character used for encoding values in wiki-internal URLs

function wfSpecialSpamFerret() { // This registers the page's class. I think. global $wgRequest;

$app = new SpecialSpamFerret($wgRequest); }

require_once( $wgScriptPath.'includes/SpecialPage.php' ); /* class clsMenu {

   public $Page;
   private $SubNodes;
   private $AllNodes;
   protected $Action;
   protected $Selected;
   public function __construct($iWikiPage) {

$this->Page = $iWikiPage;

   }
   public function WikiText($iAction) {

$this->Action = $iAction; if (isset($this->AllNodes[$iAction])) { $this->AllNodes[$iAction]->Activate(); } $out = $this->WikiText_SubMenu($iAction); return $out;

   }
   public function WikiText_SubMenu($iAction) {

$out = NULL; foreach ($this->SubNodes as $objNode) { if (!is_null($out)) { $out .= ' | '; } $out .= $objNode->WikiText($iAction); } return $out;

   }
   public function Add(clsMenuNode $iNode) {

$this->SubNodes[$iNode->Name] = $iNode; $this->Root()->AllNodes[$iNode->Name] = $iNode; $iNode->Parent = $this;

   }
   protected function Root() {

return $this;

   }
   protected function Activate() {    }	// do nothing
   public function Execute() {

if (isset($this->Selected)) { $out = $this->Selected->DoAction(); } else { $out = NULL; } return $out;

   }

}

abstract class clsMenuNode extends clsMenu {

   public $Name;
   protected $Text;
   protected $DoSpec;
   public $Parent;
   protected $IsActive;
   public function __construct($iText, $iDo) {

$this->Name = $iDo; $this->Text = $iText; $this->DoSpec = $iDo;

   }
   public function Root() {

return $this->Parent->Root();

   }
   abstract public function DoAction();
   protected function Activate() {

$this->IsActive = TRUE; $this->Parent->Activate();

   }
   public function WikiText($iAction) {

$wtSelf = $this->Root()->Page; $wtItem = "[[$wtSelf/page".KS_CHAR_URL_ASSIGN."{$this->DoSpec}/|{$this->Text}]]"; // if ($iAction == $this->DoSpec) { if ($this->IsActive) { $out = "$wtItem"; $this->Root()->Selected = $this; } else { $out = $wtItem; } return $out;

   }

}

class clsMenuRow extends clsMenuNode {

   public function DoAction() {

$out = "
{$this->Text}: "; $out .= $this->WikiText_SubMenu($this->Root()->Action); return $out;

   }

}

class clsMenuItem extends clsMenuNode {

   public function DoAction() {

// later: actually implement menu item action

   }

}

  • /

class SpecialSpamFerret extends SpecialPage { //======= // STATIC

   static private $objDB;
   static public function Setting($iName) {

global $wgSpamFerretSettings;

return $wgSpamFerretSettings[$iName];

   }
   static public function DB() {

if (!isset(self::$objDB)) { self::$objDB = new clsDatabase(self::Setting('dbspec')); self::$objDB->Open(); } return self::$objDB;

   }

//======= // DYNAMIC

 protected $args;
 public function __construct() {

global $wgMessageCache;

parent::__construct( 'SpamFerret' ); $this->includable( false );

       $wgMessageCache->addMessage('spamferret', 'SpamFerret administration');
 }
 function execute( $par ) {

global $wgUser;

$this->setHeaders(); $this->GetArgs($par);

if ($wgUser->isAllowed('editinterface')) { $this->doAdmin(); } else { $this->doUser(); }

 }
 private function GetArgs($par) {

/*

PURPOSE: Parses variable arguments from the URL
 The URL is formatted as a series of arguments /arg=val/arg=val/..., so that we can always refer directly
   to any particular item as a wiki page title while also not worrying about hierarchy/order.
  • /
   $args_raw = split('/',$par);
   foreach($args_raw as $arg_raw) {

if (strpos($arg_raw,KS_CHAR_URL_ASSIGN) !== FALSE) { list($key,$val) = split(KS_CHAR_URL_ASSIGN,$arg_raw); $this->args[$key] = $val; }

   }
 }
 public function doAdmin() {

global $wgOut; /* PURPOSE: do stuff that only admins are allowed to do

  • /

if (isset($this->args['page'])) { $page = $this->args['page']; } else { $page = NULL; } // display menu $wtSelf = 'Special:'.$this->name();

$objMenu = new clsMenu($wtSelf); $objMenu->Add($objRow = new clsMenuRow('Utilities','menu.util')); $objRow->Add(new clsMenuItem('check expression','ckexpr')); $objRow->Add(new clsMenuItem('check text','cktext')); $objMenu->Add($objRow = new clsMenuRow('Inspect','menu.insp')); $objRow->Add(new clsMenuItem('recent edits','edits')); $objRow->Add(new clsMenuItem('filters','filters'));

$out = $objMenu->WikiText($page); $out .= $objMenu->Execute();

$wgOut->addHTML('

');

$wgOut->addWikiText($out,TRUE); $out = ;

$wgOut->addHTML('

');

if (!is_null($page)) { $id = isset($this->args['id'])?$this->args['id']:NULL; switch ($page) { case 'ckexpr': $this->doRegexChecker(); break; case 'cktext': $this->doTextChecker(); break; case 'edits': $this->doInspectEdits(); break; case 'filters': $this->doInspectFilters(); break; case 'filter': $this->doEditFilter($id); } }

 }
 public function doUser() {

global $wgOut; /* PURPOSE: do only stuff that regular users are allowed to do

  • /

$wgOut->AddWikiText('Hello regular user! I haven\'t written anything for you yet, but eventually.');

 }

// individual admin functions

   public function doInspectEdits() {

global $wgOut,$wgRequest;

$sqlFilt = $wgRequest->getVal('sqlFilt',); $sqlSort = $wgRequest->getVal('sqlSort','ID DESC'); $sqlRows = $wgRequest->getVal('sqlRows',50);

if ($sqlFilt == ) { $sqlFiltTerm = ; } else { $sqlFiltTerm = ' WHERE '.$sqlFilt; }

if ($sqlSort == ) { $sqlSortTerm = ; } else { $sqlSortTerm = ' ORDER BY '.$sqlSort; }

if ($sqlRows == ) { $sqlRowsTerm = ; } else { $sqlRowsTerm = ' LIMIT '.$sqlRows; } $sql = 'SELECT * FROM attempts'.$sqlFiltTerm.$sqlSortTerm.$sqlRowsTerm;

   }
   public function doEditFilter($iID) {

global $wgOut;

$dbSP = self::DB(); $tblFilt = new clsTable($dbSP); $tblFilt->Name('patterns'); $tblFilt->KeyName('ID'); $objRows = $tblFilt->GetItem($iID); if ($objRows->hasRows()) { $htPattern = htmlspecialchars($objRows->Pattern);

$out = '

'; $out .= "\n"; $out .= "\n"; $out .= "\n"; $out .= "\n"; $out .= "\n
ID:{$objRows->ID}
Pattern:{$htPattern}
Added:{$objRows->WhenAdded}
Tried:{$objRows->WhenTried}
flags:";

$out .= ShowFlag('active',$objRows->isActive,'Active'); $out .= ShowFlag('regex',$objRows->isRegex,'Regex'); $out .= ShowFlag('diff',$objRows->isDiff,'Diff'); $out .= ShowFlag('isurl',$objRows->isURL,'URL');

$out .= '

';

$wgOut->AddHTML($out); $out = ; }

   }
   public function doInspectFilters() {

global $wgOut;

$dbSP = self::DB(); $tblFilt = new clsTable($dbSP); $tblFilt->Name('patterns'); $tblFilt->KeyName('ID'); $objRows = $tblFilt->GetData(); if ($objRows->hasRows()) { $out = "{| class=sortable\n|-\n! ID || Pattern || A? || U? || R? || D? || Added || Tried || Count"; $isOdd = TRUE; while ($objRows->NextRow()) { $wtStyle = $isOdd?'background:#ffffff;':'background:#eeeeee;'; $isOdd = !$isOdd;

$id = $objRows->ID; $wtID = SpIDSelfLink('filter','id',$id,$id); $strPatt = ''.$objRows->Pattern.''; $isActive = $objRows->isActive; $isURL = $objRows->isURL; $isRegex = $objRows->isRegex; $isDiff = $objRows->isDiff; $wtAdded = TimeStamp_HideTime($objRows->WhenAdded); $wtTried = TimeStamp_HideTime($objRows->WhenTried); $intCount = $objRows->Count;

$out .= "\n|- style=\"$wtStyle\"\n|$wtID||$strPatt||$isActive||$isURL||$isRegex||$isDiff||$wtAdded||$wtTried||align=right|$intCount"; } $out .= "\n|}\n"; $wgOut->AddWikiText($out); } else { $wgOut->AddHTML('No filters have been defined.'); if (!$dbSP->isOk()) { $wgOut->AddHTML('
Database Error: '.$dbSP->getError()); } }

   }
   public function doRegexChecker() {

global $wgOut,$wgRequest;

$inExpr = $wgRequest->getVal('expr'); $inText = $wgRequest->getVal('sample');

$wgOut->AddHTML('<form method=post action="">'); $wgOut->AddHTML('Expression: <input name=expr size=80 value="'.htmlspecialchars($inExpr).'">'); $wgOut->AddHTML('
Text to check:
<textarea name=sample cols=50 rows=10>'.htmlspecialchars($inText).'</textarea>'); $wgOut->AddHTML('<input name=go type=submit value="Evaluate">'); $wgOut->AddHTML('</form>'); $wgOut->AddWikiText('==Results=='); $wgOut->AddWikiText("checked: ".htmlspecialchars($inExpr),TRUE); $chDelim = '/'; $strPattCk = str_replace($chDelim,'\\'.$chDelim,$inExpr); $isMatch = @preg_match('/'.$strPattCk.'/',$inText,$matches); if (isset($php_errormsg)) { $outErr .= "===Error===\n$php_errormsg"; $wgOut->AddWikiText($outErr); } else { $wgOut->AddWikiText("===Matches===");

$wgOut->AddHTML('

'.htmlspecialchars(var_export($matches,TRUE)).'

');

}

   }
   public function doTextChecker() {

global $wgOut,$wgRequest; global $gFilterMatches,$gFilterCount,$gFilterRows; global $debug;

$inText = $wgRequest->getVal('sample'); $doEcho = $wgRequest->getVal('doShowText'); $htDoEcho = $doEcho?' checked':;

$wgOut->AddHTML('Check sample text against all defined filters.'); $wgOut->AddHTML('<form method=post action="">'); $wgOut->AddHTML('
Text to check:
<textarea name=sample cols=50 rows=10>'.$inText.'</textarea>'); $wgOut->AddHTML('<input name=go type=submit value="Evaluate">'); $wgOut->AddHTML('<input name=doShowText type=checkbox'.$htDoEcho.'>Echo input text'); $wgOut->AddHTML('</form>'); if ($inText != ) { $wgOut->AddWikiText('==Results=='); if ($doEcho) { $wgOut->AddWikiText("checked: ".htmlspecialchars($inText),TRUE); }

$objSF = new SpamFerret(); $objSF->OpenDatabase(); $objSF->txtEditRaw = $inText; $objSF->CheckFilters(TRUE); $wgOut->AddWikiText('* '.$gFilterRows.' filter'.Pluralize($gFilterRows).' defined'); $wgOut->AddWikiText('* '.$gFilterCount.' filter'.Pluralize($gFilterCount).' checked'); if (is_array($gFilterMatches)) { $out = "{|\n|-\n! ID || length || filter"; $isOdd = FALSE; foreach ($gFilterMatches as $id=>$text) {

$wtStyle = $isOdd?'background:#ffffff;':'background:#eeeeee;'; $isOdd = !$isOdd;

$objFilt = $objSF->PatternTbl->GetItem($id);

$isActive = $objFilt->isActive; if (!$isActive) { $wtStyle .= ' color: #888888;'; $wtStyle .= ' text-decoration: line-through;'; }

$out .= "\n|- style=\"$wtStyle\"\n| $id || ".strlen($text)." || {$objFilt->Pattern}"; } $out .= "\n|}"; } else { $out = "* No filter matches"; } $wgOut->AddWikiText($out); //$wgOut->AddWikiText('* DEBUG: '.$debug); }

   }

} function SpIDSelfLink($iDo,$iKey,$iVal,$iText) { // return ''.$iText.'';

   return ''.$iText.'';

} /* function SelfLink($iPage,$iKey,$iVal,$iText) {

   return ''.$iText.'';

}

  • /

function ShowFlag($iName,$iVal,$iText) {

   $out = '<input type=checkbox name="'.$iName.'" value='.$iVal.'>'.$iText;
   return $out;

}

if (!function_exists('TimeStamp_HideTime')) { //-- function TimeStamp_HideTime($iStamp) {

   if (is_string($iStamp)) {

$intStamp = strtotime($iStamp);

   } else if (is_int($iStamp)) {

$intStamp = $iStamp;

   } else {

$intStamp = NULL;

   }
   if (!is_null($intStamp)) {

return date('Y-m-d',$intStamp);

   } else {

return NULL;

   }

} //-- } </php>