Extension:AWCmod
<?php /**
* AWC extension *Template:Php
* @package MediaWiki * @subpackage Extensions * @author Aran Dunkley User:Nad * @licence GNU General Public Licence 2.0 or later */
if (!defined('MEDIAWIKI')) die('Not an entry point.');
define('AWCMOD_VERSION', '0.2.1, 2008-10-30');
$wgAutoConfirmCount = 10^10;
$wgExtensionFunctions[] = 'wfSetupAWCmod'; $wgExtensionCredits['other'][] = $wgExtensionCredits['specialpage'][] = array( 'name' => 'AWCmod', 'author' => 'User:Nad', 'description' => 'Custom extension for Adeft', 'url' => 'http://www.organicdesign.co.nz/Extension:AWCmod', 'version' => AWCMOD_VERSION );
require_once "$IP/includes/SpecialPage.php";
- Check if any awcpref parameters passed in request
$awcSearchByPref = array(); foreach ($_REQUEST as $k => $v) if (ereg('^awcsbp_(.+)$', $k, $m)) $awcSearchByPref[$m[1]] = $v;
- Process posted contact details
if (isset($_POST['wpFirstName'])) { $_POST['wpRealName'] = $_POST['wpFirstName'].' '.$_POST['wpLastName']; }
- Don't apply the db hook unless awcsearchprefs have been posted
if ($awcDBHook = count($awcSearchByPref) > 0) $_POST['fID'] = array('all');
- First stage of db patch
if ($awcDBHook) {
# SearchEngine is based on $wgDBtype so must be set before it gets changed to DatabaseAWC # - this may be paranoid now since $wgDBtype is changed back after LoadBalancer has initialised AWCmod::fixSearchType();
$wgOldDBtype = $wgDBtype; if (class_exists('Database')) wfAWCmodDBHook(); }
class AWCmod {
var $JS = ; var $searchPrefs = array(); var $skillsOptions = ; var $stateOptions = ; var $countyOptions = ;
function __construct() { global $wgUser, $wgHooks, $wgMessageCache, $wgParser, $wgTitle, $awcSearchByPref, $wgGroupPermissions, $wgWhitelistRead;
# Deny access until email address confirmed #$wgGroupPermissions['*']['read'] = false; #$wgWhitelistRead = array("Special:Preferences", "Special:Userlogin", "-");
#if (!$wgUser->isEmailConfirmed()) $wgGroupPermissions['user']['read'] = false;
$this->searchPrefs = $awcSearchByPref;
$wgHooks['RenderPreferencesForm'][] = $this; #$wgHooks['UserCreateForm'][] = $this; $wgHooks['SavePreferences'][] = $this; #$wgHooks['AbortNewAccount'][] = $this; $wgHooks['SpecialPageExecuteAfterPage'][] = $this;
# Modify login form messages to say email and name compulsory
- $wgMessageCache->addMessages(array('prefs-help-email' => 'Required'));
- $wgMessageCache->addMessages(array('prefs-help-realname' => 'Required'));
# Add a tag for creating search links $wgParser->setHook('forum_search', array($this, 'forumTag')); $wgParser->setHook('profile_search', array($this, 'profileTag'));
}
/**
* Prepare the dropdown list content if using a special page containing one of our forms
*/
function buildOptionLists(&$user = false) {
include(dirname(__FILE__)."/states.php");
$this->JS = $awcJS;
# Build skills option list $skills = $this->getOption($user, 'skills'); $this->skillsOptions = '<option/>'; foreach (array( 'Concerned Citizen', 'Accountant', 'Lawyer', 'Tax Advisor', 'Tax Agent', 'Assessor', 'Realtor' ) as $s) { $selected = $skills == $s ? ' selected' : ; $this->skillsOptions .= "<option$selected>$s</option>\n"; }
# Build state options list $state = $this->getOption($user, 'state'); $this->stateOptions = '<option value="">Enter state...</option>'; foreach (array_keys($awcStates) as $s) { $selected = $state == $s ? ' selected' : ; $this->stateOptions .= "<option$selected>$s</option>\n"; }
# Build county options list $county = $this->getOption($user, 'county'); $this->countyOptions = '<option value="">Enter county...</option>'; if ($state) { foreach (split(',', $awcStates[$state]) as $c) { $c = substr($c, 1, -1); $selected = $county == $c ? ' selected' : ; $this->countyOptions .= "<option$selected>$c</option>\n"; } } }
/** * Get an option from the passed user or array (or it can be false) */ function getOption(&$obj, $opt, $default = ) { if (is_object($obj)) return $obj->getOption($opt); if (is_array($obj) && array_key_exists($opt, $obj)) return $obj[$opt]; return $default; }
/** * Hack to add onSubmit to form for validation */ function onSpecialPageExecuteAfterPage(&$special, $par, $func) { if ($special->mName == 'Preferences') { global $wgOut; $wgOut->mBodytext = str_replace('<form', '<form onsubmit="return awcValidate(this)"', $wgOut->mBodytext); } return true; }
/** * Add the new prefs to a new tab in the preferences form */ function onRenderPreferencesForm(&$form, &$out) { $out->addHTML('<fieldset><legend>'.wfMsg('awc-preftab').'</legend>'.wfMsg('awc-prefmsg').$this->renderAWCprefs().'</fieldset>'); return true; }
/** * Add the new prefs to the "create new account" form */ function onUserCreateForm(&$template) { $template->data['header'] = $this->renderAWCprefs(); return true; }
/** * Update the user object when the prefs from the form are saved */ function onSavePreferences(&$form, &$user) { $this->setOptions($user); return true; }
/** * Update the user object with extra prefs in the account-creation form */ function onAbortNewAccount(&$user, &$error) { $this->setOptions($user); return true; }
/** * Set user options from posted form */ function setOptions(&$user) { global $wgRequest; $user->setOption('firstname', $wgRequest->getVal('wpFirstName')); $user->setOption('lastname', $wgRequest->getVal('wpLastName')); $user->setOption('phone', $wgRequest->getVal('wpPhone')); $user->setOption('mobile', $wgRequest->getVal('wpMobile')); $user->setOption('business', $wgRequest->getVal('wpBusiness')); $user->setOption('skills', $wgRequest->getVal('wpSkills')); $user->setOption('website', $wgRequest->getVal('wpWebsite')); $user->setOption('fax', $wgRequest->getVal('wpFax')); $user->setOption('address', $wgRequest->getVal('wpAddress')); $user->setOption('address2', $wgRequest->getVal('wpAddress2')); $user->setOption('city', $wgRequest->getVal('wpCity')); $user->setOption('state', $wgRequest->getVal('wpState')); $user->setOption('county', $wgRequest->getVal('wpCounty')); $user->setOption('zipcode', $wgRequest->getVal('wpZipCode')); $user->setOption('logo', $wgRequest->getVal('wpLogo')); $user->setOption('notes', $wgRequest->getVal('wpNotes')); }
/** * Return the HTML for the AWC preference inputs */ function renderAWCprefs() { global $wgUser, $wgOut, $wgHooks; $this->buildOptionLists($wgUser); $wgOut->addScript("<script type='text/javascript'>{$this->JS}</script>");
$html = "
$this->addRow( wfLabel('First Name', 'wpFirstName'), wfInput('wpFirstName', 20, $wgUser->getOption('firstname'), array('id' => 'wpFirstName')) ) . $this->addRow( wfLabel('Last Name', 'wpLastName'), wfInput('wpLastName', 20, $wgUser->getOption('lastname'), array('id' => 'wpLastName')) ) . $this->addRow( wfLabel('Voice Phone', 'wpPhone'), wfInput('wpPhone', 10, $wgUser->getOption('phone'), array('id' => 'wpPhone')) ) . $this->addRow( wfLabel('Mobile Phone', 'wpMobile'), wfInput('wpMobile', 10, $wgUser->getOption('mobile'), array('id' => 'wpMobile')) ) . $this->addRow( wfLabel('Fax', 'wpFax'), wfInput('wpFax', 10, $wgUser->getOption('fax'), array('id' => 'wpFax')) ) . $this->addRow( wfLabel('Expertise', 'wpSkills'), '<select name="wpSkills" id="wpSkills">'.$this->skillsOptions.'</select>' ) . $this->addRow( wfLabel('Business', 'wpBusiness'), wfInput('wpBusiness', 20, $wgUser->getOption('business'), array('id' => 'wpBusiness')) ) . $this->addRow( wfLabel('Website', 'wpWebsite'), wfInput('wpWebsite', 20, $wgUser->getOption('website'), array('id' => 'wpWebsite')) ) . $this->addRow( wfLabel('Address', 'wpAddress'), wfInput('wpAddress', 20, $wgUser->getOption('address'), array('id' => 'wpAddress')) ) . $this->addRow( wfLabel('Address 2', 'wpAddress2'), wfInput('wpAddress2', 20, $wgUser->getOption('address2'), array('id' => 'wpAddress2')) ) . $this->addRow( wfLabel('City', 'wpCity'), wfInput('wpCity', 20, $wgUser->getOption('city'), array('id' => 'wpCity')) ) . $this->addRow( wfLabel('State', 'wpState'), '<select name="wpState" id="wpState" onchange="awcUpdateCounty(this.value)">'.$this->stateOptions.'</select>', 'Required' ) . $this->addRow( wfLabel('County', 'wpCounty'), '<select name="wpCounty" id="wpCounty">'.$this->countyOptions.'</select>', 'Required' ) . $this->addRow( wfLabel('Zip Code', 'wpZipCode'), wfInput('wpZipCode', 10, $wgUser->getOption('zipcode'), array('id' => 'wpZipCode')) ) . $this->addRow( wfLabel('Logo', 'wpLogo'), wfInput('wpLogo', 10, $wgUser->getOption('logo'), array('id' => 'wpLogo', 'type' => 'file')) ) . $this->addRow(wfLabel('Additional information', 'wpNotes'), )
. '' . '<textarea cols=30 rows="7" id="wpNotes" name="wpNotes">'.$wgUser->getOption('notes').'</textarea> |
';
$html .= '<script type="text/javascript">awcAddValidation()</script>'; return $html; }
function addRow($td1, $td2, $td3 = ) {
return " $td1: $td2
";
}
/**
* Render a link to Special:AWCForum
*/
function forumTag($text, $argv, &$parser) {
# Default args for a search link
$defaults = array(
'action' => 'search/s_form',
'full' => 'full',
'Sis_like' => 'like',
'kw' => 'default',
's_memposts' => 'on',
'is_like' => 'is',
'memname' => ,
'fID' => array('all')
);
# Build query string from defaults and tag args
$qs = array();
foreach ($argv as $k => $v) {
if (!in_array($k, array_keys($defaults))) $qs[] = "awcsbp_$k=$v";
}
if (count($qs)) $argv['memname'] = 'dummy';
foreach ($defaults as $k => $v) {
if (in_array($k, array_keys($argv))) $v = $argv[$k];
if (is_array($v)) foreach ($v as $i) $qs[] = "{$k}[]=$i"; else $qs[] = "$k=$v";
}
$qs = join('&', $qs);
# Build link
$url = Title::newFromText('AWCForum', NS_SPECIAL)->getLocalURL($qs);
$link = "<a href='$url'>$text</a>";
return $link;
}
/**
* Render a link to Special:AWCProfile
*/
function profileTag($text, $argv, &$parser) {
$qs = array();
$qs = join('&', $qs);
$url = Title::newFromText('ProfileSearch', NS_SPECIAL)->getLocalURL($qs);
return $link;
}
/**
* Patch the SQL used by the search to filter by user properties
*/
function patchSQL(&$sql) {
# Convert the prefs to an SQL condition statement for selecting users
$cond = array();
foreach ($this->searchPrefs as $k => $v) $cond[] = "(user_options REGEXP('$k=$v'))";
if (count($cond)) {
# Convert the resulting users to an SQL condition statement for the AWC query
$dbr = wfGetDB(DB_SLAVE);
$tbl = $dbr->tableName('user');
$res = $dbr->select($tbl, 'user_name', join(' AND ', $cond));
$cond = array();
while ($row = $dbr->fetchRow($res)) $cond[] = "p.p_user='$row[0]'";
$dbr->freeResult($res);
# Replace the default user condition in the SQL with the new one
if (count($cond)) {
$sql = preg_replace("|p.p_user\\s=\\s*'dummy'|", '('.join(' OR ', $cond).')', $sql);
$sql = preg_replace("|'%default%'|", "'%%'", $sql);
}
}
}
/**
* Updates passed LoadBalancer's DB servers to secure class
*/
static function updateLB(&$lb) {
$lb->closeAll();
foreach ($lb->mServers as $i => $server) $lb->mServers[$i]['type'] = 'AWC';
}
/**
* Hack to ensure proper search class is used
* - $wgDBtype determines search class unless already defined in $wgSearchType
* - just copied method from SearchEngine::create()
*/
static function fixSearchType() {
global $wgDBtype, $wgSearchType;
if ($wgSearchType) return;
elseif ($wgDBtype == 'mysql') $wgSearchType = 'SearchMySQL4';
elseif ($wgDBtype == 'postgres') $wgSearchType = 'SearchPostgres';
elseif ($wgDBtype == 'oracle') $wgSearchType = 'SearchOracle';
else $wgSearchType = 'SearchEngineDummy';
}
}
/**
* Hook in to DB class to allow overriding the AWC query (based on SimpleSecurity method)
*/
function wfAWCmodDBHook() {
global $wgDBtype, $awcDBHook, $wgOldDBtype;
$oldClass = ucfirst($wgDBtype);
$wgDBtype = 'AWC';
eval("class Database{$wgDBtype} extends Database{$oldClass}".' {
public function query($sql, $fname = "", $tempIgnore = false) {
#print $sql;
global $wgAWCmod;
if (is_object($wgAWCmod) && (preg_match("|^SELECT.+?FROM.+?WHERE.+?p.p_user\\s*=\\s*\'dummy\'|s",$sql)))
$wgAWCmod->patchSQL($sql);
return parent::query($sql, $fname, $tempIgnore);
}
}');
$awcDBHook = false;
}
/**
* Define a new class based on the SpecialPage class
*/
class SpecialProfileSearch extends SpecialPage {
var $posted = array();
var $results = array();
function __construct() {
SpecialPage::SpecialPage(
'ProfileSearch', # name as seen in links etc
false, # user rights required
true, # listed in special:specialpages
false, # function called by execute() - defaults to wfSpecial{$name}
false, # file included by execute() - defaults to Special{$name}.php, only used if no function
false # includable
);
}
/**
* Override SpecialPage::execute()
*/
function execute($param) {
global $wgAWCmod, $wgOut, $wgRequest;
$this->setHeaders();
$title = Title::makeTitle(NS_SPECIAL, 'ProfileSearch');
# Get any posted/getted values and add to object->posted array
$this->posted = array(
'skills' => $wgRequest->getText('wpSkills'),
'city' => $wgRequest->getText('wpCity'),
'state' => $wgRequest->getText('wpState'),
'county' => $wgRequest->getText('wpCounty')
);
# Render form including any posted vals
$wgAWCmod->buildOptionLists($this->posted);
$wgOut->addScript("<script type='text/javascript'>{$wgAWCmod->JS}</script>");
$wgOut->addWikiText(wfMsg('awc-searchmsg'));
$wgOut->addHTML('
'
.wfElement('form', array('class' => 'profilesearch', 'action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null)
.'City: '.wfInput('wpCity', 20, $this->posted['city'], array('id' => 'wpCity')).' '
.'State: <select name="wpState" id="wpState" onchange="awcUpdateCounty(this.value)">'.$wgAWCmod->stateOptions.'</select> '
.'County: <select name="wpCounty" id="wpCounty">'.$wgAWCmod->countyOptions.'</select> '
.wfElement('input', array('type' => 'submit', 'name' => 'wpFind', 'value' => "Search"))
.'</form>
'
);
# Perform query and render results if any posted vals
if (count($this->posted)) {
# Convert the prefs to an SQL condition statement for selecting users
$cond = array();
foreach ($this->posted as $k => $v) if ($v) $cond[] = "(user_options REGEXP('$k=$v'))";
# Extract the fields for each user from the database
$dbr = wfGetDB(DB_SLAVE);
$tbl = $dbr->tableName('user');
$res = $dbr->select($tbl, 'user_id', join(' AND ', $cond));
while ($row = $dbr->fetchRow($res)) {
$user = User::newFromId($row[0]);
$this->results[] = array(
'First Name' => $user->getOption('firstname'),
'Last Name' => $user->getOption('lastname'),
'Expertise' => $user->getOption('skills'),
'Business' => $user->getOption('business'),
'City' => $user->getOption('city'),
'State' => $user->getOption('state'),
'County' => $user->getOption('county')
);
}
$dbr->freeResult($res);
# Render results if any
if (count($this->results)) {
$title = Title::makeTitle(NS_SPECIAL, 'ProfileSearch');
$head = join('', array_keys($this->results[0]));
$wgOut->addHTML("
$head | |
---|---|
".join(" | ", $row)." |
\n");
} else $wgOut->addHTML('No results to display'); } } }
function wfSetupAWCmod() { global $wgAWCmod, $awcDBHook, $wgLanguageCode, $wgMessageCache, $wgLoadBalancer, $wgDBtype, $wgOldDBtype;
# Add the messages used by the specialpage
if ($wgLanguageCode == 'en') {
$wgMessageCache->addMessages(array(
'profilesearch' => "Search by profile",
'awc-preftab' => "Extended Profile",
'awc-prefmsg' => "
Client supplied explanation of this tab goes here.
Change at end of the AWCmod.php script, remember to update the source
<a href='http://www.organicdesign.co.nz/Extensions:AWCmod'>here</a>.
",
'awc-searchmsg' => "\n\nClient supplied explanation of this page goes here.\n
Change at end of the AWCmod.php script, remember to update the source
here.\n\n"
));
}
# Add the specialpage to the environment SpecialPage::addPage(new SpecialProfileSearch());
# Instantiate the AWC singleton now that the environment is prepared $wgAWCmod = new AWCmod();
if ($awcDBHook) {
# If the DB hook couldn't be set up early, do it now # - but now the LoadBalancer exists and must have its DB types changed wfAWCmodDBHook(); if (function_exists('wfGetLBFactory')) wfGetLBFactory()->forEachLB(array('AWCmod', 'updateLB')); elseif (is_object($wgLoadBalancer)) AWCmod::updateLB($wgLoadBalancer); else die("Can't hook in to Database class!");
# Request a DB connection to ensure the LoadBalancer is initialised, # then change back to old DBtype since it won't be used for making connections again but can affect other operations # such as $wgContLang->stripForSearch which is called by SearchMySQL::parseQuery wfGetDB(); $wgDBtype = $wgOldDBtype; }
}