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.7, 2008-11-06');
$wgAutoConfirmCount = 10^10;
$awcDefaultImage = ; $awcMaxImageSize = 100000;
$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, $awcSearchByPref, $wgGroupPermissions, $wgWhitelistRead, $wgRequest;
# 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['AbortNewAccount'][] = $this; # Save the extra prefs after posting account-create #$wgHooks['UserCreateForm'][] = $this; # Modify the create-account form to add new prefs $wgHooks['RenderPreferencesForm'][] = $this; # Add the new pref tab $wgHooks['SavePreferences'][] = $this; # Save the extra posted data to the user object's options $wgHooks['SpecialPageExecuteAfterPage'][] = $this; # Modify preferences forms enctype and onsubmit $wgHooks['OutputPageBeforeHTML'][] = $this; # Add profile info to user pages
# 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'));
# Process an uploaded profile image if one was posted if (array_key_exists('wpProfileImage', $_FILES) && $_FILES['wpProfileImage']['size'] > 0) $this->processUploadedImage($_FILES['wpProfileImage']);
}
/**
* 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 for validation and enctype for image upload to form tag */ function onSpecialPageExecuteAfterPage(&$special, $par, $func) { global $wgOut; if ($special->mName == 'Preferences') { $wgOut->mBodytext = str_replace( '<form', '<form onsubmit="return awcValidate(this)" enctype="multipart/form-data"', $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('wpProfileImage')); $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 Name', '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', 'wpProfileImage'), wfInput('wpProfileImage', 10, $wgUser->getOption('logo'), array('id' => 'wpProfileImage', 'type' => 'file')) ) . $this->addRow(wfLabel('Additional Information (1000 Characters Max)', 'wpNotes'), )
. '' . '<textarea cols=30 rows="7" id="wpNotes" name="wpNotes">'.$wgUser->getOption('notes').'</textarea> |
';
$html .= '<script type="text/javascript">awcAddValidation()</script>'; return $html; }
/** * Add a table row to the form */ function addRow($td1, $td2, $td3 = ) {
return " $td1: $td2
"; } /** * Add the user profile info if it's a user page */ function onOutputPageBeforeHTML(&$out, &$text) { global $wgTitle, $wgDBname, $wgUploadDirectory, $wgUploadPath, $awcDefaultImage; if (is_object($wgTitle) && $wgTitle->getNamespace() == NS_USER) { $user = User::newFromName($wgTitle->getText()); $id = $user->getId(); $m = glob("$wgUploadDirectory/user-$wgDBname-$id.*"); $src = count($m) > 0 ? $m[0] : $awcDefaultImage; $src = preg_replace("%^.+(?=/user-)%", $wgUploadPath, $src); $profile = "
\n"; $profile .= "\n";$tr = ; foreach(array( 'Name' => $user->getRealName(), 'Business' => $user->getOption('business'), 'Website' => $user->getOption('website'), 'Expertise' => $user->getOption('skills'), 'City' => $user->getOption('city'), 'County' => $user->getOption('county'), 'State' => $user->getOption('state'), 'Zip' => $user->getOption('zipcode'), 'Voice' => $user->getOption('phone'), 'Mobile' => $user->getOption('mobile'), 'Fax' => $user->getOption('fax'), 'Info' => $user->getOption('notes') ) as $k => $v) {
$profile .= "$tr\n"; $tr = ""; } $profile .= "<img height='228' src=\"$src\" /> | $k: | $v |
---|---|---|
\n";
$text = $profile.$text; } return true; }
/** * Process uploaded image file */ function processUploadedImage($file) { global $wgUser, $wgDBname, $wgSiteNotice, $wgUploadDirectory, $awcMaxImageSize; $error = false; if (!ereg('^image/(jpeg|png|gif)$', $file['type'])) $error = 'Uploaded file was not of a valid type!'; if ($file['size'] > $awcMaxImageSize) $error = 'Profile images are restricted to a maximum of 100KBytes'; if ($file['error'] > 0) $error = 'Uploaded error number '.$file['error'].' occurred';
if ($error) $wgSiteNotice = "
";
else { $name = preg_replace('%.+(\..+?)$%', "user-{$wgDBname}-{$wgUser->getId()}$1", $file['name']); move_uploaded_file($file['tmp_name'], "$wgUploadDirectory/$name"); } }
/** * 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 $title = Title::newFromText('AWCForum', NS_SPECIAL); $url = $title->getLocalURL($qs); if (empty($text)) $text = $title->getPrefixedText($title); $link = "<a href='$url'>$text</a>";
return $link; }
/** * Render a link to Special:AWCProfile */ function profileTag($text, $argv, &$parser) {
# Build query string from defaults and tag args $qs = array(); foreach ($argv as $k => $v) { $k = ucfirst($k); if ($k == 'Expertise' || $k == 'State' || $k == 'County' || $k == 'City') $qs[] = "wp$k=$v"; } $qs = join('&', $qs);
# Build link $title = Title::newFromText('ProfileSearch', NS_SPECIAL); $url = $title->getLocalURL($qs); if (empty($text)) $text = $title->getPrefixedText($title); $link = "<a href='$url'>$text</a>";
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('wpExpertise'), '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('
Expertise: | <select name=\"wpExpertise\" id=\"wpExpertise\">{$wgAWCmod->skillsOptions}</select> |
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"))." |
"
);
# 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]); $name = $user->getName(); $page = Title::newFromText($name, NS_USER)->getLocalURL(); $this->results[] = array( 'User ID' => "<a href='$page'>$name</a>", '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("
\n"); foreach ($this->results as $row) $wgOut->addHTML("\n"); $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' => "
Let others find you by filling in this information!
",
'awc-searchmsg' => "\n\nSearch for other Property Tax pros here! If you want to be added, edit your Extended Profile in User Preferences\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; }
}