* @copyright 2001-2007 VIKO team and contributors * @license http://www.gnu.org/licenses/gpl.html GPL 2.0 */ /** * This is a child class of TableAbstraction */ require_once 'TableAbstraction.php'; /** * The class contains a school object */ require_once 'School.php'; /** * The class contains a form object */ require_once 'Form.php'; /** * CourseList::getFromTeacher() is used in User::delete method to delete all the courses of teacher */ require_once 'CourseList.php'; /** * When deleting user, all associated materials also have to get deleted */ require_once 'MaterialFactory.php'; /** * The class needs to generate some HTML links */ require_once 'HTML.php'; /** * The class needs an URI service, provided by Module class */ require_once 'Module.php'; /** * Person name in normal format */ define('FIRSTNAME_LASTNAME', 0); /** * Person name in format suitable when in list of sorted names */ define('LASTNAME_FIRSTNAME', 1); /** * Class for all different VIKO users * */ class User extends TableAbstraction { /** * Returns username * * @return string the username */ function getUsername() { return $this->getAttribute("_username"); } /** * Sets username * * @param string $username username */ function setUsername( $username ) { $this->_username = $username; } /** * Sets password of user * * @param string $password plain text password */ function setPassword( $password ) { $this->_password_hash = sha1( $password ); } /** * Returns first name * * @return string first name */ function getFirstName() { return $this->getAttribute("_first_name"); } /** * Sets first name * * @param string $first_name first name */ function setFirstName( $first_name ) { $this->_first_name = $first_name; } /** * Returns last name * * @return string last name */ function getLastName() { return $this->getAttribute("_last_name"); } /** * Sets last name * * @param string $last_name last name */ function setLastName( $last_name ) { $this->_last_name = $last_name; } /** * Returns e-mail address * * @return string e-mail address */ function getEmail() { return $this->getAttribute("_email"); } /** * Sets e-mail address * * @param string $email e-mail address */ function setEmail( $email ) { $this->_email = $email; } /** * Returns groupname * * @return string groupname */ function getGroup() { return $this->getAttribute("_group"); } /** * Sets groupname * * @param string $group groupname */ function setGroup( $group ) { if ( $group == "STUDENT" || $group == "TEACHER" || $group == "SCHOOLADMIN" || $group == "ADMIN" ) { $this->_group = $group; } else { trigger_error( "Unknown group $group.", E_USER_ERROR ); } } /** * Returns form * * @return Form form object */ function getForm() { return $this->getAttribute("_form"); } /** * Sets form * * @param Form $form form object */ function setForm( $form ) { $this->_form = $form; } /** * Returns school * * @return School school object */ function getSchool() { return $this->getAttribute("_school"); } /** * Sets school * * @param School $school school object */ function setSchool( $school ) { $this->_school = $school; } /** * Returns both first and last name of user in single string * * @param int $order should firstname be first or lastname * @return string HTML-safe full name of the user */ function getFullName( $order=FIRSTNAME_LASTNAME ) { $firstname = $this->getFirstName(); $lastname = $this->getLastName(); if ( $order == FIRSTNAME_LASTNAME ) { $name = "$firstname $lastname"; } else { $name = "$lastname $firstname"; } // escape all five special characters: <, >, &, ", ' return htmlspecialchars( $name, ENT_QUOTES ); } /** * Returns link to user info or e-mail with user name as label * * If active user (saved in $_SESSION['user']) is student, * then e-mail link is returned, * otherwise link to /user/#id# page is created. * * @param int $order should firstname be first or lastname * @return string HTML anchor element */ function getUserLink( $order=FIRSTNAME_LASTNAME ) { $name = $this->getFullName( $order ); if ( $_SESSION["user"]->hasAccessToGroup( "SCHOOLADMIN" ) ) { $id = $this->getID(); return HTML::a( Module::URI('user', 'edit', $id), $name ); } elseif ( $_SESSION["user"]->hasAccessToGroup( "TEACHER" ) ) { $id = $this->getID(); return HTML::a( Module::URI("user", "view", $id), $name ); } else { $email = $this->getEmail(); // only create e-mail-link, if user has e-mail address specified if ( $email == "" ) { return $name; } else { return HTML::a( "mailto:$email", $name ); } } } /** * Returns link to user e-mail address * * The label of the link will also be the e-mail address. * If e-mail address is missing, an empty string is returned. * * @return string HTML anchor element */ function getEmailLink() { $email = $this->getEmail(); if ( $email == "" ) { return ""; } else { return HTML::a( "mailto:$email", $email ); } } /** * Returns name of the table containing users */ function getTableName() { return "Users"; } /** * Returns the primary key field name of the users table */ function getIDFieldName() { return "user_id"; } /** * Implementation of reading a database record */ function readFromDatabaseRecord( $record ) { $this->_username = $record['username']; $this->_password_hash = $record['password']; $this->_first_name = $record['firstname']; $this->_last_name = $record['lastname']; $this->_email = $record['email']; $this->_group = $record['user_group']; $this->_school =& new School( $record['school_id'] ); if ( isset($record['form_id']) ) { $this->_form =& new Form( $record['form_id'] ); $this->_form->setSchool( $this->_school ); } } /** * Implementation of composing a database record */ function writeToDatabaseRecord() { // the following properties may not be NULL if ( !isset($this->_username) ) { trigger_error("Username must be specified when saving user.", E_USER_ERROR); } if ( !isset($this->_password_hash) ) { trigger_error("Password must be specified when saving user.", E_USER_ERROR); } if ( !isset($this->_first_name) ) { trigger_error("First name must be specified when saving user.", E_USER_ERROR); } if ( !isset($this->_last_name) ) { trigger_error("Last name must be specified when saving user.", E_USER_ERROR); } if ( !isset($this->_email) ) { trigger_error("E-mail address must be specified when saving user.", E_USER_ERROR); } if ( !isset($this->_group) ) { trigger_error("Group must be specified when saving user.", E_USER_ERROR); } $record = array( "username" => $this->_username, "password" => $this->_password_hash, "firstname" => $this->_first_name, "lastname" => $this->_last_name, "email" => $this->_email, "user_group" => $this->_group ); // user does not need to belong to school - the field may be NULL if ( isset($this->_school) && $this->_school->getID() > 0 ) { $record['school_id'] = $this->_school->getID(); } else { $record['school_id'] = null; } // user does not need to belong to form - the field may be NULL if ( isset($this->_form) && $this->_form->getID() > 0 ) { $record['form_id'] = $this->_form->getID(); } else { $record['form_id'] = null; } return $record; } /** * Returns true, if user is of that group * * @access public * @param string $group groupname * @return boolean true, if user is in group $group */ function belongsToGroup( $group ) { return ($this->_group == $group); } /** * Returns true, if user is of higher or equal group than specified group * * This method should be used to detect if user can view certain material. * For example if only teachers should be able to edit list of courses, * then actually all the higher levels should also be able to do so. * *
     * if ( $_SESSION['user'].hasAccessToGroup('TEACHER') ) {
     *     // this code is executed if user is TEACHER, SCHOOLADMIN or ADMIN.
     * }
     * 
* * STUDENT rights: STUDENT, TEACHER, SCHOOLADMIN, ADMIN. * * TEACHER rights: TEACHER, SCHOOLADMIN, ADMIN. * * SCHOOLADMIN rights: SCHOOLADMIN, ADMIN. * * ADMIN rights: ADMIN. * * @access public * @param string $group groupname * @return boolean true, if user is in group $group */ function hasAccessToGroup( $group ) { if ( ( $this->_group == $group ) || ( $group == "STUDENT" ) || ( $group == "TEACHER" && $this->_group != "STUDENT" ) || ( $group == "SCHOOLADMIN" && $this->_group == "ADMIN" ) ) { return true; } else { return false; } } /** * Changes password * * This method changes the password of user, * but it does so only in memory. To make change * permanent use the User::save method. * * An optional parameter $old_password may be specified, * to perform first a check if the old password is correct. * An example with that kind of checking follows: * *
     * if ( $user->changePassword( $newpass, $oldpass ) ) {
     *     $user->save();
     *     echo "Password successfully changed.";
     * }
     * else {
     *     echo "Wrong old password specified!";
     * }
     * 
* * @access public * @param string $new_password plain text password * @param string $old_password old plain text password, * this attribute may be left unspecified, e.g. when * administrator changes the password of regular user. * @return boolean true on success, * false when old password is incorrect */ function changePassword( $new_password, $old_password = null ) { if ( isset($old_password) ) { if ( $this->verifyPassword( $old_password ) ) { $this->setPassword( $new_password ); return true; } else { return false; } } else { $this->setPassword( $new_password ); return true; } } /** * Checks if the supplied argument is users' password * * @access public * @param string $password password to check * @return boolean true when password matches. */ function verifyPassword( $password ) { if ( isset($this->_username) ) { return User::authenticate( $this->_username, $password ); } elseif ( isset($this->_id) ) { $this->loadFromDatabaseByID(); return User::authenticate( $this->_username, $password ); } else { trigger_error("No user ID specified when verifying password.", E_USER_ERROR); } } /** * Checks if username and supplied password match * * This function may be called statically. An example follows: * *
     * if ( User::authenticate( $username, $pass ) ) {
     *     echo "Password correct.";
     * }
     * else {
     *     echo "Password and username do not match!";
     * }
     * 
* * Note! If the password is stored in database * by using PASSWORD of OLD_PASSWORD hash, then the method * automagically replaces it with SHA1 hash. * * @access public * @param string $username username * @param string $password password to check * @return boolean true when password matches. */ function authenticate( $username, $password ) { // Try authenticating the user with applying // SHA1 function to the password if ( User::authenticateWithHashFunction($username, $password, "SHA1") ) { return true; } // If this fails, then the password might be stored // with either PASSWORD() or OLD_PASSWORD() function. // First try with PASSWORD and then with OLD_PASSWORD. elseif ( User::authenticateWithHashFunction($username, $password, "PASSWORD") || User::authenticateWithHashFunction($username, $password, "OLD_PASSWORD") ) { // re-save the password // using the current password encryption technology of VIKO return User::savePassword($username, $password); } // Finally give up and return error else { return false; } } /** * Authenticates the user by applying * custom hashfunction to the $password. * On success returns true. * * @return boolean */ function authenticateWithHashFunction($username, $password, $function) { $db = DBInstance::get(); $result = $db->getOne( " SELECT user_id FROM Users WHERE username = ? AND password = $function( ? ) ", array( $username, $password ) ); // check for errors if ( PEAR::isError($result) ) { trigger_error( $result->getMessage(), E_USER_ERROR ); } // if the result is set, the query succeeded return isset($result); } /** * Saves password into database * * Statically callable. * * @access public * @return boolean false when username was not found */ function savePassword( $username, $password ) { $db = DBInstance::get(); $result = $db->query( " UPDATE Users SET password = SHA1( ? ) WHERE username = ? ", array( $password, $username ) ); // check for errors if ( PEAR::isError($result) ) { trigger_error( $result->getMessage(), E_USER_ERROR ); } return ( $db->affectedRows() == 1 ); } /** * Removes the user from database * * The ID must be specified for this to work. * * @access public * @return boolean true on successful delete. * false, if the user with ID did not exist */ function delete() { // first delete the user record itself if ( parent::delete() ) { // After that remove all the courses the user is teaching $course_list = CourseList::getFromTeacher( $this ); foreach ( $course_list as $course ) { $course->delete(); } $db = DBInstance::get(); // remove user from each course where he is registered as student $db->query("DELETE FROM Courses_Users WHERE user_id=?", $this->_id ); // remove all the marks he has received $db->query("DELETE FROM Marks WHERE user_id=?", $this->_id ); // and remove all the forum posts he has created $db->query("DELETE FROM Posts WHERE user_id=?", $this->_id ); // get list of materials and delete these $r = $db->query("SELECT * FROM Materials WHERE user_id=?", $this->_id ); while ( $r->fetchInto( $row ) ) { $material = MaterialFactory::material( $row['material_type'], $row['material_id'] ); $material->readFromDatabaseRecord( $row ); $material->delete(); } // NOTE: Maybe we should just mark the user as deleted, not actually delete him? return true; } else { return false; } } } ?>