5 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
\r
6 * Copyright (C) 2002-2009 The Nucleus Group
\r
8 * This program is free software; you can redistribute it and/or
\r
9 * modify it under the terms of the GNU General Public License
\r
10 * as published by the Free Software Foundation; either version 2
\r
11 * of the License, or (at your option) any later version.
\r
12 * (see nucleus/documentation/index.html#license for more info)
\r
15 * A class representing site members
\r
17 * @license http://nucleuscms.org/license.txt GNU General Public License
\r
18 * @copyright Copyright (C) 2002-2009 The Nucleus Group
\r
19 * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $
\r
23 // 1 when authenticated, 0 when not
\r
24 public $loggedin = 0;
\r
25 public $password; // not the actual password, but rather a MD5 hash
\r
26 private $algorism = 'md5';
\r
28 public $cookiekey; // value that should also be in the client cookie to allow authentication
\r
29 private $cookie_salt = FALSE;
\r
34 public $displayname;
\r
37 public $admin = 0; // (either 0 or 1)
\r
38 public $canlogin = 0; // (either 0 or 1)
\r
40 public $autosave = 1; // if the member use the autosave draft function
\r
41 private $locale = '';
\r
44 * Member::__construct()
\r
45 * Constructor for a member object
\r
51 public function __construct()
\r
57 * Member::createFromName()
\r
58 * Create a member object for a given displayname
\r
61 * @param String $displayname login name
\r
62 * @return Object member object
\r
65 public static function &createFromName($displayname)
\r
67 $mem = new Member();
\r
68 $mem->readFromName($displayname);
\r
73 * Member::createFromID()
\r
74 * Create a member object for a given ID
\r
77 * @param Integer $id id for member
\r
79 public static function &createFromID($id)
\r
81 $mem = new Member();
\r
82 $mem->readFromID($id);
\r
87 * Member::readFromName()
\r
88 * Read member table in database
\r
90 * @param String $displayname login name
\r
91 * @return Object SQL resource
\r
94 public function readFromName($displayname)
\r
96 return $this->read('mname='.DB::quoteValue($displayname));
\r
100 * Member::readFromID()
\r
101 * Read member table in database
\r
103 * @param Integer $id id for member
\r
104 * @return Object SQL resource
\r
107 public function readFromID($id)
\r
109 return $this->read("mnumber=" . intval($id));
\r
114 * hash the target string
\r
116 * @param String $string target string
\r
117 * @return Void hashed string
\r
119 public function hash($string)
\r
121 switch ( $this->algorism )
\r
125 $string = md5($string);
\r
131 * Member::set_cookie_salt()
\r
133 * @param integer $key secureCookieKey value
\r
137 private function set_cookie_salt($key = 0)
\r
147 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
\r
150 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
\r
153 $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
\r
156 $this->cookie_salt = serverVar('REMOTE_ADDR');
\r
159 $this->cookie_salt = 'none';
\r
166 * Tries to login as a given user.
\r
167 * Returns true when succeeded, returns false when failed
\r
168 * 3.40 adds CustomLogin event
\r
170 * @param String $login login name for member
\r
171 * @param String $password password for member
\r
172 * @param Integer $shared whether the user agent is shared or not
\r
175 public function login($login, $password, $shared=1)
\r
177 global $CONF, $errormessage, $manager;
\r
179 /* TODO: validation for $login, $password, $shared */
\r
180 if ( $login == '' || $password == '' )
\r
184 /* limiting the length of password to avoid hash collision */
\r
185 $password=i18n::substr($password, 0, 40);
\r
188 * generate cookie salt from secure cookie key settings
\r
189 * (either 'none', 0, 8, 16, 24, or 32)
\r
191 if ( !$this->cookie_salt )
\r
194 if ( array_key_exists('secureCookieKey', $CONF) )
\r
196 $salt = $CONF['secureCookieKey'];
\r
198 $this->set_cookie_salt($salt);
\r
203 $manager->notify('CustomLogin', array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal));
\r
205 $this->loggedin = 0;
\r
208 $this->loggedin = ( $this->readFromName($login) );
\r
210 elseif ( $allowlocal )
\r
212 $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );
\r
216 if ( !$this->loggedin )
\r
218 $trimlogin = trim($login);
\r
219 if ( empty($trimlogin) )
\r
221 $errormessage = "Please enter a username.";
\r
225 $errormessage = 'Login failed for ' . $login;
\r
227 $manager->notify('LoginFailed', array('username' => $login) );
\r
228 ActionLog::add(INFO, $errormessage);
\r
230 /* login success */
\r
233 /* For lower compatibility */
\r
234 if ( strlen($this->password) === 32 )
\r
236 $this->password = $this->hash($password);
\r
239 $this->newCookieKey();
\r
240 $this->setCookies($shared);
\r
242 if ( $this->cookie_salt !== 'none' )
\r
244 /* secure cookie key */
\r
245 $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));
\r
249 $errormessage = '';
\r
250 $manager->notify('LoginSuccess', array('member' => &$member, 'username' => $login) );
\r
251 ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");
\r
254 return $this->loggedin;
\r
258 * Member::cookielogin()
\r
259 * Login using cookie key
\r
261 * @param String $login not used
\r
262 * @param String $cookiekey not used
\r
263 * @return Boolean login or not
\r
265 public function cookielogin($login='', $cookiekey='')
\r
267 global $CONF, $manager;
\r
269 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
\r
271 /* Cookie Authentication */
\r
272 $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");
\r
274 /* TODO: validation for each cookie values */
\r
276 /* limiting the length of password to avoid hash collision */
\r
277 $ck = i18n::substr($ck,0,32);
\r
280 * generate cookie salt from secure cookie key settings
\r
281 * (either 'none', 0, 8, 16, 24, or 32)
\r
283 if ( !$this->cookie_salt )
\r
286 if ( array_key_exists('secureCookieKey', $CONF) )
\r
288 $salt = $CONF['secureCookieKey'];
\r
290 $this->set_cookie_salt($salt);
\r
293 if ( $this->cookie_salt !== 'none' )
\r
295 $ck = $this->hash($ck . $this->cookie_salt);
\r
297 $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );
\r
300 /* renew cookies when not on a shared computer */
\r
301 if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )
\r
303 $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));
\r
304 $this->setCookies();
\r
307 return $this->loggedin;
\r
312 * logout and expire cookie
\r
317 public function logout()
\r
319 global $CONF, $manager;
\r
321 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
\r
323 /* remove cookies on logout */
\r
324 setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
\r
325 setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
\r
326 $manager->notify('Logout', array('username' => cookieVar("{$CONF['CookiePrefix']}user") ) );
\r
329 $this->loggedin = 0;
\r
334 * Member::isLoggedIn()
\r
335 * return member is loggedin or not
\r
340 public function isLoggedIn()
\r
342 return $this->loggedin;
\r
347 * Read member information from the database
\r
349 * @param String $where where statement
\r
350 * @return Resource SQL resource
\r
353 public function read($where)
\r
356 $query = 'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;
\r
358 $row = DB::getRow($query);
\r
360 $this->setRealName($row['mrealname']);
\r
361 $this->setEmail($row['memail']);
\r
362 $this->password = $row['mpassword'];
\r
363 $this->setCookieKey($row['mcookiekey']);
\r
364 $this->setURL($row['murl']);
\r
365 $this->setDisplayName($row['mname']);
\r
366 $this->setAdmin($row['madmin']);
\r
367 $this->id = $row['mnumber'];
\r
368 $this->setCanLogin($row['mcanlogin']);
\r
369 $this->setNotes($row['mnotes']);
\r
370 $this->setLocale($row['mlocale']);
\r
371 $this->setAutosave($row['mautosave']);
\r
373 return $row ? TRUE : FALSE;
\r
377 * Member::isBlogAdmin()
\r
378 * Returns true if member is an admin for the given blog
\r
379 * (returns false if not a team member)
\r
381 * @param Integer $blogid weblog id
\r
382 * @return Integer weblog admin or not
\r
385 public function isBlogAdmin($blogid)
\r
387 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'
\r
388 . ' tblog=' . intval($blogid)
\r
389 . ' and tmember='. $this->getID();
\r
390 $res = DB::getValue($query);
\r
392 return ($res == 1);
\r
398 * Member::blogAdminRights()
\r
400 * @param integer $blogid ID of target weblog
\r
401 * @return boolean whether to have admin rights to the weblog or not
\r
404 public function blogAdminRights($blogid)
\r
406 return ($this->isAdmin() || $this->isBlogAdmin($blogid));
\r
410 * Member::teamRights()
\r
412 * @param integer $blogid ID of target weblog
\r
413 * @return boolean whether to have admin right to the weblog or not
\r
416 public function teamRights($blogid)
\r
418 return ($this->isAdmin() || $this->isTeamMember($blogid));
\r
422 * Member::isTeamMember()
\r
423 * Returns true if this member is a team member of the given blog
\r
425 * @param integer $blogid ID of target weblog
\r
426 * @return boolean whether to join the weblog or not
\r
429 public function isTeamMember($blogid)
\r
431 $query = 'SELECT * FROM '.sql_table('team').' WHERE'
\r
432 . ' tblog=' . intval($blogid)
\r
433 . ' and tmember='. $this->getID();
\r
434 $res = DB::getResult($query);
\r
435 return ($res->rowCount() != 0);
\r
439 * Member::canAddItem()
\r
441 * @param integer $catid ID of target category
\r
442 * @return boolean whether to be able to add items to the category or not
\r
445 public function canAddItem($catid)
\r
449 // if this is a 'newcat' style newcat
\r
450 // no blog admin of destination blog -> NOK
\r
451 // blog admin of destination blog -> OK
\r
452 if ( i18n::strpos($catid,'newcat') === 0 )
\r
455 list($blogid) = sscanf($catid,"newcat-%d");
\r
456 return $this->blogAdminRights($blogid);
\r
459 // category does not exist -> NOK
\r
460 if ( !$manager->existsCategory($catid) )
\r
465 $blogid = getBlogIDFromCatID($catid);
\r
467 // no team rights for blog -> NOK
\r
468 if (!$this->teamRights($blogid))
\r
473 // all other cases: OK
\r
478 * Member::canAlterComment()
\r
479 * Returns true if this member can edit/delete a commentitem. This can be in the
\r
481 * - member is a super-admin
\r
482 * - member is the author of the comment
\r
483 * - member is admin of the blog associated with the comment
\r
484 * - member is author of the item associated with the comment
\r
486 * @param integer $commentid ID of target comment
\r
487 * @return boolean delete/edit the comment or not
\r
490 public function canAlterComment($commentid)
\r
492 if ( $this->isAdmin() )
\r
497 $query = 'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'
\r
498 . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')
\r
499 . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);
\r
500 $res = DB::getRow($query);
\r
502 return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());
\r
506 * Member::canAlterItem()
\r
507 * Returns true if this member can edit/delete an item. This is true in the following
\r
508 * cases: - member is a super-admin
\r
509 * - member is the author of the item
\r
510 * - member is admin of the the associated blog
\r
512 * @param integer $itemid ID of target item
\r
513 * @return boolean delete/edit the item or not
\r
516 public function canAlterItem($itemid)
\r
518 if ($this->isAdmin()) return 1;
\r
520 $query = 'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);
\r
521 $res = DB::getRow($query);
\r
522 return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);
\r
526 * Member::canBeDeleted()
\r
527 * Return true if member can be deleted. This means that there are no items posted by the member left
\r
530 * @return boolean whether there is no items or exists
\r
533 public function canBeDeleted()
\r
535 $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());
\r
536 return ( $res->rowCount() == 0 );
\r
540 * Member::canUpdateItem()
\r
541 * returns true if this member can move/update an item to a given category,
\r
542 * false if not (see comments fot the tests that are executed)
\r
544 * @param integer $itemid
\r
545 * @param string $newcat (can also be of form 'newcat-x' with x=blogid)
\r
546 * @return boolean whether being able to update the item or not
\r
549 public function canUpdateItem($itemid, $newcat)
\r
553 // item does not exists -> NOK
\r
554 if ( !$manager->existsItem($itemid,1,1) )
\r
559 // cannot alter item -> NOK
\r
560 if (!$this->canAlterItem($itemid))
\r
565 // if this is a 'newcat' style newcat
\r
566 // no blog admin of destination blog -> NOK
\r
567 // blog admin of destination blog -> OK
\r
568 if ( i18n::strpos($newcat, 'newcat') === 0 )
\r
571 list($blogid) = sscanf($newcat, 'newcat-%d');
\r
572 return $this->blogAdminRights($blogid);
\r
575 // category does not exist -> NOK
\r
576 if (!$manager->existsCategory($newcat))
\r
582 $item =& $manager->getItem($itemid,1,1);
\r
584 // old catid = new catid -> OK
\r
585 if ($item['catid'] == $newcat)
\r
590 // not a valid category -> NOK
\r
591 $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));
\r
597 // get destination blog
\r
598 $source_blogid = getBlogIDFromItemID($itemid);
\r
599 $dest_blogid = getBlogIDFromCatID($newcat);
\r
601 // not a team member of destination blog -> NOK
\r
602 if ( !$this->teamRights($dest_blogid) )
\r
607 // if member is author of item -> OK
\r
608 if ( $item['authorid'] == $this->getID() )
\r
613 // if member has admin rights on both blogs: OK
\r
614 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )
\r
619 // all other cases: NOK
\r
624 * Member::setCookies()
\r
625 * Sets the cookies for the member
\r
627 * @param boolean $shared set this to 1 when using a shared computer. Cookies will expire
\r
628 * at the end of the session in this case.
\r
632 public function setCookies($shared = 0)
\r
636 if ( $CONF['SessionCookie'] || $shared )
\r
642 $lifetime = time()+2592000;
\r
645 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
\r
646 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
\r
648 // make sure cookies on shared pcs don't get renewed
\r
651 setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
\r
657 * Member::sendActivationLink()
\r
658 * Send activation mail
\r
660 * @param string $type activation type
\r
661 * @param string $extra extra info
\r
664 public function sendActivationLink($type, $extra='')
\r
668 if ( !isset($CONF['ActivationDays']) )
\r
670 $CONF['ActivationDays'] = 2;
\r
673 // generate key and URL
\r
674 $key = $this->generateActivationEntry($type, $extra);
\r
675 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;
\r
677 // choose text to use in mail
\r
681 $message = _ACTIVATE_REGISTER_MAIL;
\r
682 $subject = _ACTIVATE_REGISTER_MAILTITLE;
\r
685 $message = _ACTIVATE_FORGOT_MAIL;
\r
686 $subject = _ACTIVATE_FORGOT_MAILTITLE;
\r
688 case 'addresschange':
\r
689 $message = _ACTIVATE_CHANGE_MAIL;
\r
690 $subject = _ACTIVATE_CHANGE_MAILTITLE;
\r
695 // fill out variables in text
\r
697 'siteName' => $CONF['SiteName'],
\r
698 'siteUrl' => $CONF['IndexURL'],
\r
699 'memberName' => $this->getDisplayName(),
\r
700 'activationUrl' => $url,
\r
701 'activationDays' => $CONF['ActivationDays']
\r
704 $message = Template::fill($message, $aVars);
\r
705 $subject = Template::fill($subject, $aVars);
\r
708 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());
\r
710 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');
\r
715 * Member::getAdminBlogs()
\r
716 * Returns an array of all blogids for which member has admin rights
\r
719 * @return array weblog IDs in which this member has admin rights
\r
722 public function getAdminBlogs()
\r
726 if ($this->isAdmin())
\r
728 $query = 'SELECT bnumber as blogid from '.sql_table('blog');
\r
732 $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();
\r
735 $res = DB::getResult($query);
\r
736 if ( $res->rowCount() > 0 )
\r
738 foreach ( $res as $row )
\r
740 array_push($blogs, $row['blogid']);
\r
747 * Member::getTeamBlogs()
\r
748 * Returns an array of all blogids for which member has team rights
\r
750 * @param boolean $incAdmin whether checking weblog admin rights or not
\r
751 * @return array weblog IDs in which this member join
\r
754 public function getTeamBlogs($incAdmin = 1)
\r
756 $incAdmin = intval($incAdmin);
\r
759 if ( $this->isAdmin() && $incAdmin )
\r
761 $query = 'SELECT bnumber as blogid from '.sql_table('blog');
\r
765 $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();
\r
768 $res = DB::getResult($query);
\r
769 if ( $res->rowCount() > 0 )
\r
771 foreach ( $res as $row )
\r
773 array_push($blogs, $row['blogid']);
\r
780 * Member::getNotifyFromMailAddress()
\r
782 * Returns an email address from which notification of commenting/karma voting can
\r
783 * be sent. A suggestion can be given for when the member is not logged in
\r
785 * @param String $suggest
\r
786 * @return String mail address or suggestion
\r
788 public function getNotifyFromMailAddress($suggest = "")
\r
791 if ( $this->isLoggedIn() )
\r
793 return $this->getDisplayName() . " <" . $this->getEmail() . ">";
\r
795 else if ( NOTIFICATION::address_validation($suggest) )
\r
799 return $CONF['AdminEmail'];
\r
804 * Write data to database
\r
810 public function write()
\r
812 $query = 'UPDATE '.sql_table('member')
\r
813 . ' SET mname=' . DB::quoteValue($this->displayname) . ', '
\r
814 . 'mrealname='. DB::quoteValue($this->realname) . ', '
\r
815 . 'mpassword='. DB::quoteValue($this->password) . ', '
\r
816 . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '
\r
817 . 'murl=' . DB::quoteValue($this->url) . ', '
\r
818 . 'memail=' . DB::quoteValue($this->email) . ', '
\r
819 . 'madmin=' . intval($this->admin) . ', '
\r
820 . 'mnotes=' . DB::quoteValue($this->notes) . ', '
\r
821 . 'mcanlogin=' . intval($this->canlogin) . ', '
\r
822 . 'mlocale=' . DB::quoteValue($this->locale) . ', '
\r
823 . 'mautosave=' . intval($this->autosave) . ' '
\r
824 . 'WHERE mnumber=' . intval($this->id);
\r
825 DB::execute($query);
\r
829 public function checkCookieKey($key)
\r
831 return ( ($key != '') && ( $key == $this->getCookieKey() ) );
\r
834 public function checkPassword($pw)
\r
836 /* for lower compatibility (md5) */
\r
837 if ( strlen($this->password) === 32 )
\r
839 return (md5($pw) == $this->password);
\r
841 return ($this->hash($pw) == $this->password);
\r
844 public function getRealName()
\r
846 return $this->realname;
\r
849 public function setRealName($name)
\r
851 $this->realname = $name;
\r
854 public function getEmail()
\r
856 return $this->email;
\r
859 public function setEmail($email)
\r
861 $this->email = $email;
\r
864 public function getPassword()
\r
866 return $this->password;
\r
869 public function setPassword($pwd)
\r
871 $this->password = $this->hash($pwd);
\r
874 public function getCookieKey()
\r
876 return $this->cookiekey;
\r
880 * Member::newCookieKey()
\r
881 * Generate new cookiekey, save it, and return it
\r
887 public function newCookieKey()
\r
889 mt_srand( (double) microtime() * 1000000);
\r
890 $this->cookiekey = $this->hash(uniqid(mt_rand()));
\r
892 return $this->cookiekey;
\r
895 public function setCookieKey($val)
\r
897 $this->cookiekey = $val;
\r
900 public function getURL()
\r
905 public function setURL($site)
\r
907 $this->url = $site;
\r
910 public function getLocale()
\r
912 return $this->locale;
\r
915 public function setLocale($locale)
\r
917 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)
\r
918 && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )
\r
922 $this->locale = $locale;
\r
926 public function setDisplayName($nick)
\r
928 $this->displayname = $nick;
\r
931 public function getDisplayName()
\r
933 return $this->displayname;
\r
936 public function isAdmin()
\r
938 return $this->admin;
\r
941 public function setAdmin($val)
\r
943 $this->admin = $val;
\r
946 public function canLogin()
\r
948 return $this->canlogin;
\r
951 public function setCanLogin($val)
\r
953 $this->canlogin = $val;
\r
956 public function getNotes()
\r
958 return $this->notes;
\r
961 public function setNotes($val)
\r
963 $this->notes = $val;
\r
966 public function getAutosave()
\r
968 return $this->autosave;
\r
971 public function setAutosave($val)
\r
973 $this->autosave = $val;
\r
981 * @return integer id of this member object
\r
984 public function getID()
\r
991 * Returns true if there is a member with the given login name
\r
994 * @param string $name target name
\r
995 * @return boolean whether target name exists or not
\r
997 public static function exists($name)
\r
999 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));
\r
1000 return ( $r->rowCount() != 0 );
\r
1004 * Member::existsID()
\r
1005 * Returns true if there is a member with the given ID
\r
1008 * @param integer $id target id
\r
1009 * @return boolean whether target id exists or not
\r
1012 public static function existsID($id)
\r
1014 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));
\r
1015 return ( $r->rowCount() != 0 );
\r
1019 * Member::isNameProtected()
\r
1020 * Checks if a username is protected.
\r
1021 * If so, it can not be used on anonymous comments
\r
1023 * @param string $name target name
\r
1024 * @return boolean whether the name exists or not
\r
1027 public function isNameProtected($name)
\r
1030 $name = strip_tags($name);
\r
1031 $name = trim($name);
\r
1032 return self::exists($name);
\r
1036 * Member::create()
\r
1037 * Adds a new member
\r
1040 * @param String $name
\r
1041 * @param String $realname
\r
1042 * @param String $password
\r
1043 * @param String $email
\r
1044 * @param String $url
\r
1045 * @param String $admin
\r
1046 * @param String $canlogin
\r
1047 * @param String $notes
\r
1048 * @return String 1 if success, others if fail
\r
1050 static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)
\r
1052 if ( !NOTIFICATION::address_validation($email) )
\r
1054 return _ERROR_BADMAILADDRESS;
\r
1057 /* TODO: this method should be in MEMBER class, not globalfunctions */
\r
1058 if ( !isValidDisplayName($name) )
\r
1060 return _ERROR_BADNAME;
\r
1063 if ( self::exists($name) )
\r
1065 return _ERROR_NICKNAMEINUSE;
\r
1070 return _ERROR_REALNAMEMISSING;
\r
1073 /* TODO: check the number of characters */
\r
1076 return _ERROR_PASSWORDMISSING;
\r
1080 * begin if: sometimes user didn't prefix the URL with http:// or https://,
\r
1081 * this cause a malformed URL. Let's fix it.
\r
1084 if ( !preg_match('#^https?://#', $url) )
\r
1086 $url = 'http://' . $url;
\r
1089 $name = DB::quoteValue($name);
\r
1090 $realname = DB::quoteValue($realname);
\r
1091 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */
\r
1092 $password = DB::quoteValue(md5($password));
\r
1093 $email = DB::quoteValue($email);
\r
1094 $url = DB::quoteValue($url);
\r
1095 $admin = (integer) $admin;
\r
1096 $canlogin = (integer) $canlogin;
\r
1097 $notes = DB::quoteValue($notes);
\r
1099 $query = "INSERT INTO %s"
\r
1100 . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"
\r
1101 . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";
\r
1102 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);
\r
1103 DB::execute($query);
\r
1105 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);
\r
1111 * Member::getActivationInfo()
\r
1112 * Returns activation info for a certain key (an object with properties vkey, vmember, ...)
\r
1115 * @param string $key activation key
\r
1116 * @return mixed return 0 if failed, else return activation table object
\r
1119 public static function getActivationInfo($key)
\r
1121 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);
\r
1122 $res = DB::getResult($query);
\r
1124 if ( !$res || ($res->rowCount() == 0) )
\r
1128 return $res->fetch();
\r
1132 * Member::generateActivationEntry()
\r
1133 * Creates an account activation key
\r
1134 * addresschange -> old email address
\r
1136 * @param string $type one of the following values (determines what to do when activation expires)
\r
1137 * 'register' (new member registration)
\r
1138 * 'forgot' (forgotton password)
\r
1139 * 'addresschange' (member address has changed)
\r
1140 * @param string $extra extra info (needed when validation link expires)
\r
1141 * @return string activation key
\r
1143 public function generateActivationEntry($type, $extra = '')
\r
1145 // clean up old entries
\r
1146 $this->cleanupActivationTable();
\r
1148 // kill any existing entries for the current member (delete is ok)
\r
1149 // (only one outstanding activation key can be present for a member)
\r
1150 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));
\r
1152 // indicates if the member can log in while the link is active
\r
1153 $canLoginWhileActive = false;
\r
1157 $canLoginWhileActive = true;
\r
1161 case 'addresschange':
\r
1162 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );
\r
1169 // generate a random key
\r
1170 srand((double)microtime()*1000000);
\r
1171 $key = $this->hash(uniqid(rand(), true));
\r
1173 // attempt to add entry in database
\r
1174 // add in database as non-active
\r
1175 $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';
\r
1176 $query = sprintf($query
\r
1177 , sql_table('activation')
\r
1178 , DB::quoteValue($key)
\r
1179 , DB::formatDateTime()
\r
1180 , intval($this->getID())
\r
1181 , DB::quoteValue($type)
\r
1182 , DB::quoteValue($extra)
\r
1184 if ( DB::execute($query) !== FALSE )
\r
1188 // mark member as not allowed to log in
\r
1189 if ( !$canLoginWhileActive )
\r
1191 $this->setCanLogin(0);
\r
1200 * Member::activate()
\r
1201 * Inidicates that an activation link has been clicked and any forms displayed
\r
1202 * there have been successfully filled out.
\r
1204 * @param string $key activation key
\r
1208 public function activate($key)
\r
1210 // get activate info
\r
1211 $info = self::getActivationInfo($key);
\r
1219 switch ( $info['vtype'] )
\r
1225 // set canlogin value
\r
1227 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));
\r
1229 case 'addresschange':
\r
1230 // reset old 'canlogin' value
\r
1231 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);
\r
1232 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));
\r
1236 // delete from activation table
\r
1237 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));
\r
1244 * Member::cleanupActivationTable()
\r
1245 * Cleans up entries in the activation table. All entries older than 2 days are removed.
\r
1251 public function cleanupActivationTable()
\r
1254 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )
\r
1256 $actdays = intval($CONF['ActivationDays']);
\r
1260 $CONF['ActivationDays'] = 2;
\r
1262 $boundary = time() - (60 * 60 * 24 * $actdays);
\r
1264 // 1. walk over all entries, and see if special actions need to be performed
\r
1265 $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
\r
1266 $res = DB::getResult($query);
\r
1268 foreach ( $res as $row )
\r
1270 switch ( $row['vtype'] )
\r
1273 // delete all information about this site member. registration is undone because there was
\r
1274 // no timely activation
\r
1275 include_once($DIR_LIBS . 'ADMIN.php');
\r
1276 Admin::deleteOneMember(intval($row['vmember']));
\r
1278 case 'addresschange':
\r
1279 // revert the e-mail address of the member back to old address
\r
1280 list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);
\r
1281 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));
\r
1284 // delete the activation link and ignore. member can request a new password using the
\r
1285 // forgot password link
\r
1290 // 2. delete activation entries for real
\r
1291 $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
\r
1292 DB::execute($query);
\r
1297 * Member::$language
\r
1304 public $language = '';
\r
1306 * Member::getLanguage()
\r
1313 public function getLanguage()
\r
1315 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )
\r
1326 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
1327 * Copyright (C) 2002-2009 The Nucleus Group
1329 * This program is free software; you can redistribute it and/or
1330 * modify it under the terms of the GNU General Public License
1331 * as published by the Free Software Foundation; either version 2
1332 * of the License, or (at your option) any later version.
1333 * (see nucleus/documentation/index.html#license for more info)
1336 * A class representing site members
1338 * @license http://nucleuscms.org/license.txt GNU General Public License
1339 * @copyright Copyright (C) 2002-2009 The Nucleus Group
1340 * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $
1344 // 1 when authenticated, 0 when not
1345 public $loggedin = 0;
1346 public $password; // not the actual password, but rather a MD5 hash
1347 private $algorism = 'md5';
1349 public $cookiekey; // value that should also be in the client cookie to allow authentication
1350 private $cookie_salt = FALSE;
1355 public $displayname;
1358 public $admin = 0; // (either 0 or 1)
1359 public $canlogin = 0; // (either 0 or 1)
1361 public $autosave = 1; // if the member use the autosave draft function
1362 public $adminskin = 0; // if the member use the autosave draft function
1363 public $bookmarklet = 0; // if the member use the autosave draft function
1364 private $locale = '';
1367 * Member::__construct()
1368 * Constructor for a member object
1374 public function __construct()
1380 * Member::createFromName()
1381 * Create a member object for a given displayname
1384 * @param String $displayname login name
1385 * @return Object member object
1388 public static function &createFromName($displayname)
1390 $mem = new Member();
1391 $mem->readFromName($displayname);
1396 * Member::createFromID()
1397 * Create a member object for a given ID
1400 * @param Integer $id id for member
1402 public static function &createFromID($id)
1404 $mem = new Member();
1405 $mem->readFromID($id);
1410 * Member::readFromName()
1411 * Read member table in database
1413 * @param String $displayname login name
1414 * @return Object SQL resource
1417 public function readFromName($displayname)
1419 return $this->read('mname='.DB::quoteValue($displayname));
1423 * Member::readFromID()
1424 * Read member table in database
1426 * @param Integer $id id for member
1427 * @return Object SQL resource
1430 public function readFromID($id)
1432 return $this->read("mnumber=" . intval($id));
1437 * hash the target string
1439 * @param String $string target string
1440 * @return Void hashed string
1442 public function hash($string)
1444 switch ( $this->algorism )
1448 $string = md5($string);
1454 * Member::set_cookie_salt()
1456 * @param integer $key secureCookieKey value
1460 private function set_cookie_salt($key = 0)
1470 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
1473 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
1476 $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
1479 $this->cookie_salt = serverVar('REMOTE_ADDR');
1482 $this->cookie_salt = 'none';
1489 * Tries to login as a given user.
1490 * Returns true when succeeded, returns false when failed
1491 * 3.40 adds CustomLogin event
1493 * @param String $login login name for member
1494 * @param String $password password for member
1495 * @param Integer $shared whether the user agent is shared or not
1498 public function login($login, $password, $shared=1)
1500 global $CONF, $errormessage, $manager;
1502 /* TODO: validation for $login, $password, $shared */
1503 if ( $login == '' || $password == '' )
1507 /* limiting the length of password to avoid hash collision */
1508 $password=i18n::substr($password, 0, 40);
1511 * generate cookie salt from secure cookie key settings
1512 * (either 'none', 0, 8, 16, 24, or 32)
1514 if ( !$this->cookie_salt )
1517 if ( array_key_exists('secureCookieKey', $CONF) )
1519 $salt = $CONF['secureCookieKey'];
1521 $this->set_cookie_salt($salt);
1526 $data = array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal);
1527 $manager->notify('CustomLogin', $data);
1529 $this->loggedin = 0;
1532 $this->loggedin = ( $this->readFromName($login) );
1534 elseif ( $allowlocal )
1536 $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );
1540 if ( !$this->loggedin )
1542 $trimlogin = trim($login);
1543 if ( empty($trimlogin) )
1545 $errormessage = "Please enter a username.";
1549 $errormessage = 'Login failed for ' . $login;
1551 $data = array('username' => $login);
1552 $manager->notify('LoginFailed', $data);
1553 ActionLog::add(INFO, $errormessage);
1558 /* For lower compatibility */
1559 if ( strlen($this->password) === 32 )
1561 $this->password = $this->hash($password);
1564 $this->newCookieKey();
1565 $this->setCookies($shared);
1567 if ( $this->cookie_salt !== 'none' )
1569 /* secure cookie key */
1570 $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));
1575 $data = array('member' => &$this, 'username' => $login);
1576 $manager->notify('LoginSuccess', $data);
1577 ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");
1580 return $this->loggedin;
1584 * Member::cookielogin()
1585 * Login using cookie key
1587 * @param String $login not used
1588 * @param String $cookiekey not used
1589 * @return Boolean login or not
1591 public function cookielogin($login='', $cookiekey='')
1593 global $CONF, $manager;
1595 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
1597 /* Cookie Authentication */
1598 $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");
1600 /* TODO: validation for each cookie values */
1602 /* limiting the length of password to avoid hash collision */
1603 $ck = i18n::substr($ck,0,32);
1606 * generate cookie salt from secure cookie key settings
1607 * (either 'none', 0, 8, 16, 24, or 32)
1609 if ( !$this->cookie_salt )
1612 if ( array_key_exists('secureCookieKey', $CONF) )
1614 $salt = $CONF['secureCookieKey'];
1616 $this->set_cookie_salt($salt);
1619 if ( $this->cookie_salt !== 'none' )
1621 $ck = $this->hash($ck . $this->cookie_salt);
1623 $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );
1626 /* renew cookies when not on a shared computer */
1627 if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )
1629 $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));
1630 $this->setCookies();
1633 return $this->loggedin;
1638 * logout and expire cookie
1643 public function logout()
1645 global $CONF, $manager;
1647 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
1649 /* remove cookies on logout */
1650 setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1651 setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1652 $data = array('username' => cookieVar("{$CONF['CookiePrefix']}user"));
1653 $manager->notify('Logout', $data);
1656 $this->loggedin = 0;
1661 * Member::isLoggedIn()
1662 * return member is loggedin or not
1667 public function isLoggedIn()
1669 return $this->loggedin;
1674 * Read member information from the database
1676 * @param String $where where statement
1677 * @return Resource SQL resource
1680 public function read($where)
1683 $query = 'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;
1685 $row = DB::getRow($query);
1687 $this->setRealName($row['mrealname']);
1688 $this->setEmail($row['memail']);
1689 $this->password = $row['mpassword'];
1690 $this->setCookieKey($row['mcookiekey']);
1691 $this->setURL($row['murl']);
1692 $this->setDisplayName($row['mname']);
1693 $this->setAdmin($row['madmin']);
1694 $this->id = $row['mnumber'];
1695 $this->setCanLogin($row['mcanlogin']);
1696 $this->setNotes($row['mnotes']);
1697 $this->setLocale($row['mlocale']);
1698 $this->setAutosave($row['mautosave']);
1699 $this->setAdminSkin($row['madminskin']);
1700 $this->setBookmarklet($row['mbkmklt']);
1702 return $row ? TRUE : FALSE;
1706 * Member::isBlogAdmin()
1707 * Returns true if member is an admin for the given blog
1708 * (returns false if not a team member)
1710 * @param Integer $blogid weblog id
1711 * @return Integer weblog admin or not
1714 public function isBlogAdmin($blogid)
1716 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'
1717 . ' tblog=' . intval($blogid)
1718 . ' and tmember='. $this->getID();
1719 $res = DB::getValue($query);
1727 * Member::blogAdminRights()
1729 * @param integer $blogid ID of target weblog
1730 * @return boolean whether to have admin rights to the weblog or not
1733 public function blogAdminRights($blogid)
1735 return ($this->isAdmin() || $this->isBlogAdmin($blogid));
1739 * Member::teamRights()
1741 * @param integer $blogid ID of target weblog
1742 * @return boolean whether to have admin right to the weblog or not
1745 public function teamRights($blogid)
1747 return ($this->isAdmin() || $this->isTeamMember($blogid));
1751 * Member::isTeamMember()
1752 * Returns true if this member is a team member of the given blog
1754 * @param integer $blogid ID of target weblog
1755 * @return boolean whether to join the weblog or not
1758 public function isTeamMember($blogid)
1760 $query = 'SELECT * FROM '.sql_table('team').' WHERE'
1761 . ' tblog=' . intval($blogid)
1762 . ' and tmember='. $this->getID();
1763 $res = DB::getResult($query);
1764 return ($res->rowCount() != 0);
1768 * Member::canAddItem()
1770 * @param integer $catid ID of target category
1771 * @return boolean whether to be able to add items to the category or not
1774 public function canAddItem($catid)
1778 // if this is a 'newcat' style newcat
1779 // no blog admin of destination blog -> NOK
1780 // blog admin of destination blog -> OK
1781 if ( i18n::strpos($catid,'newcat') === 0 )
1784 list($blogid) = sscanf($catid,"newcat-%d");
1785 return $this->blogAdminRights($blogid);
1788 // category does not exist -> NOK
1789 if ( !$manager->existsCategory($catid) )
1794 $blogid = getBlogIDFromCatID($catid);
1796 // no team rights for blog -> NOK
1797 if (!$this->teamRights($blogid))
1802 // all other cases: OK
1807 * Member::canAlterComment()
1808 * Returns true if this member can edit/delete a commentitem. This can be in the
1810 * - member is a super-admin
1811 * - member is the author of the comment
1812 * - member is admin of the blog associated with the comment
1813 * - member is author of the item associated with the comment
1815 * @param integer $commentid ID of target comment
1816 * @return boolean delete/edit the comment or not
1819 public function canAlterComment($commentid)
1821 if ( $this->isAdmin() )
1826 $query = 'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'
1827 . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')
1828 . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);
1829 $res = DB::getRow($query);
1831 return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());
1835 * Member::canAlterItem()
1836 * Returns true if this member can edit/delete an item. This is true in the following
1837 * cases: - member is a super-admin
1838 * - member is the author of the item
1839 * - member is admin of the the associated blog
1841 * @param integer $itemid ID of target item
1842 * @return boolean delete/edit the item or not
1845 public function canAlterItem($itemid)
1847 if ($this->isAdmin()) return 1;
1849 $query = 'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);
1850 $res = DB::getRow($query);
1851 return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);
1855 * Member::canBeDeleted()
1856 * Return true if member can be deleted. This means that there are no items posted by the member left
1859 * @return boolean whether there is no items or exists
1862 public function canBeDeleted()
1864 $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());
1865 return ( $res->rowCount() == 0 );
1869 * Member::canUpdateItem()
1870 * returns true if this member can move/update an item to a given category,
1871 * false if not (see comments fot the tests that are executed)
1873 * @param integer $itemid
1874 * @param string $newcat (can also be of form 'newcat-x' with x=blogid)
1875 * @return boolean whether being able to update the item or not
1878 public function canUpdateItem($itemid, $newcat)
1882 // item does not exists -> NOK
1883 if ( !$manager->existsItem($itemid,1,1) )
1888 // cannot alter item -> NOK
1889 if (!$this->canAlterItem($itemid))
1894 // if this is a 'newcat' style newcat
1895 // no blog admin of destination blog -> NOK
1896 // blog admin of destination blog -> OK
1897 if ( i18n::strpos($newcat, 'newcat') === 0 )
1900 list($blogid) = sscanf($newcat, 'newcat-%d');
1901 return $this->blogAdminRights($blogid);
1904 // category does not exist -> NOK
1905 if (!$manager->existsCategory($newcat))
1911 $item =& $manager->getItem($itemid,1,1);
1913 // old catid = new catid -> OK
1914 if ($item['catid'] == $newcat)
1919 // not a valid category -> NOK
1920 $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));
1926 // get destination blog
1927 $item =& $manager->getItem($itemid, 1, 1);
1928 $source_blogid = $item['blogid'];
1929 $dest_blogid = getBlogIDFromCatID($newcat);
1931 // not a team member of destination blog -> NOK
1932 if ( !$this->teamRights($dest_blogid) )
1937 // if member is author of item -> OK
1938 if ( $item['authorid'] == $this->getID() )
1943 // if member has admin rights on both blogs: OK
1944 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )
1949 // all other cases: NOK
1954 * Member::setCookies()
1955 * Sets the cookies for the member
1957 * @param boolean $shared set this to 1 when using a shared computer. Cookies will expire
1958 * at the end of the session in this case.
1962 public function setCookies($shared = 0)
1966 if ( $CONF['SessionCookie'] || $shared )
1972 $lifetime = time()+2592000;
1975 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1976 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1978 // make sure cookies on shared pcs don't get renewed
1981 setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
1987 * Member::sendActivationLink()
1988 * Send activation mail
1990 * @param string $type activation type
1991 * @param string $extra extra info
1994 public function sendActivationLink($type, $extra='')
1998 if ( !isset($CONF['ActivationDays']) )
2000 $CONF['ActivationDays'] = 2;
2003 // generate key and URL
2004 $key = $this->generateActivationEntry($type, $extra);
2005 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;
2007 // choose text to use in mail
2011 $message = _ACTIVATE_REGISTER_MAIL;
2012 $subject = _ACTIVATE_REGISTER_MAILTITLE;
2015 $message = _ACTIVATE_FORGOT_MAIL;
2016 $subject = _ACTIVATE_FORGOT_MAILTITLE;
2018 case 'addresschange':
2019 $message = _ACTIVATE_CHANGE_MAIL;
2020 $subject = _ACTIVATE_CHANGE_MAILTITLE;
2025 // fill out variables in text
2027 'siteName' => $CONF['SiteName'],
2028 'siteUrl' => $CONF['IndexURL'],
2029 'memberName' => $this->getDisplayName(),
2030 'activationUrl' => $url,
2031 'activationDays' => $CONF['ActivationDays']
2034 $message = Template::fill($message, $aVars);
2035 $subject = Template::fill($subject, $aVars);
2038 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());
2040 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');
2045 * Member::getAdminBlogs()
2046 * Returns an array of all blogids for which member has admin rights
2049 * @return array weblog IDs in which this member has admin rights
2052 public function getAdminBlogs()
2056 if ($this->isAdmin())
2058 $query = 'SELECT bnumber as blogid from '.sql_table('blog');
2062 $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();
2065 $res = DB::getResult($query);
2066 if ( $res->rowCount() > 0 )
2068 foreach ( $res as $row )
2070 array_push($blogs, $row['blogid']);
2077 * Member::getTeamBlogs()
2078 * Returns an array of all blogids for which member has team rights
2080 * @param boolean $incAdmin whether checking weblog admin rights or not
2081 * @return array weblog IDs in which this member join
2084 public function getTeamBlogs($incAdmin = 1)
2086 $incAdmin = intval($incAdmin);
2089 if ( $this->isAdmin() && $incAdmin )
2091 $query = 'SELECT bnumber as blogid from '.sql_table('blog');
2095 $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();
2098 $res = DB::getResult($query);
2099 if ( $res->rowCount() > 0 )
2101 foreach ( $res as $row )
2103 array_push($blogs, $row['blogid']);
2110 * Member::getNotifyFromMailAddress()
2112 * Returns an email address from which notification of commenting/karma voting can
2113 * be sent. A suggestion can be given for when the member is not logged in
2115 * @param String $suggest
2116 * @return String mail address or suggestion
2118 public function getNotifyFromMailAddress($suggest = "")
2121 if ( $this->isLoggedIn() )
2123 return $this->getDisplayName() . " <" . $this->getEmail() . ">";
2125 else if ( NOTIFICATION::address_validation($suggest) )
2129 return $CONF['AdminEmail'];
2134 * Write data to database
2140 public function write()
2142 $query = 'UPDATE '.sql_table('member')
2143 . ' SET mname=' . DB::quoteValue($this->displayname) . ', '
2144 . 'mrealname='. DB::quoteValue($this->realname) . ', '
2145 . 'mpassword='. DB::quoteValue($this->password) . ', '
2146 . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '
2147 . 'murl=' . DB::quoteValue($this->url) . ', '
2148 . 'memail=' . DB::quoteValue($this->email) . ', '
2149 . 'madmin=' . intval($this->admin) . ', '
2150 . 'mnotes=' . DB::quoteValue($this->notes) . ', '
2151 . 'mcanlogin=' . intval($this->canlogin) . ', '
2152 . 'mlocale=' . DB::quoteValue($this->locale) . ', '
2153 . 'madminskin=' . DB::quoteValue($this->adminskin) . ', '
2154 . 'mbkmklt=' . DB::quoteValue($this->bookmarklet) . ', '
2155 . 'mautosave=' . intval($this->autosave) . ' '
2156 . 'WHERE mnumber=' . intval($this->id);
2157 DB::execute($query);
2161 public function checkCookieKey($key)
2163 return ( ($key != '') && ( $key == $this->getCookieKey() ) );
2166 public function checkPassword($pw)
2168 /* for lower compatibility (md5) */
2169 if ( strlen($this->password) === 32 )
2171 return (md5($pw) == $this->password);
2173 return ($this->hash($pw) == $this->password);
2176 public function getRealName()
2178 return $this->realname;
2181 public function setRealName($name)
2183 $this->realname = $name;
2186 public function getEmail()
2188 return $this->email;
2191 public function setEmail($email)
2193 $this->email = $email;
2196 public function getPassword()
2198 return $this->password;
2201 public function setPassword($pwd)
2203 $this->password = $this->hash($pwd);
2206 public function getCookieKey()
2208 return $this->cookiekey;
2212 * Member::newCookieKey()
2213 * Generate new cookiekey, save it, and return it
2219 public function newCookieKey()
2221 mt_srand( (double) microtime() * 1000000);
2222 $this->cookiekey = $this->hash(uniqid(mt_rand()));
2224 return $this->cookiekey;
2227 public function setCookieKey($val)
2229 $this->cookiekey = $val;
2232 public function getURL()
2237 public function setURL($site)
2242 public function setAdminSkin($skin)
2244 $this->adminskin = $skin;
2247 public function setBookmarklet($skin)
2249 $this->bookmarklet = $skin;
2252 public function getAdminSkin()
2254 return $this->adminskin;
2257 public function getBookmarklet()
2259 return $this->bookmarklet;
2262 public function getLocale()
2264 return $this->locale;
2267 public function setLocale($locale)
2269 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)
2270 && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )
2274 $this->locale = $locale;
2278 public function setDisplayName($nick)
2280 $this->displayname = $nick;
2283 public function getDisplayName()
2285 return $this->displayname;
2288 public function isAdmin()
2290 return $this->admin;
2293 public function setAdmin($val)
2295 $this->admin = $val;
2298 public function canLogin()
2300 return $this->canlogin;
2303 public function setCanLogin($val)
2305 $this->canlogin = $val;
2308 public function getNotes()
2310 return $this->notes;
2313 public function setNotes($val)
2315 $this->notes = $val;
2318 public function getAutosave()
2320 return $this->autosave;
2323 public function setAutosave($val)
2325 $this->autosave = $val;
2333 * @return integer id of this member object
2336 public function getID()
2343 * Returns true if there is a member with the given login name
2346 * @param string $name target name
2347 * @return boolean whether target name exists or not
2349 public static function exists($name)
2351 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));
2352 return ( $r->rowCount() != 0 );
2356 * Member::existsID()
2357 * Returns true if there is a member with the given ID
2360 * @param integer $id target id
2361 * @return boolean whether target id exists or not
2364 public static function existsID($id)
2366 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));
2367 return ( $r->rowCount() != 0 );
2371 * Member::isNameProtected()
2372 * Checks if a username is protected.
2373 * If so, it can not be used on anonymous comments
2375 * @param string $name target name
2376 * @return boolean whether the name exists or not
2379 public function isNameProtected($name)
2382 $name = strip_tags($name);
2383 $name = trim($name);
2384 return self::exists($name);
2392 * @param String $name
2393 * @param String $realname
2394 * @param String $password
2395 * @param String $email
2396 * @param String $url
2397 * @param String $admin
2398 * @param String $canlogin
2399 * @param String $notes
2400 * @return String 1 if success, others if fail
2402 static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)
2404 if ( !NOTIFICATION::address_validation($email) )
2406 return _ERROR_BADMAILADDRESS;
2409 /* TODO: this method should be in MEMBER class, not globalfunctions */
2410 if ( !isValidDisplayName($name) )
2412 return _ERROR_BADNAME;
2415 if ( self::exists($name) )
2417 return _ERROR_NICKNAMEINUSE;
2422 return _ERROR_REALNAMEMISSING;
2425 /* TODO: check the number of characters */
2428 return _ERROR_PASSWORDMISSING;
2432 * begin if: sometimes user didn't prefix the URL with http:// or https://,
2433 * this cause a malformed URL. Let's fix it.
2436 if ( !preg_match('#^https?://#', $url) )
2438 $url = 'http://' . $url;
2441 $name = DB::quoteValue($name);
2442 $realname = DB::quoteValue($realname);
2443 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */
2444 $password = DB::quoteValue(md5($password));
2445 $email = DB::quoteValue($email);
2446 $url = DB::quoteValue($url);
2447 $admin = (integer) $admin;
2448 $canlogin = (integer) $canlogin;
2449 $notes = DB::quoteValue($notes);
2451 $query = "INSERT INTO %s"
2452 . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"
2453 . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";
2454 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);
2455 DB::execute($query);
2457 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);
2463 * Member::getActivationInfo()
2464 * Returns activation info for a certain key (an object with properties vkey, vmember, ...)
2467 * @param string $key activation key
2468 * @return mixed return 0 if failed, else return activation table object
2471 public static function getActivationInfo($key)
2473 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);
2474 $res = DB::getResult($query);
2476 if ( !$res || ($res->rowCount() == 0) )
2480 return $res->fetch();
2484 * Member::generateActivationEntry()
2485 * Creates an account activation key
2486 * addresschange -> old email address
2488 * @param string $type one of the following values (determines what to do when activation expires)
2489 * 'register' (new member registration)
2490 * 'forgot' (forgotton password)
2491 * 'addresschange' (member address has changed)
2492 * @param string $extra extra info (needed when validation link expires)
2493 * @return string activation key
2495 public function generateActivationEntry($type, $extra = '')
2497 // clean up old entries
2498 $this->cleanupActivationTable();
2500 // kill any existing entries for the current member (delete is ok)
2501 // (only one outstanding activation key can be present for a member)
2502 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));
2504 // indicates if the member can log in while the link is active
2505 $canLoginWhileActive = false;
2509 $canLoginWhileActive = true;
2513 case 'addresschange':
2514 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );
2521 // generate a random key
2522 srand((double)microtime()*1000000);
2523 $key = $this->hash(uniqid(rand(), true));
2525 // attempt to add entry in database
2526 // add in database as non-active
2527 $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';
2528 $query = sprintf($query
2529 , sql_table('activation')
2530 , DB::quoteValue($key)
2531 , DB::formatDateTime()
2532 , intval($this->getID())
2533 , DB::quoteValue($type)
2534 , DB::quoteValue($extra)
2536 if ( DB::execute($query) !== FALSE )
2540 // mark member as not allowed to log in
2541 if ( !$canLoginWhileActive )
2543 $this->setCanLogin(0);
2552 * Member::activate()
2553 * Inidicates that an activation link has been clicked and any forms displayed
2554 * there have been successfully filled out.
2556 * @param string $key activation key
2560 public function activate($key)
2562 // get activate info
2563 $info = self::getActivationInfo($key);
2571 switch ( $info['vtype'] )
2577 // set canlogin value
2579 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));
2581 case 'addresschange':
2582 // reset old 'canlogin' value
2583 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);
2584 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));
2588 // delete from activation table
2589 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));
2596 * Member::cleanupActivationTable()
2597 * Cleans up entries in the activation table. All entries older than 2 days are removed.
2603 public function cleanupActivationTable()
2606 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )
2608 $actdays = intval($CONF['ActivationDays']);
2612 $CONF['ActivationDays'] = 2;
2614 $boundary = time() - (60 * 60 * 24 * $actdays);
2616 // 1. walk over all entries, and see if special actions need to be performed
2617 $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
2618 $res = DB::getResult($query);
2620 foreach ( $res as $row )
2622 switch ( $row['vtype'] )
2625 // delete all information about this site member. registration is undone because there was
2626 // no timely activation
2627 include_once($DIR_LIBS . 'ADMIN.php');
2628 Admin::deleteOneMember(intval($row['vmember']));
2630 case 'addresschange':
2631 // revert the e-mail address of the member back to old address
2632 list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);
2633 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));
2636 // delete the activation link and ignore. member can request a new password using the
2637 // forgot password link
2642 // 2. delete activation entries for real
2643 $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
2644 DB::execute($query);
2656 public $language = '';
2658 * Member::getLanguage()
2665 public function getLanguage()
2667 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )
2674 >>>>>>> skinnable-master