From HTYP, the free directory anyone can edit
<?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 = "<br>'''{$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('<table style="background: #ccffcc;"><tr><td>');
$wgOut->addWikiText($out,TRUE); $out = '';
$wgOut->addHTML('</td></tr></table>');
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 = '<table>';
$out .= "\n<tr><td align=right><b>ID</b>:</td><td>{$objRows->ID}</td></tr>";
$out .= "\n<tr><td align=right><b>Pattern</b>:</td><td>{$htPattern}</td></tr>";
$out .= "\n<tr><td align=right><b>Added</b>:</td><td>{$objRows->WhenAdded}</td></tr>";
$out .= "\n<tr><td align=right><b>Tried</b>:</td><td>{$objRows->WhenTried}</td></tr>";
$out .= "\n<tr><td align=right><b>flags</b>:</td><td>";
$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 .= '</td></tr></table>';
$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 = '<nowiki>'.$objRows->Pattern.'</nowiki>';
$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('<br><b>Database Error</b>: '.$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('<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('</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('<pre>'.htmlspecialchars(var_export($matches,TRUE)).'</pre>');
}
}
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('<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=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)." || <nowiki>{$objFilt->Pattern}</nowiki>";
}
$out .= "\n|}";
} else {
$out = "* No filter matches";
}
$wgOut->AddWikiText($out);
//$wgOut->AddWikiText('* DEBUG: '.$debug);
}
}
}
function SpIDSelfLink($iDo,$iKey,$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.']]';
}
/*
function SelfLink($iPage,$iKey,$iVal,$iText) {
return '[[{{FULLPAGENAME}}/page'.KS_CHAR_URL_ASSIGN.$iPage.'/'.$iKey.KS_CHAR_URL_ASSIGN.$iVal.'|'.$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;
}
}
//--
}