science-ation/user.inc.php

648 lines
18 KiB
PHP

<?
/*
This file is part of the 'Science Fair In A Box' project
SFIAB Website: http://www.sfiab.ca
Copyright (C) 2005 Sci-Tech Ontario Inc <info@scitechontario.org>
Copyright (C) 2005 James Grant <james@lightbox.org>
Copyright (C) 2007 David Grant <dave@lightbox.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
?>
<?
function user_valid_role($role)
{
global $roles;
if(!is_array($role)) $role = array($role);
foreach($role as $r) {
if(!array_key_exists($r, $roles)) return false;
}
return true;
}
function user_load($users_id, $accounts_id = false)
{
$query = "SELECT * FROM users WHERE ";
if($accounts_id != false) {
$accounts_id = intval($accounts_id);
$query .= "`users`.`accounts_id`='$accounts_id' ORDER BY `users`.`year` DESC LIMIT 1";
} else {
$id = intval($users_id);
$query .= " `users`.`id`='$id'";
}
$q=mysql_query($query);
if(mysql_num_rows($q) == 0)
return false;
if(mysql_num_rows($q) > 1) {
echo "ERROR: More than one user.\n";
exit;
}
/* Load the user */
$ret = mysql_fetch_assoc($q);
/* Sanitize before using these in mysql queries */
$ret['id'] = intval($ret['id']);
$ret['accounts_id'] = intval($ret['accounts_id']);
$ret['year'] = intval($ret['year']);
/* Get roles, and active/complete status for each role */
$query = "SELECT * FROM user_roles LEFT JOIN roles ON roles.id=user_roles.roles_id WHERE user_roles.users_id={$ret['id']}";
$q = mysql_query($query);
$ret['roles'] = array();
while(($roledata = mysql_fetch_assoc($q))) {
$t = $roledata['type'];
$ret['roles'][$t] = $roledata;
}
if(count($ret['roles']) == 0) {
/* No roles, that's ok actually, the previous logic here was that
* a user without roles is deleted.. but.. this could happen for
* new users, or if someone deletes all their roles before adding
* a new role */
}
/* Convenience */
$ret['name'] = ($ret['firstname'] ? "{$ret['firstname']} " : '').$ret['lastname'];
/* Email recipient for "to" field on emails */
if( ($ret['firstname'] || $ret['lastname']) && $ret['email']) {
//use their full name if we have it
//if the name contains anything non-standard, we need to quote it.
if(eregi("[^a-z0-9 ]",$ret['name']))
$ret['emailrecipient']="\"{$ret['name']}\" <{$ret['email']}>";
else
$ret['emailrecipient']="{$ret['name']} <{$ret['email']}>";
}
else if($ret['email']) {
//otherwise, just their email address
$ret['emailrecipient']=$ret['email'];
}
else {
$ret['emailrecipient']="";
}
foreach(array_keys($ret['roles']) as $r) {
/* Do the load routines inline, the explosion of user roles
* means it's just silly to have a different function for each
* one. If we get one that has a crazy amount of stuff to do,
* we could move it all to a function and call it in the
* switch below */
switch($r) {
case 'committee':
$u['ord'] = intval($u['ord']);
$u['displayemail'] = ($u['displayemail'] == 'yes') ? 'yes' : 'no';
break;
case 'judge':
$u['years_school'] = intval($u['years_school']);
$u['years_regional'] = intval($u['years_regional']);
$u['years_national'] = intval($u['years_national']);
$u['willing_chair'] = ($u['willing_chair'] == 'yes') ? 'yes' : 'no';
$u['special_award_only'] = ($u['special_award_only'] == 'yes') ? 'yes' : 'no';
$u['cat_prefs'] = strlen($u['cat_prefs'] > 0) ? unserialize($u['cat_prefs']) : array();
$u['div_prefs'] = strlen($u['div_prefs'] > 0) ? unserialize($u['div_prefs']) : array();
$u['divsub_prefs'] = strlen($u['divsub_prefs'] > 0) ? unserialize($u['divsub_prefs']) : array();
// $u['expertise_other'] = $u['expertise_other'];
$u['languages'] = strlen($u['languages'] > 0) ? unserialize($u['languages']) : array();
// $u['highest_psd'] = $u['highest_psd'];
/* Sanity check the arrays, make sure they are arrays */
$should_be_arrays = array('cat_prefs','div_prefs',
'divsub_prefs','languages');
foreach($should_be_arrays as $k) {
if(!is_array($u[$k])) $u[$k] = array();
}
break;
case 'sponsor':
$u['sponsors_id'] = intval($u['sponsors_id']);
if($u['sponsors_id']) {
$q=mysql_query("SELECT * FROM sponsors WHERE id='{$u['sponsors_id']}'");
$u['sponsor']=mysql_fetch_assoc($q);
}
break;
default:
/* Nothing to do for all other roles */
break;
}
}
/* Do this assignment without recursion :) */
unset($ret['orig']);
$orig = $ret;
$ret['orig'] = $orig;
return $ret;
}
function user_load_by_accounts_id($accounts_id)
{
return user_load(0, $accounts_id);
}
function user_load_by_email($email)
{
/* Find the accounts_id for the email, regardless of deleted status */
$e = mysql_real_escape_string($email);
$q = mysql_query("SELECT accounts_id FROM users WHERE email='$e' OR username='$e'");
if(mysql_num_rows($q) == 1) {
$i = mysql_fetch_assoc($q);
return user_load_by_accounts_id($i['accounts_id']);
}
return false;
}
/* FIXME: these are going to need conference IDs too */
function user_load_by_accounts_id_year($uid, $year)
{
echo "FIXME";
exit;
$q = mysql_query("SELECT id FROM users WHERE uid='$uid' AND year <= '$year'");
if(!mysql_num_rows($q)) return false;
$i = mysql_fetch_assoc($q);
return user_load($i['id']);
}
function user_save(&$u)
{
global $conference;
global $roles;
/* Sanity check */
if($u['conferences_id'] != $u['orig']['conferences_id']) {
echo "The user's conference changed. Can't save a user to a difference conference, use user_dupe to copy the user to a new conference.\n";
exit;
}
/* Add any new roles */
$orig_roles = array_keys($u['orig']['roles']);
$new_roles = array_keys($u['roles']);
$added = array_diff($new_roles, $orig_roles);
foreach($added as $r) {
if(!user_add_role_allowed($r, $u)) {
echo "HALT: user can't add this roles";
exit;
}
mysql_query("INSERT INTO user_roles(accounts_id,users_id,roles_id,active,complete)
VALUES('{$u['accounts_id']}','{$u['id']}','{$roles[$r]['id']}','no','no')");
}
/* Delete any removed roles */
$removed = array_diff($orig_roles, $new_roles);
foreach($removed as $r) {
mysql_query("DELETE FROM user_roles WHERE users_id='{$u['id']}' AND roles_id='{$roles[$r]['id']}'");
}
$fields = array('salutation','firstname','lastname','username',
'phonehome','phonework','phonecell','fax','organization',
'address','address2','city','province','postalcode','sex',
'firstaid', 'cpr', 'lang', 'notes');
$fields_for_role['committee'] = array('emailprivate','ord','displayemail',
'access_admin','access_config','access_super');
$fields_for_role['judge'] = array('years_school','years_regional','years_national',
'willing_chair','special_award_only',
'cat_prefs','div_prefs','divsub_prefs',
'expertise_other','languages', 'highest_psd');
$fields_for_role['student'] = array('schools_id');
$fields_for_role['fair'] = array('fairs_id');
$fields_for_role['sponsor'] = array('sponsors_id','primary','position');
/* Merge fields as necessary, build a big list of fields to save */
foreach($new_roles as $r) {
$fields = array_merge($fields, $fields_for_role[$r]);
}
$set = "";
foreach($fields as $f) {
if($u[$f] == $u['orig'][$f]) continue;
if($set != "") $set .=',';
if($u[$f] == NULL) {
$set .= "$f=NULL";
continue;
}
if(is_array($u[$f]))
$data = mysql_escape_string(serialize($u[$f]));
else
$data = mysql_escape_string(stripslashes($u[$f]));
$set .= "$f='$data'";
}
if($set != "") {
$query = "UPDATE users SET $set WHERE id='{$u['id']}'";
mysql_query($query);
echo mysql_error();
}
/* Record all the data in orig that we saved so subsequent
* calls to user_save don't try to overwrite data already
* saved to the database */
unset($u['orig']);
$orig = $u;
$u['orig'] = $orig;
// print_r($u);
}
function user_remove_role(&$u, $role)
{
if(!array_key_exists($role, $u['roles'])) {
/* Hum, type specified, but the user is not this type,
* so, i guess we're done. */
return;
}
$id = $u['id'];
/* Do role-specific remove actions */
switch($role) {
case 'committee':
mysql_query("DELETE FROM committees_link WHERE users_uid='{$u['accounts_id']}'");
break;
case 'judge':
mysql_query("DELETE FROM judges_teams_link WHERE users_id='$id'");
mysql_query("DELETE FROM judges_specialawards_sel WHERE users_id='$id'");
break;
default:
break;
}
/* Delete the role */
unset($u['roles'][$role]);
/* Save this user (takes care of removing entries from the user_roles db) */
user_save($u);
}
/* If role is specified, just delete the role from the user.
* If not, delete the whole user, all roles */
function user_delete($u, $role=false)
{
$finish_delete = false;
if(!is_array($u)) {
$u = user_load($u);
}
if($role != false) {
user_remove_role($u, $role);
if(count($u['roles']) == 0) {
/* No roles left, finish the delete */
$finish_delete = true;
}
} else {
/* Delete the whole user, every role */
foreach(array_keys($u['roles']) as $r)
user_remove_role($u, $r);
$finish_delete = true;
}
if($finish_delete) {
mysql_query("UPDATE users SET deleted='yes', deleteddatetime=NOW() WHERE id='{$u['id']}'");
return true;
}
/* User had some other role, so delete was not completed. */
return false;
}
/* Purge functions. These completely eliminate all traces of a user from the
* database. This action cannot be undone. We prefer the committee to use the
* "delete" functions, which simply mark the account as "deleted". */
function user_purge($u, $role=false)
{
/* Delete the user, then completely delete them from
* the DB if delete returns true, that is, if there's
* no other role blocking the delete/purge */
$finish_purge = user_delete($u, $role);
if($finish_purge == true) {
mysql_query("DELETE FROM users WHERE id='{$u['id']}'");
return true;
}
/* Not purged, some other role existed */
return false;
}
/* Duplicate a row in the users table, or any one of the users_* tables. */
function user_dupe_row($db, $key, $val, $newval)
{
global $config;
$nullfields = array('deleteddatetime'); /* Fields that can be null */
$q = mysql_query("SELECT * FROM $db WHERE $key='$val'");
if(mysql_num_rows($q) != 1) {
echo "ERROR duplicating row in $db: $key=$val NOT FOUND.\n";
exit;
}
$i = mysql_fetch_assoc($q);
$i[$key] = $newval;
foreach($i as $k=>$v) {
if($v == NULL && in_array($k, $nullfields))
$i[$k] = 'NULL';
else if($k == 'year')
$i[$k] = $config['FAIRYEAR'];
else
$i[$k] = '\''.mysql_escape_string($v).'\'';
}
$keys = '`'.join('`,`', array_keys($i)).'`';
$vals = join(',', array_values($i));
$q = "INSERT INTO $db ($keys) VALUES ($vals)";
// echo "Dupe Query: [$q]";
$r = mysql_query($q);
echo mysql_error();
$id = mysql_insert_id();
return $id;
}
/* Used by the login scripts to copy one user from one year to another */
function user_dupe($u, $new_year)
{
/* Dupe a user if:
* - They don't exist in the current year
* (users->year != the target year (passed in so we can use it in the rollover script) )
* - They have a previous year entry
* (users->year DESC LIMIT 1 == 1 row)
* - That previous entry has deleted=no */
/* Find the last entry */
$q = mysql_query("SELECT id,uid,year,deleted FROM users WHERE uid='{$u['uid']}'
ORDER BY year DESC LIMIT 1");
$r = mysql_fetch_object($q);
if($r->deleted == 'yes') {
echo "Cannot duplicate user ID {$u['id']}, they are deleted. Undelete them first.\n";
exit;
}
if($r->year == $new_year) {
echo "Cannot duplicate user ID {$u['id']}, they already exist in year $new_year\n";
exit;
}
$id = user_dupe_row('users', 'id', $u['id'], NULL);
$q = mysql_query("UPDATE users SET year='$new_year' WHERE id='$id'");
/* Load the new user */
$u2 = user_load($id);
foreach($u2['types'] as $t) {
user_dupe_row("users_$t", 'users_id', $u['id'], $id);
}
/* Return the ID of the new user */
return $id;
}
/* Returns true if loaded user ($u) is allowed to add role $role to their
* profile. THis is intended as a last-stop mechanism, preventing, for example
* a student from co-existing with any other role . */
function user_add_role_allowed(&$u, $role)
{
foreach(array_keys($u['roles']) as $ur) {
switch($ur) {
case 'student':
/* Student cant' add any other role */
return false;
default:
if($role == 'student') {
/* No role can add the student role */
return false;
}
/* All other roles can coexist (even the fair role) */
break;
}
}
return true;
}
function user_add_role(&$u, $role)
{
if(!user_add_role_allowed($u, $role)) {
/* If we get in here, someone is hand crafting URLs */
echo "HALT: invalid role add specified for operation.";
exit;
}
/* Add the role */
$u['roles'][$role] = array('active' =>'yes',
'complete' => 'no');
/* Save it now so the DB gets updated */
user_save($u);
}
function user_create($accounts_id, $conferences_id=0)
{
global $config, $conference;
if($conferences_id == 0) $conferences_id = $conference['id'];
/* Make sure the user doesn't already exist */
$q = mysql_query("SELECT id FROM users WHERE accounts_id='$accounts_id' AND conferences_id='$conferences_id'");
if(mysql_num_rows($q)) {
echo "ERROR: user_create called for a user that already exists.\n";
exit;
}
/* Create the user */
mysql_query("INSERT INTO users(`accounts_id`,`conferences_id`) VALUES('$accounts_id','$conferences_id')");
$id = mysql_insert_id();
/* Return a loaded user with no roles */
return user_load($id);
}
/* Perform some checks. Make sure the person is logged in, and that their
* password hasn't expired (the password_expired var is set in the login page)
*/
function user_auth_required($all_required, $one_required = array())
{
global $config;
$ok = true;
unset($_SESSION['request_uri']);
if(!isset($_SESSION['roles'])) {
message_push(error(i18n("You must login to view that page")));
$_SESSION['request_uri'] = $_SERVER['REQUEST_URI'];
header("location: {$config['SFIABDIRECTORY']}/user_login.php?type=$type");
exit;
}
/* Make sure the user has each role in $all_required, this returns
* an array in the same order as $all_required, with all members
* in $all_required that are also in the session roles */
if(!is_array($all_required)) $all_required = array($all_required);
$match = array_intersect($all_required, $_SESSION['roles']);
if($all_required != $match) {
/* Something is missing */
$ok = false;
}
/* Make sure the user has one role in $one_required */
if(!is_array($one_required)) $one_required = array($one_required);
if(count($one_required)) {
$match = array_intersect($one_required, $_SESSION['roles']);
if(count($match) == 0) {
/* Missing any role in $one_required */
$ok = false;
}
}
if(!$ok) {
message_push(error(i18n("You do not have permission to view that page")));
header("location: {$config['SFIABDIRECTORY']}/user_login.php");
exit;
}
/* Forward to password expired, remember the target URI */
if($_SESSION['password_expired'] == true) {
$_SESSION['request_uri'] = $_SERVER['REQUEST_URI'];
header("location: {$config['SFIABDIRECTORY']}/user_password.php");
exit;
}
/* Return the first role that matched, this retains the previous
* behaviour */
return $match[0];
}
function user_volunteer_registration_status()
{
global $config;
// $now = date('Y-m-d H:i:s');
// if($now < $config['dates']['judgeregopen']) return "notopenyet";
// if($now > $config['dates']['judgeregclose']) return "closed";
return "open";
}
function user_judge_registration_status()
{
global $config;
$now = date('Y-m-d H:i:s');
if($now < $config['dates']['judgeregopen']) return "notopenyet";
if($now > $config['dates']['judgeregclose']) return "closed";
return "open";
}
$user_personal_fields_map = array(
'salutation' => array('salutation'),
'name' => array('firstname','lastname'),
'email' => array('email'),
'sex' => array('sex'),
'phonehome' => array('phonehome'),
'phonework' => array('phonework'),
'phonecell' => array('phonecell'),
'fax' => array('fax'),
'org' => array('organization'),
'birthdate' => array('birthdate'),
'lang' => array('lang'),
'address' => array('address', 'address2', 'postalcode'),
'city' => array('city'),
'province' => array('province'),
'firstaid' => array('firstaid','cpr'));
function user_personal_fields($role)
{
global $config, $user_personal_fields_map;
$ret = array('firstname','lastname','email');
$fields = $config["{$role}_personal_fields"];
if($fields != '') {
$fields = split(',', $fields);
foreach($fields as $f) {
$ret = array_merge($ret, $user_personal_fields_map[$f]);
}
}
return $ret;
}
function user_personal_required_fields($role)
{
global $config, $user_personal_fields_map;
$ret = array('firstname','lastname','email');
$required = $config["{$role}_personal_required"];
if($required != '') {
$fields = split(',', $required);
foreach($fields as $f) {
$ret = array_merge($ret, $user_personal_fields_map[$f]);
}
}
/* Filter some elements that are never required.
* - address2
*/
$ret = array_diff($ret, array('address2'));
return $ret;
}
function user_personal_info_status(&$u)
{
$required = array();
foreach(array_keys($u['roles']) as $r) {
$required = array_merge($required,
user_personal_required_fields($r));
}
foreach($required as $r) {
$val = trim($u[$r]);
if(strlen($val) > 0) {
/* Ok */
} else {
return 'incomplete';
}
}
/* FIXME: somehow call the $role _status_update() function to update
* the individual [$role]['complete'] entry? */
return 'complete';
}
/* user_{$role}_login() is called with a full $u loaded */
function user_fair_login($u)
{
/* Double check, make sure the user is of this role */
if(!array_key_exists('fair', $u['roles'])) {
echo "ERROR: attempted to login fair on a non-fair user\n";
exit;
}
$_SESSION['fairs_id'] = $u['fairs_id'];// == 'yes') ? true : false;
}
?>