4 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
5 * Copyright (C) 2002-2009 The Nucleus Group
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 * (see nucleus/documentation/index.html#license for more info)
14 * A class representing site members
16 * @license http://nucleuscms.org/license.txt GNU General Public License
17 * @copyright Copyright (C) 2002-2009 The Nucleus Group
18 * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $
22 // 1 when authenticated, 0 when not
24 public $password; // not the actual password, but rather a MD5 hash
25 private $algorism = 'md5';
27 public $cookiekey; // value that should also be in the client cookie to allow authentication
28 private $cookie_salt = FALSE;
36 public $admin = 0; // (either 0 or 1)
37 public $canlogin = 0; // (either 0 or 1)
39 public $autosave = 1; // if the member use the autosave draft function
40 public $adminskin = 0; // if the member use the autosave draft function
41 public $bookmarklet = 0; // if the member use the autosave draft function
45 * Member::__construct()
46 * Constructor for a member object
52 public function __construct()
58 * Member::createFromName()
59 * Create a member object for a given displayname
62 * @param String $displayname login name
63 * @return Object member object
66 public static function &createFromName($displayname)
69 $mem->readFromName($displayname);
74 * Member::createFromID()
75 * Create a member object for a given ID
78 * @param Integer $id id for member
80 public static function &createFromID($id)
83 $mem->readFromID($id);
88 * Member::readFromName()
89 * Read member table in database
91 * @param String $displayname login name
92 * @return Object SQL resource
95 public function readFromName($displayname)
97 return $this->read('mname='.DB::quoteValue($displayname));
101 * Member::readFromID()
102 * Read member table in database
104 * @param Integer $id id for member
105 * @return Object SQL resource
108 public function readFromID($id)
110 return $this->read("mnumber=" . intval($id));
115 * hash the target string
117 * @param String $string target string
118 * @return Void hashed string
120 public function hash($string)
122 switch ( $this->algorism )
126 $string = md5($string);
132 * Member::set_cookie_salt()
134 * @param integer $key secureCookieKey value
138 private function set_cookie_salt($key = 0)
148 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
151 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
154 $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
157 $this->cookie_salt = serverVar('REMOTE_ADDR');
160 $this->cookie_salt = 'none';
167 * Tries to login as a given user.
168 * Returns true when succeeded, returns false when failed
169 * 3.40 adds CustomLogin event
171 * @param String $login login name for member
172 * @param String $password password for member
173 * @param Integer $shared whether the user agent is shared or not
176 public function login($login, $password, $shared=1)
178 global $CONF, $errormessage, $manager;
180 /* TODO: validation for $login, $password, $shared */
181 if ( $login == '' || $password == '' )
185 /* limiting the length of password to avoid hash collision */
186 $password=i18n::substr($password, 0, 40);
189 * generate cookie salt from secure cookie key settings
190 * (either 'none', 0, 8, 16, 24, or 32)
192 if ( !$this->cookie_salt )
195 if ( array_key_exists('secureCookieKey', $CONF) )
197 $salt = $CONF['secureCookieKey'];
199 $this->set_cookie_salt($salt);
204 $data = array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal);
205 $manager->notify('CustomLogin', $data);
210 $this->loggedin = ( $this->readFromName($login) );
212 elseif ( $allowlocal )
214 $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );
218 if ( !$this->loggedin )
220 $trimlogin = trim($login);
221 if ( empty($trimlogin) )
223 $errormessage = "Please enter a username.";
227 $errormessage = 'Login failed for ' . $login;
229 $data = array('username' => $login);
230 $manager->notify('LoginFailed', $data);
231 ActionLog::add(INFO, $errormessage);
236 /* For lower compatibility */
237 if ( strlen($this->password) === 32 )
239 $this->password = $this->hash($password);
242 $this->newCookieKey();
243 $this->setCookies($shared);
245 if ( $this->cookie_salt !== 'none' )
247 /* secure cookie key */
248 $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));
253 $data = array('member' => $this, 'username' => $login);
254 $manager->notify('LoginSuccess', $data);
255 ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");
258 return $this->loggedin;
262 * Member::cookielogin()
263 * Login using cookie key
265 * @param String $login not used
266 * @param String $cookiekey not used
267 * @return Boolean login or not
269 public function cookielogin($login='', $cookiekey='')
271 global $CONF, $manager;
273 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
275 /* Cookie Authentication */
276 $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");
278 /* TODO: validation for each cookie values */
280 /* limiting the length of password to avoid hash collision */
281 $ck = i18n::substr($ck,0,32);
284 * generate cookie salt from secure cookie key settings
285 * (either 'none', 0, 8, 16, 24, or 32)
287 if ( !$this->cookie_salt )
290 if ( array_key_exists('secureCookieKey', $CONF) )
292 $salt = $CONF['secureCookieKey'];
294 $this->set_cookie_salt($salt);
297 if ( $this->cookie_salt !== 'none' )
299 $ck = $this->hash($ck . $this->cookie_salt);
301 $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );
304 /* renew cookies when not on a shared computer */
305 if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )
307 $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));
311 return $this->loggedin;
316 * logout and expire cookie
321 public function logout()
323 global $CONF, $manager;
325 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
327 /* remove cookies on logout */
328 setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
329 setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
330 $data = array('username' => cookieVar("{$CONF['CookiePrefix']}user") );
331 $manager->notify('Logout', $data);
339 * Member::isLoggedIn()
340 * return member is loggedin or not
345 public function isLoggedIn()
347 return $this->loggedin;
352 * Read member information from the database
354 * @param String $where where statement
355 * @return Resource SQL resource
358 public function read($where)
361 $query = 'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;
363 $row = DB::getRow($query);
365 $this->setRealName($row['mrealname']);
366 $this->setEmail($row['memail']);
367 $this->password = $row['mpassword'];
368 $this->setCookieKey($row['mcookiekey']);
369 $this->setURL($row['murl']);
370 $this->setDisplayName($row['mname']);
371 $this->setAdmin($row['madmin']);
372 $this->id = $row['mnumber'];
373 $this->setCanLogin($row['mcanlogin']);
374 $this->setNotes($row['mnotes']);
375 $this->setLocale($row['mlocale']);
376 $this->setAutosave($row['mautosave']);
377 $this->setAdminSkin($row['madminskin']);
378 $this->setBookmarklet($row['mbkmklt']);
380 return $row ? TRUE : FALSE;
384 * Member::isBlogAdmin()
385 * Returns true if member is an admin for the given blog
386 * (returns false if not a team member)
388 * @param Integer $blogid weblog id
389 * @return Integer weblog admin or not
392 public function isBlogAdmin($blogid)
394 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'
395 . ' tblog=' . intval($blogid)
396 . ' and tmember='. $this->getID();
397 $res = DB::getValue($query);
405 * Member::blogAdminRights()
407 * @param integer $blogid ID of target weblog
408 * @return boolean whether to have admin rights to the weblog or not
411 public function blogAdminRights($blogid)
413 return ($this->isAdmin() || $this->isBlogAdmin($blogid));
417 * Member::teamRights()
419 * @param integer $blogid ID of target weblog
420 * @return boolean whether to have admin right to the weblog or not
423 public function teamRights($blogid)
425 return ($this->isAdmin() || $this->isTeamMember($blogid));
429 * Member::isTeamMember()
430 * Returns true if this member is a team member of the given blog
432 * @param integer $blogid ID of target weblog
433 * @return boolean whether to join the weblog or not
436 public function isTeamMember($blogid)
438 $query = 'SELECT * FROM '.sql_table('team').' WHERE'
439 . ' tblog=' . intval($blogid)
440 . ' and tmember='. $this->getID();
441 $res = DB::getResult($query);
442 return ($res->rowCount() != 0);
446 * Member::canAddItem()
448 * @param integer $catid ID of target category
449 * @return boolean whether to be able to add items to the category or not
452 public function canAddItem($catid)
456 // if this is a 'newcat' style newcat
457 // no blog admin of destination blog -> NOK
458 // blog admin of destination blog -> OK
459 if ( i18n::strpos($catid,'newcat') === 0 )
462 list($blogid) = sscanf($catid,"newcat-%d");
463 return $this->blogAdminRights($blogid);
466 // category does not exist -> NOK
467 if ( !$manager->existsCategory($catid) )
472 $blogid = getBlogIDFromCatID($catid);
474 // no team rights for blog -> NOK
475 if (!$this->teamRights($blogid))
480 // all other cases: OK
485 * Member::canAlterComment()
486 * Returns true if this member can edit/delete a commentitem. This can be in the
488 * - member is a super-admin
489 * - member is the author of the comment
490 * - member is admin of the blog associated with the comment
491 * - member is author of the item associated with the comment
493 * @param integer $commentid ID of target comment
494 * @return boolean delete/edit the comment or not
497 public function canAlterComment($commentid)
499 if ( $this->isAdmin() )
504 $query = 'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'
505 . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')
506 . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);
507 $res = DB::getRow($query);
509 return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());
513 * Member::canAlterItem()
514 * Returns true if this member can edit/delete an item. This is true in the following
515 * cases: - member is a super-admin
516 * - member is the author of the item
517 * - member is admin of the the associated blog
519 * @param integer $itemid ID of target item
520 * @return boolean delete/edit the item or not
523 public function canAlterItem($itemid)
525 if ($this->isAdmin()) return 1;
527 $query = 'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);
528 $res = DB::getRow($query);
529 return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);
533 * Member::canBeDeleted()
534 * Return true if member can be deleted. This means that there are no items posted by the member left
537 * @return boolean whether there is no items or exists
540 public function canBeDeleted()
542 $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());
543 return ( $res->rowCount() == 0 );
547 * Member::canUpdateItem()
548 * returns true if this member can move/update an item to a given category,
549 * false if not (see comments fot the tests that are executed)
551 * @param integer $itemid
552 * @param string $newcat (can also be of form 'newcat-x' with x=blogid)
553 * @return boolean whether being able to update the item or not
556 public function canUpdateItem($itemid, $newcat)
560 // item does not exists -> NOK
561 if ( !$manager->existsItem($itemid,1,1) )
566 // cannot alter item -> NOK
567 if (!$this->canAlterItem($itemid))
572 // if this is a 'newcat' style newcat
573 // no blog admin of destination blog -> NOK
574 // blog admin of destination blog -> OK
575 if ( i18n::strpos($newcat, 'newcat') === 0 )
578 list($blogid) = sscanf($newcat, 'newcat-%d');
579 return $this->blogAdminRights($blogid);
582 // category does not exist -> NOK
583 if (!$manager->existsCategory($newcat))
589 $item =& $manager->getItem($itemid,1,1);
591 // old catid = new catid -> OK
592 if ($item['catid'] == $newcat)
597 // not a valid category -> NOK
598 $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));
604 // get destination blog
605 $item =& $manager->getItem($itemid, 1, 1);
606 $source_blogid = $item['blogid'];
607 $dest_blogid = getBlogIDFromCatID($newcat);
609 // not a team member of destination blog -> NOK
610 if ( !$this->teamRights($dest_blogid) )
615 // if member is author of item -> OK
616 if ( $item['authorid'] == $this->getID() )
621 // if member has admin rights on both blogs: OK
622 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )
627 // all other cases: NOK
632 * Member::setCookies()
633 * Sets the cookies for the member
635 * @param boolean $shared set this to 1 when using a shared computer. Cookies will expire
636 * at the end of the session in this case.
640 public function setCookies($shared = 0)
644 if ( $CONF['SessionCookie'] || $shared )
650 $lifetime = time()+2592000;
653 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
654 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
656 // make sure cookies on shared pcs don't get renewed
659 setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
665 * Member::sendActivationLink()
666 * Send activation mail
668 * @param string $type activation type
669 * @param string $extra extra info
672 public function sendActivationLink($type, $extra='')
676 if ( !isset($CONF['ActivationDays']) )
678 $CONF['ActivationDays'] = 2;
681 // generate key and URL
682 $key = $this->generateActivationEntry($type, $extra);
683 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;
685 // choose text to use in mail
689 $message = _ACTIVATE_REGISTER_MAIL;
690 $subject = _ACTIVATE_REGISTER_MAILTITLE;
693 $message = _ACTIVATE_FORGOT_MAIL;
694 $subject = _ACTIVATE_FORGOT_MAILTITLE;
696 case 'addresschange':
697 $message = _ACTIVATE_CHANGE_MAIL;
698 $subject = _ACTIVATE_CHANGE_MAILTITLE;
703 // fill out variables in text
705 'siteName' => $CONF['SiteName'],
706 'siteUrl' => $CONF['IndexURL'],
707 'memberName' => $this->getDisplayName(),
708 'activationUrl' => $url,
709 'activationDays' => $CONF['ActivationDays']
712 $message = Template::fill($message, $aVars);
713 $subject = Template::fill($subject, $aVars);
716 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());
718 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');
723 * Member::getAdminBlogs()
724 * Returns an array of all blogids for which member has admin rights
727 * @return array weblog IDs in which this member has admin rights
730 public function getAdminBlogs()
734 if ($this->isAdmin())
736 $query = 'SELECT bnumber as blogid from '.sql_table('blog');
740 $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();
743 $res = DB::getResult($query);
744 if ( $res->rowCount() > 0 )
746 foreach ( $res as $row )
748 array_push($blogs, $row['blogid']);
755 * Member::getTeamBlogs()
756 * Returns an array of all blogids for which member has team rights
758 * @param boolean $incAdmin whether checking weblog admin rights or not
759 * @return array weblog IDs in which this member join
762 public function getTeamBlogs($incAdmin = 1)
764 $incAdmin = intval($incAdmin);
767 if ( $this->isAdmin() && $incAdmin )
769 $query = 'SELECT bnumber as blogid from '.sql_table('blog');
773 $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();
776 $res = DB::getResult($query);
777 if ( $res->rowCount() > 0 )
779 foreach ( $res as $row )
781 array_push($blogs, $row['blogid']);
788 * Member::getNotifyFromMailAddress()
790 * Returns an email address from which notification of commenting/karma voting can
791 * be sent. A suggestion can be given for when the member is not logged in
793 * @param String $suggest
794 * @return String mail address or suggestion
796 public function getNotifyFromMailAddress($suggest = "")
799 if ( $this->isLoggedIn() )
801 return $this->getDisplayName() . " <" . $this->getEmail() . ">";
803 else if ( NOTIFICATION::address_validation($suggest) )
807 return $CONF['AdminEmail'];
812 * Write data to database
818 public function write()
820 $query = 'UPDATE '.sql_table('member')
821 . ' SET mname=' . DB::quoteValue($this->displayname) . ', '
822 . 'mrealname='. DB::quoteValue($this->realname) . ', '
823 . 'mpassword='. DB::quoteValue($this->password) . ', '
824 . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '
825 . 'murl=' . DB::quoteValue($this->url) . ', '
826 . 'memail=' . DB::quoteValue($this->email) . ', '
827 . 'madmin=' . intval($this->admin) . ', '
828 . 'mnotes=' . DB::quoteValue($this->notes) . ', '
829 . 'mcanlogin=' . intval($this->canlogin) . ', '
830 . 'mlocale=' . DB::quoteValue($this->locale) . ', '
831 . 'madminskin=' . DB::quoteValue($this->adminskin) . ', '
832 . 'mbkmklt=' . DB::quoteValue($this->bookmarklet) . ', '
833 . 'mautosave=' . intval($this->autosave) . ' '
834 . 'WHERE mnumber=' . intval($this->id);
839 public function checkCookieKey($key)
841 return ( ($key != '') && ( $key == $this->getCookieKey() ) );
844 public function checkPassword($pw)
846 /* for lower compatibility (md5) */
847 if ( strlen($this->password) === 32 )
849 return (md5($pw) == $this->password);
851 return ($this->hash($pw) == $this->password);
854 public function getRealName()
856 return $this->realname;
859 public function setRealName($name)
861 $this->realname = $name;
864 public function getEmail()
869 public function setEmail($email)
871 $this->email = $email;
874 public function getPassword()
876 return $this->password;
879 public function setPassword($pwd)
881 $this->password = $this->hash($pwd);
884 public function getCookieKey()
886 return $this->cookiekey;
890 * Member::newCookieKey()
891 * Generate new cookiekey, save it, and return it
897 public function newCookieKey()
899 mt_srand( (double) microtime() * 1000000);
900 $this->cookiekey = $this->hash(uniqid(mt_rand()));
902 return $this->cookiekey;
905 public function setCookieKey($val)
907 $this->cookiekey = $val;
910 public function getURL()
915 public function setURL($site)
920 public function setAdminSkin($skin)
922 $this->adminskin = $skin;
925 public function setBookmarklet($skin)
927 $this->bookmarklet = $skin;
930 public function getAdminSkin()
932 return $this->adminskin;
935 public function getBookmarklet()
937 return $this->bookmarklet;
940 public function getLocale()
942 return $this->locale;
945 public function setLocale($locale)
947 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)
948 && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )
952 $this->locale = $locale;
956 public function setDisplayName($nick)
958 $this->displayname = $nick;
961 public function getDisplayName()
963 return $this->displayname;
966 public function isAdmin()
971 public function setAdmin($val)
976 public function canLogin()
978 return $this->canlogin;
981 public function setCanLogin($val)
983 $this->canlogin = $val;
986 public function getNotes()
991 public function setNotes($val)
996 public function getAutosave()
998 return $this->autosave;
1001 public function setAutosave($val)
1003 $this->autosave = $val;
1011 * @return integer id of this member object
1014 public function getID()
1021 * Returns true if there is a member with the given login name
1024 * @param string $name target name
1025 * @return boolean whether target name exists or not
1027 public static function exists($name)
1029 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));
1030 return ( $r->rowCount() != 0 );
1034 * Member::existsID()
1035 * Returns true if there is a member with the given ID
1038 * @param integer $id target id
1039 * @return boolean whether target id exists or not
1042 public static function existsID($id)
1044 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));
1045 return ( $r->rowCount() != 0 );
1049 * Member::isNameProtected()
1050 * Checks if a username is protected.
1051 * If so, it can not be used on anonymous comments
1053 * @param string $name target name
1054 * @return boolean whether the name exists or not
1057 public function isNameProtected($name)
1060 $name = strip_tags($name);
1061 $name = trim($name);
1062 return self::exists($name);
1070 * @param String $name
1071 * @param String $realname
1072 * @param String $password
1073 * @param String $email
1074 * @param String $url
1075 * @param String $admin
1076 * @param String $canlogin
1077 * @param String $notes
1078 * @return String 1 if success, others if fail
1080 static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)
1082 if ( !NOTIFICATION::address_validation($email) )
1084 return _ERROR_BADMAILADDRESS;
1087 /* TODO: this method should be in MEMBER class, not globalfunctions */
1088 if ( !isValidDisplayName($name) )
1090 return _ERROR_BADNAME;
1093 if ( self::exists($name) )
1095 return _ERROR_NICKNAMEINUSE;
1100 return _ERROR_REALNAMEMISSING;
1103 /* TODO: check the number of characters */
1106 return _ERROR_PASSWORDMISSING;
1110 * begin if: sometimes user didn't prefix the URL with http:// or https://,
1111 * this cause a malformed URL. Let's fix it.
1114 if ( !preg_match('#^https?://#', $url) )
1116 $url = 'http://' . $url;
1119 $name = DB::quoteValue($name);
1120 $realname = DB::quoteValue($realname);
1121 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */
1122 $password = DB::quoteValue(md5($password));
1123 $email = DB::quoteValue($email);
1124 $url = DB::quoteValue($url);
1125 $admin = (integer) $admin;
1126 $canlogin = (integer) $canlogin;
1127 $notes = DB::quoteValue($notes);
1129 $query = "INSERT INTO %s"
1130 . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"
1131 . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";
1132 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);
1133 DB::execute($query);
1135 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);
1141 * Member::getActivationInfo()
1142 * Returns activation info for a certain key (an object with properties vkey, vmember, ...)
1145 * @param string $key activation key
1146 * @return mixed return 0 if failed, else return activation table object
1149 public static function getActivationInfo($key)
1151 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);
1152 $res = DB::getResult($query);
1154 if ( !$res || ($res->rowCount() == 0) )
1158 return $res->fetch();
1162 * Member::generateActivationEntry()
1163 * Creates an account activation key
1164 * addresschange -> old email address
1166 * @param string $type one of the following values (determines what to do when activation expires)
1167 * 'register' (new member registration)
1168 * 'forgot' (forgotton password)
1169 * 'addresschange' (member address has changed)
1170 * @param string $extra extra info (needed when validation link expires)
1171 * @return string activation key
1173 public function generateActivationEntry($type, $extra = '')
1175 // clean up old entries
1176 $this->cleanupActivationTable();
1178 // kill any existing entries for the current member (delete is ok)
1179 // (only one outstanding activation key can be present for a member)
1180 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));
1182 // indicates if the member can log in while the link is active
1183 $canLoginWhileActive = false;
1187 $canLoginWhileActive = true;
1191 case 'addresschange':
1192 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );
1199 // generate a random key
1200 srand((double)microtime()*1000000);
1201 $key = $this->hash(uniqid(rand(), true));
1203 // attempt to add entry in database
1204 // add in database as non-active
1205 $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';
1206 $query = sprintf($query
1207 , sql_table('activation')
1208 , DB::quoteValue($key)
1209 , DB::formatDateTime()
1210 , intval($this->getID())
1211 , DB::quoteValue($type)
1212 , DB::quoteValue($extra)
1214 if ( DB::execute($query) !== FALSE )
1218 // mark member as not allowed to log in
1219 if ( !$canLoginWhileActive )
1221 $this->setCanLogin(0);
1230 * Member::activate()
1231 * Inidicates that an activation link has been clicked and any forms displayed
1232 * there have been successfully filled out.
1234 * @param string $key activation key
1238 public function activate($key)
1240 // get activate info
1241 $info = self::getActivationInfo($key);
1249 switch ( $info['vtype'] )
1255 // set canlogin value
1257 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));
1259 case 'addresschange':
1260 // reset old 'canlogin' value
1261 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);
1262 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));
1266 // delete from activation table
1267 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));
1274 * Member::cleanupActivationTable()
1275 * Cleans up entries in the activation table. All entries older than 2 days are removed.
1281 public function cleanupActivationTable()
1284 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )
1286 $actdays = intval($CONF['ActivationDays']);
1290 $CONF['ActivationDays'] = 2;
1292 $boundary = time() - (60 * 60 * 24 * $actdays);
1294 // 1. walk over all entries, and see if special actions need to be performed
1295 $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
1296 $res = DB::getResult($query);
1298 foreach ( $res as $row )
1300 switch ( $row['vtype'] )
1303 // delete all information about this site member. registration is undone because there was
1304 // no timely activation
1305 include_once($DIR_LIBS . 'ADMIN.php');
1306 Admin::deleteOneMember(intval($row['vmember']));
1308 case 'addresschange':
1309 // revert the e-mail address of the member back to old address
1310 list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);
1311 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));
1314 // delete the activation link and ignore. member can request a new password using the
1315 // forgot password link
1320 // 2. delete activation entries for real
1321 $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
1322 DB::execute($query);
1334 public $language = '';
1336 * Member::getLanguage()
1343 public function getLanguage()
1345 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )