Difference between revisions of "Extension:AWCmod"
(add the new tag and special page) |
m (few more client mandated mods...should be a wrap...) |
||
(9 intermediate revisions by 2 users not shown) | |||
Line 10: | Line 10: | ||
if (!defined('MEDIAWIKI')) die('Not an entry point.'); | if (!defined('MEDIAWIKI')) die('Not an entry point.'); | ||
− | define('AWCMOD_VERSION', '0. | + | define('AWCMOD_VERSION', '0.2.7, 2008-11-06'); |
+ | |||
+ | $wgAutoConfirmCount = 10^10; | ||
+ | |||
+ | $awcDefaultImage = ''; | ||
+ | $awcMaxImageSize = 100000; | ||
$wgExtensionFunctions[] = 'wfSetupAWCmod'; | $wgExtensionFunctions[] = 'wfSetupAWCmod'; | ||
− | $wgExtensionCredits['other'][] = array( | + | $wgExtensionCredits['other'][] = $wgExtensionCredits['specialpage'][] = array( |
'name' => 'AWCmod', | 'name' => 'AWCmod', | ||
'author' => '[http://www.organicdesign.co.nz/User:Nad User:Nad]', | 'author' => '[http://www.organicdesign.co.nz/User:Nad User:Nad]', | ||
'description' => 'Custom extension for Adeft', | 'description' => 'Custom extension for Adeft', | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
'url' => 'http://www.organicdesign.co.nz/Extension:AWCmod', | 'url' => 'http://www.organicdesign.co.nz/Extension:AWCmod', | ||
'version' => AWCMOD_VERSION | 'version' => AWCMOD_VERSION | ||
Line 36: | Line 33: | ||
# Process posted contact details | # Process posted contact details | ||
− | if (isset($_POST['wpFirstName'])) | + | if (isset($_POST['wpFirstName'])) $_POST['wpRealName'] = $_POST['wpFirstName'].' '.$_POST['wpLastName']; |
− | |||
− | |||
# Don't apply the db hook unless awcsearchprefs have been posted | # Don't apply the db hook unless awcsearchprefs have been posted | ||
Line 56: | Line 51: | ||
class AWCmod { | class AWCmod { | ||
+ | var $JS = ''; | ||
var $searchPrefs = array(); | 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; | $this->searchPrefs = $awcSearchByPref; | ||
− | $wgHooks[' | + | #$wgHooks['AbortNewAccount'][] = $this; # Save the extra prefs after posting account-create |
− | #$wgHooks['UserCreateForm'][] | + | #$wgHooks['UserCreateForm'][] = $this; # Modify the create-account form to add new prefs |
− | $wgHooks['SavePreferences'][] = $this; | + | $wgHooks['RenderPreferencesForm'][] = $this; # Add the new pref tab |
− | + | $wgHooks['SavePreferences'][] = $this; # Save the extra posted data to the user object's options | |
− | $wgHooks[' | + | $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 | # Modify login form messages to say email and name compulsory | ||
Line 76: | Line 81: | ||
$wgParser->setHook('forum_search', array($this, 'forumTag')); | $wgParser->setHook('forum_search', array($this, 'forumTag')); | ||
$wgParser->setHook('profile_search', array($this, 'profileTag')); | $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"; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
/** | /** | ||
− | * Hack to add onSubmit to form | + | * 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) { | function onSpecialPageExecuteAfterPage(&$special, $par, $func) { | ||
− | + | global $wgOut; | |
if ($special->mName == 'Preferences') { | if ($special->mName == 'Preferences') { | ||
− | + | $wgOut->mBodytext = str_replace( | |
− | $wgOut->mBodytext = str_replace('<form', '<form onsubmit="return awcValidate(this)"', $wgOut->mBodytext); | + | '<form', |
+ | '<form onsubmit="return awcValidate(this)" enctype="multipart/form-data"', | ||
+ | $wgOut->mBodytext | ||
+ | ); | ||
} | } | ||
return true; | return true; | ||
Line 94: | Line 160: | ||
*/ | */ | ||
function onRenderPreferencesForm(&$form, &$out) { | function onRenderPreferencesForm(&$form, &$out) { | ||
− | $out->addHTML('<fieldset><legend> | + | $out->addHTML('<fieldset><legend>'.wfMsg('awc-preftab').'</legend>'.wfMsg('awc-prefmsg').$this->renderAWCprefs().'</fieldset>'); |
return true; | return true; | ||
} | } | ||
Line 141: | Line 207: | ||
$user->setOption('county', $wgRequest->getVal('wpCounty')); | $user->setOption('county', $wgRequest->getVal('wpCounty')); | ||
$user->setOption('zipcode', $wgRequest->getVal('wpZipCode')); | $user->setOption('zipcode', $wgRequest->getVal('wpZipCode')); | ||
− | $user->setOption('logo', $wgRequest->getVal(' | + | $user->setOption('logo', $wgRequest->getVal('wpProfileImage')); |
$user->setOption('notes', $wgRequest->getVal('wpNotes')); | $user->setOption('notes', $wgRequest->getVal('wpNotes')); | ||
} | } | ||
Line 150: | Line 216: | ||
function renderAWCprefs() { | function renderAWCprefs() { | ||
global $wgUser, $wgOut, $wgHooks; | global $wgUser, $wgOut, $wgHooks; | ||
− | + | $this->buildOptionLists($wgUser); | |
− | $wgOut->addScript("<script type='text/javascript'>$ | + | $wgOut->addScript("<script type='text/javascript'>{$this->JS}</script>"); |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
$html = "<div class=awc-loginprefs><table>" . | $html = "<div class=awc-loginprefs><table>" . | ||
Line 211: | Line 242: | ||
$this->addRow( | $this->addRow( | ||
wfLabel('Expertise', 'wpSkills'), | wfLabel('Expertise', 'wpSkills'), | ||
− | '<select name="wpSkills" id="wpSkills">'.$skillsOptions.'</select>' | + | '<select name="wpSkills" id="wpSkills">'.$this->skillsOptions.'</select>' |
) . | ) . | ||
$this->addRow( | $this->addRow( | ||
− | wfLabel('Business', 'wpBusiness'), | + | wfLabel('Business Name', 'wpBusiness'), |
wfInput('wpBusiness', 20, $wgUser->getOption('business'), array('id' => 'wpBusiness')) | wfInput('wpBusiness', 20, $wgUser->getOption('business'), array('id' => 'wpBusiness')) | ||
) . | ) . | ||
Line 235: | Line 266: | ||
$this->addRow( | $this->addRow( | ||
wfLabel('State', 'wpState'), | wfLabel('State', 'wpState'), | ||
− | '<select name="wpState" id="wpState" onchange="awcUpdateCounty(this.value)">'.$stateOptions.'</select>', | + | '<select name="wpState" id="wpState" onchange="awcUpdateCounty(this.value)">'.$this->stateOptions.'</select>', |
'Required' | 'Required' | ||
) . | ) . | ||
$this->addRow( | $this->addRow( | ||
wfLabel('County', 'wpCounty'), | wfLabel('County', 'wpCounty'), | ||
− | '<select name="wpCounty" id="wpCounty">'.$countyOptions.'</select>', | + | '<select name="wpCounty" id="wpCounty">'.$this->countyOptions.'</select>', |
'Required' | 'Required' | ||
) . | ) . | ||
Line 248: | Line 279: | ||
) . | ) . | ||
$this->addRow( | $this->addRow( | ||
− | wfLabel('Logo', ' | + | wfLabel('Logo', 'wpProfileImage'), |
− | wfInput(' | + | wfInput('wpProfileImage', 10, $wgUser->getOption('logo'), array('id' => 'wpProfileImage', 'type' => 'file')) |
) . | ) . | ||
− | $this->addRow(wfLabel('Additional | + | $this->addRow(wfLabel('Additional Information (1000 Characters Max)', 'wpNotes'), '') |
. '<tr><td colspan="3"><textarea cols=30 rows="7" id="wpNotes" name="wpNotes">'.$wgUser->getOption('notes').'</textarea></td></tr>' | . '<tr><td colspan="3"><textarea cols=30 rows="7" id="wpNotes" name="wpNotes">'.$wgUser->getOption('notes').'</textarea></td></tr>' | ||
. '</table></div>'; | . '</table></div>'; | ||
+ | |||
+ | |||
+ | |||
$html .= '<script type="text/javascript">awcAddValidation()</script>'; | $html .= '<script type="text/javascript">awcAddValidation()</script>'; | ||
return $html; | return $html; | ||
} | } | ||
+ | /** | ||
+ | * Add a table row to the form | ||
+ | */ | ||
function addRow($td1, $td2, $td3 = '') { | function addRow($td1, $td2, $td3 = '') { | ||
return "<tr> | return "<tr> | ||
Line 264: | Line 301: | ||
<td><div class='prefsectiontip'><div class='error'>$td3</div></div></td> | <td><div class='prefsectiontip'><div class='error'>$td3</div></div></td> | ||
</tr>"; | </tr>"; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 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 = "<table class='userprofile'><tr align='left'>\n"; | ||
+ | $profile .= "<td rowspan='12' valign='top'><img height='228' src=\"$src\" /></td>\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<th valign='top' align='left'>$k:</th><td valign='top' align='left'>$v</td></tr>\n"; | ||
+ | $tr = "<tr>"; | ||
+ | } | ||
+ | $profile .= "</table>\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 = "<div class='errorbox'>$error</div>"; | ||
+ | else { | ||
+ | $name = preg_replace('%.+(\..+?)$%', "user-{$wgDBname}-{$wgUser->getId()}$1", $file['name']); | ||
+ | move_uploaded_file($file['tmp_name'], "$wgUploadDirectory/$name"); | ||
+ | } | ||
} | } | ||
Line 296: | Line 386: | ||
# Build link | # 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>"; | $link = "<a href='$url'>$text</a>"; | ||
Line 306: | Line 398: | ||
*/ | */ | ||
function profileTag($text, $argv, &$parser) { | function profileTag($text, $argv, &$parser) { | ||
+ | |||
+ | # Build query string from defaults and tag args | ||
$qs = array(); | $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); | $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; | return $link; | ||
} | } | ||
Line 385: | Line 489: | ||
*/ | */ | ||
class SpecialProfileSearch extends SpecialPage { | class SpecialProfileSearch extends SpecialPage { | ||
+ | |||
+ | var $posted = array(); | ||
+ | var $results = array(); | ||
function __construct() { | function __construct() { | ||
Line 402: | Line 509: | ||
*/ | */ | ||
function execute($param) { | function execute($param) { | ||
− | global $wgOut, $wgRequest; | + | global $wgAWCmod, $wgOut, $wgRequest; |
$this->setHeaders(); | $this->setHeaders(); | ||
$title = Title::makeTitle(NS_SPECIAL, 'ProfileSearch'); | $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('<br><table>' | ||
+ | .wfElement('form', array('class' => 'profilesearch', 'action' => $title->getLocalURL('action=submit'), 'method' => 'post'), null) | ||
+ | ."<tr><td>Expertise:</td><td><select name=\"wpExpertise\" id=\"wpExpertise\">{$wgAWCmod->skillsOptions}</select></td></tr>\n" | ||
+ | ."<tr><td>City:</td><td>".wfInput('wpCity', 20, $this->posted['city'], array('id' => 'wpCity'))."</td></tr>\n" | ||
+ | ."<tr><td>State:</td><td><select name=\"wpState\" id=\"wpState\" onchange=\"awcUpdateCounty(this.value)\">{$wgAWCmod->stateOptions}</select></td></tr>\n" | ||
+ | ."<tr><td>County:</td><td><select name=\"wpCounty\" id=\"wpCounty\">{$wgAWCmod->countyOptions}</select></td></tr>\n" | ||
+ | ."<tr><td></td><td>".wfElement('input', array('type' => 'submit', 'name' => 'wpFind', 'value' => "Search"))."</td></tr>\n" | ||
+ | ."</form></table><br>" | ||
+ | ); | ||
+ | |||
+ | # 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('</th><th>', array_keys($this->results[0])); | ||
+ | $wgOut->addHTML("<table class='sortable' id='searchResults'><tr><th>$head</th></tr>\n"); | ||
+ | foreach ($this->results as $row) $wgOut->addHTML("<tr><td>".join("</td><td>", $row)."</td></tr>\n"); | ||
+ | $wgOut->addHTML("</table>\n"); | ||
+ | } else $wgOut->addHTML('<i>No results to display</i>'); | ||
+ | } | ||
} | } | ||
} | } | ||
Line 414: | Line 581: | ||
if ($wgLanguageCode == 'en') { | if ($wgLanguageCode == 'en') { | ||
$wgMessageCache->addMessages(array( | $wgMessageCache->addMessages(array( | ||
− | 'profilesearch' => | + | 'profilesearch' => "Search by profile", |
− | 'awc- | + | 'awc-preftab' => "Extended Profile", |
+ | 'awc-prefmsg' => "<br><b>Let others find you by filling in this information!</b><br>", | ||
+ | 'awc-searchmsg' => "\n\n<b>Search for other Property Tax pros here! If you want to be added, edit your Extended Profile in User Preferences</b>\n\n" | ||
)); | )); | ||
} | } |
Latest revision as of 03:32, 6 November 2008
<?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; }
}