00001 <?
00012 abstract class BaseUser extends MyObject
00013 {
00018 public $their;
00019
00024 public $pronoun;
00025
00029 public $profileImage = null;
00030
00034 public static $authCacheLife = 86400;
00035
00041 public function __construct($data = null, $table = 'users')
00042 {
00043 $this->hasAuthor = false;
00044 $this->profileImage = new Image();
00045
00046 parent::__construct($data, $table);
00047
00048
00049 $this->pronoun = 'them';
00050 $this->their = 'their';
00051 if ($this->gender == 'female')
00052 {
00053 $this->pronoun = 'her';
00054 $this->their = 'her';
00055 }
00056 else if ($this->gender == 'male')
00057 {
00058 $this->pronoun = 'him';
00059 $this->their = 'his';
00060 }
00061
00062
00063 if (!Config::get('auth_secret'))
00064 throw new Exception('You must Config::set(\'auth_secret\').');
00065
00066
00067 $this->fullTextFields[] = 'first_name';
00068 $this->fullTextFields[] = 'last_name';
00069 }
00070
00076 public function isAdmin()
00077 {
00078 return (bool)$this->is_admin;
00079 }
00080
00084 public function initLoginPage()
00085 {
00086 global $me;
00087
00088 $this->pageTitle = 'Login';
00089
00090 if ($this->params('status'))
00091 $this->addStatus($this->params('status'));
00092
00093 if ($me->id)
00094 throw new PageError('You cannot login if you are already logged in. Please log our first.');
00095 }
00096
00100 public function initViewPage()
00101 {
00102 global $me;
00103
00104
00105 if (!$this->params('id'))
00106 {
00107 if ($me->id)
00108 $this->setParam('id', $me->id);
00109
00110 else
00111 $this->assertLogin();
00112 }
00113
00114
00115 parent::initViewPage();
00116 }
00117
00121 public function drawLoginPage()
00122 {
00123
00124 $form = $this->createLoginForm($this->redir);
00125 $this->processLoginForm($form);
00126 $email = $form->getData('username');
00127
00128
00129 $form->drawIfNeeded();
00130
00131
00132 echo "<br/>\n";
00133 echo $this->getLink(".register?email=$email", 'Register') . " | ";
00134 echo $this->getLink(".lostpass?email=$email", 'Lost Password');
00135 }
00136
00141 public function processLoginForm($form)
00142 {
00143 if ($form->isSubmittedAndValid())
00144 {
00145 if ($this->authenticate($form->getData('username'), $form->getData('password'), $form->getData('remember_me')))
00146 {
00147 if ($form->getData('redirect'))
00148 Util::redirect(base64_decode($form->getData('redirect')));
00149 else
00150 Util::redirect($this->getUrl('main'));
00151 }
00152 else
00153 {
00154 $email = $form->getData('username');
00155 $userId = $this->accountExists($email);
00156 if ($userId)
00157 {
00158 $this->id = $userId;
00159 if (Config::get("users_must_activate") && $this->activation_key != '')
00160 $form->setError('username', "
00161 Login failed.<br/>
00162 You must " .
00163 $this->getLink(".activatehelp?email=$email", 'activate') .
00164 ' your account first.'
00165 );
00166 else
00167 $form->setError('password', "
00168 Login failed, please try again.
00169 Remember: passwords are case sensitive.
00170 ");
00171 }
00172 else
00173 $form->setError('username', "
00174 Account does not exist<br>
00175 You may " . $this->getLink(".register?email=$email", 'register') . ' it.'
00176 );
00177 }
00178 }
00179 }
00180
00188 public function createLoginForm($redirect = null)
00189 {
00190 $form = new Form(new OneColumnTableLayout());
00191 $form->action = $this->getUrl(".login");
00192 $form->name = 'loginForm';
00193
00194 $form->add('HiddenField', 'redirect', array(
00195 'value' => $redirect
00196 ));
00197
00198
00199 $this->loginFormAddUsernameField($form);
00200
00201 $form->add('PasswordField', 'password', array(
00202 'required' => true,
00203 'width' => '100%',
00204 'maxlength' => 255,
00205 'title' => 'Password'
00206 ));
00207
00208 $form->add('CheckboxField', 'remember_me', array(
00209 'label' => 'Remember Me',
00210 'value' => 1
00211 ));
00212
00213 $form->addSubmit('Login');
00214
00215 return $form;
00216 }
00217
00223 protected function loginFormAddUsernameField($form)
00224 {
00225 $form->add('TextField', 'username', array(
00226 'required' => true,
00227 'width' => '100%',
00228 'maxlength' => 255,
00229 'title' => 'Email'
00230 ));
00231 }
00232
00238 static public function makePassword($len = 8)
00239 {
00240
00241 $letter = array(
00242 array("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"),
00243 array("a","e","i","o","u")
00244 );
00245
00246
00247 $pass = "";
00248
00249
00250 $set = round(rand(0,1));
00251 for ($i=0; $i<$len; $i++)
00252 {
00253
00254 $set = !$set;
00255
00256
00257 $idx = floor(rand(0, count($letter[$set])-1));
00258
00259
00260 $pass .= $letter[$set][$idx];
00261 }
00262
00263 return $pass;
00264 }
00265
00269 function initLogoutPage()
00270 {
00271 global $me;
00272
00273 if ($me->id)
00274 $me->logoutProcess();
00275
00276 Util::redirect($this->baseUrl);
00277 }
00278
00282 function drawLogoutPage()
00283 {
00284 echo 'You have been logged out.';
00285 }
00286
00290 function initRegisterPage()
00291 {
00292 global $me;
00293
00294 $this->pageTitle = 'Register New Account';
00295
00296 if ($me->id)
00297 throw new PageError('You are logged in, you don\'t need another account. Please log out first.');
00298 }
00299
00305 function createRegisterForm()
00306 {
00307 $form = new Form();
00308 $form->action = $this->getUrl(".register");
00309
00310
00311 $this->registerFormAddFields($form);
00312
00313 return $form;
00314 }
00315
00323 protected function registerFormAddFields($form)
00324 {
00325 $form->add('EmailField', 'email', array(
00326 'required' => true,
00327 ));
00328 $form->add('TextField', 'username', array(
00329 'required' => true,
00330 'size' => 50
00331 ));
00332 $form->add('PasswordField', 'password1', array(
00333 'required' => true,
00334 'title' => 'Password',
00335 ));
00336 $form->add('PasswordField', 'password2', array(
00337 'required' => true,
00338 'title' => 'Password Again',
00339 ));
00340
00341 if ($this->hasField('birthday'))
00342 $form->add('DateField', 'birthday', array(
00343 'required' => true,
00344 'beginYear' => 1900,
00345 'endYear' => date('Y')
00346 ));
00347
00348 $form->add('CheckboxField', 'read_tos', array(
00349 'title' => '',
00350 'label' => 'I have read the <a href=\'' . $this->getUrl(array('module' => 'main', 'page' => 'tos')) .
00351 '\' target=\'_blank\'>Terms of Service</a>.'
00352 ));
00353 $form->add('CheckboxField', 'am_of_age', array(
00354 'title' => '',
00355 'label' => 'I am at least <b>' . Config::get('minimum_age') . ' years</b> of age.'
00356 ));
00357
00358 $form->addSubmit("Register");
00359
00360 return $form;
00361 }
00362
00366 function drawRegisterPage()
00367 {
00368 $form = $this->createRegisterForm();
00369
00370 if ($form->isSubmitted())
00371 {
00372 $this->validateRegistration($form);
00373
00374 if (!$form->hasError())
00375 $this->registrationPostSuccess($form);
00376 }
00377 else
00378 {
00379 $email = $this->params('email');
00380 if ($email)
00381 $form->setData(array('email' => $email));
00382 }
00383
00384
00385 if ($form->needsDrawn())
00386 $form->drawAll();
00387 }
00388
00394 protected function validateRegistration($form)
00395 {
00396
00397 $form->validate();
00398
00399
00400 if (!$form->getData('read_tos'))
00401 $form->setError('read_tos', 'You must read and agree to the Terms of Service');
00402
00403
00404 if ($form->getData('password1') != $form->getData('password2'))
00405 $form->setError('password2', 'Your passwords must match.');
00406
00407
00408 $parentPerm = $form->getData('am_of_age');
00409 if (!$parentPerm)
00410 $form->setError('am_of_age', 'By law, we cannot allow
00411 people under ' . Config::get('minimum_age') . ' to access the site. You must
00412 agree that you are able to access this site.');
00413
00414
00415 $year = date("Y") - Config::get('minimum_age');
00416 $month = date("m");
00417 $day = date("d");
00418 $time = strtotime("$year-$month-$day");
00419
00420
00421 $bday = $form->getData('birthday');
00422 $bdayTime = strtotime($bday);
00423
00424
00425 if ($bdayTime > $time)
00426 $form->setError('birthday', 'You are not old enough to access this site.');
00427
00428
00429 if ($this->accountExists($form->getData('email')))
00430 $form->setError('email',
00431 'That email has already been registered. If it is yours, <br/>you may <a href="' .
00432 $this->getUrl(".login") . '">login</a> or if you forgot it, <a href="' .
00433 $this->getUrl(".lostpass?email=" . $form->getData("email")) . '">reset your password.</a>');
00434
00435
00436 if ($form->hasField('username'))
00437 if ($this->usernameExists($form->getData('username')))
00438 $form->setError('username', 'That username already exists.');
00439 }
00440
00447 protected function usernameExists($username)
00448 {
00449 return (bool)dbGetNumRows(dbQuery("
00450 SELECT id
00451 FROM users
00452 WHERE username = '$username'
00453 "));
00454 }
00455
00461 protected function registrationPostSuccess($form)
00462 {
00463 global $me;
00464
00465
00466 $pass = $form->getData('password1');
00467 $encPass = sha1($pass);
00468 $this->password = $encPass;
00469
00470
00471 $this->editPagePostSaveSync($form);
00472
00473
00474 if (Config::get('users_must_activate'))
00475 $this->sendActivationEmail();
00476
00477 else
00478 {
00479 $this->doLogin();
00480 Util::redirect($this->getUrl('.main'));
00481 }
00482 }
00483
00490 function accountExists($email)
00491 {
00492 if ($ar = dbFetchAssoc(dbQuery("
00493 SELECT id
00494 FROM $this->tableName
00495 WHERE email = '$email'
00496 ")))
00497 return $ar['id'];
00498 else
00499 return false;
00500 }
00501
00505 function initEditPage()
00506 {
00507 global $me;
00508
00509
00510 $this->assertLogin();
00511
00512
00513 $this->id = $this->params('id');
00514
00515
00516 if (!$this->id)
00517 $this->id = $me->id;
00518
00519
00520 if (!$this->canEdit())
00521 throw new PageError('You do not have permission to edit this profile.');
00522 else
00523 $this->pageTitle = 'Edit Profile';
00524 }
00525
00534 function editFormValidate($form)
00535 {
00536 parent::editFormValidate($form);
00537
00538 $email = trim($form->getData('email'));
00539 if (strlen($email) && $email != $this->email)
00540 {
00541 if ($this->accountExists($email))
00542 $form->setError('email', 'That email is already in use.');
00543 }
00544 }
00545
00554 function editFormAddFields($form)
00555 {
00556 global $me;
00557
00558 if ($me->isAdmin() && $this->hasField('is_admin'))
00559 $form->add('CheckboxField', 'is_admin');
00560
00561 if ($this->hasField('first_name') && $this->hasField('last_name'))
00562 $form->add('NameField', 'name', array(
00563 'required' => true
00564 ));
00565
00566 if ($this->hasField('email'))
00567 $form->add('EmailField', 'email', array(
00568 'required' => true,
00569 'size' => 45,
00570 'maxlength' => 255
00571 ));
00572
00573 if ($this->hasField('birthday'))
00574 {
00575 $form->add('DateField', 'birthday', array(
00576 'beginYear' => 1900,
00577 'endYear' => date('Y')
00578 ));
00579 }
00580
00581 if ($this->hasField('gender'))
00582 $form->add('GenderField', 'gender', array());
00583
00584 if ($this->hasField('website'))
00585 $form->add('TextField', 'website', array(
00586 'size' => 45,
00587 'maxlength' => 255
00588 ));
00589
00590 if ($this->hasField('phone_number'))
00591 $form->add('PhoneField', 'phone_number', array(
00592 ));
00593
00594 if ($this->hasField('cell_number'))
00595 $form->add('PhoneField', 'cell_number', array(
00596 'title' => 'Cellphone Number'
00597 ));
00598
00599 if ($this->hasField('street'))
00600 $form->add('TextField', 'street', array(
00601 'size' => 45,
00602 'maxlength' => 255,
00603 'title' => 'Street Address'
00604 ));
00605
00606 if ($this->hasField('city') && $this->hasField('state') && $this->hasField('zip'))
00607 $form->add('CityStateZipField', 'citystzip', array(
00608 'title' => 'City / State / Zip'
00609 ));
00610
00611 if ($this->hasField('about_me'))
00612 $form->add('EditorField', 'about_me', array(
00613 "width" => '100%',
00614 "height" => '300px',
00615 "title" => 'About Me'
00616 ));
00617 }
00618
00626 function editPagePostSaveSync($form)
00627 {
00628 global $me;
00629
00630
00631 if ($form->hasField('name') && $this->hasField('first_name') && $this->hasField('last_name'))
00632 {
00633 $name = $form->getData('name');
00634 $this->first_name = $name['first_name'];
00635 $this->last_name = $name['last_name'];
00636 }
00637
00638
00639 parent::editPagePostSaveSync($form);
00640
00641
00642 if ($this->id == $me->id)
00643 $me->createSession();
00644 }
00645
00649 function canEdit()
00650 {
00651 global $me;
00652
00653 if ($me->isAdmin() || $this->isMe())
00654 return true;
00655 return false;
00656 }
00657
00661 function initLostPassPage()
00662 {
00663 global $me;
00664
00665 $this->pageTitle = 'Lost Password Retrieval';
00666
00667 if ($me->id)
00668 throw new PageError("You're already logged in, if you want to change your password, you can do so via your profile.");
00669 }
00670
00676 function createLostPassForm()
00677 {
00678 $form = new Form();
00679 $form->action = $this->getUrl('.lostpass');
00680
00681 $form->add('EmailField', 'email', array(
00682 'title' => 'Email Address',
00683 'required' => true,
00684 'size' => 50,
00685 'maxlength' => 255
00686 ));
00687 $form->addSubmit("Request Password Reset");
00688
00689 return $form;
00690 }
00691
00697 function drawLostpassPage()
00698 {
00699 if (!$this->id)
00700 {
00701 $form = $this->createLostPassForm();
00702
00703 if ($form->isSubmitted())
00704 {
00705 $form->validate();
00706
00707 if (!$form->hasError())
00708 {
00709 $email = $form->getData('email');
00710 if ($this->requestPassReset($email))
00711 echo "<p>We have emailed your password request to $email.</p>";
00712 else
00713 $form->setError('email', 'We did not find that email, you may ' .
00714 $this->getLink(".register?email=$email", 'register') . ' it if you like.');
00715 }
00716 }
00717 else
00718 $form->setData($this->params());
00719
00720
00721 if ($form->needsDrawn())
00722 $form->drawAll();
00723 }
00724 else
00725 echo 'If you are already logged in, you do not need to request a new password.';
00726 }
00727
00733 function createEditPassForm()
00734 {
00735 $form = new Form();
00736 $form->action = $this->getUrl('.editpass');
00737
00738 $form->add('PasswordField', 'password1', array(
00739 'required' => true,
00740 'title' => 'Password',
00741 ));
00742 $form->add('PasswordField', 'password2', array(
00743 'required' => true,
00744 'title' => 'Password Again',
00745 ));
00746 $form->addSubmit("Edit Password");
00747
00748 return $form;
00749 }
00750
00754 function initEditPassPage()
00755 {
00756 $this->pageTitle = 'Edit Password';
00757
00758 $this->id = $this->assertLogin();
00759 }
00760
00764 function drawEditPassPage()
00765 {
00766 $form = $this->createEditPassForm();
00767
00768 if ($form->isSubmitted())
00769 {
00770 $form->validate();
00771
00772 if (!$form->hasError())
00773 {
00774 $this->password = sha1($form->getData('password1'));
00775
00776
00777 if ($this->hasField('pass_changed'))
00778 $this->pass_changed = 1;
00779
00780 $this->save();
00781
00782
00783 $this->doLogin(true, false);
00784
00785
00786 $this->emailPassChanged();
00787
00788 echo 'You password has been changed.';
00789 }
00790 }
00791
00792
00793 if ($form->needsDrawn())
00794 $form->drawAll();
00795 }
00796
00800 function emailPassChanged()
00801 {
00802 $subject = 'Password Change Notice';
00803
00804 $body = 'Dear ' . $this->getName() . "\n\n";
00805 $body .= "This is just a notification that you (or someone using your account) has changed your password on " . Config::get('site_name') . ". ";
00806 $body .= "No action is required on your part, unless you did not do this, in which case contact " . Config::get('site_admin') . ".\n\n";
00807 $body .= "Thank you,\nHappy " . Config::get('site_name') . " Password Protection Robot";
00808
00809
00810 $this->mail($subject, $body);
00811 }
00812
00827 public function authenticate($username = null, $password = null, $rememberMe = null)
00828 {
00829
00830 if ($username !== null && $password !== null)
00831 {
00832
00833 if ($this->loginProcess($username, $password, $rememberMe))
00834 return true;
00835 }
00836
00837
00838 if (isset($_COOKIE['auth']))
00839 {
00840
00841 if ($this->tryAuthCode($_COOKIE['auth']))
00842 return true;
00843 }
00844
00845
00846 return false;
00847 }
00848
00858 public function tryAuthCode($code, $checkCache = true)
00859 {
00860 global $me;
00861
00862
00863 $authAr = explode("-", $code);
00864
00865
00866 $userId = $authAr[0];
00867
00868
00869 if ($me->id != $userId)
00870 {
00871
00872 $user = new User($userId);
00873 $goodHash = $user->generateHash();
00874
00875
00876 if ($code == $goodHash)
00877 {
00878
00879 if ($checkCache)
00880 if ($this->checkAuthCache($code))
00881 return true;
00882
00883
00884 $user->doLogin(true, false);
00885 return true;
00886 }
00887 }
00888
00889 else
00890 return true;
00891
00892 return false;
00893 }
00894
00905 protected function checkAuthCache($code)
00906 {
00907 global $me;
00908
00909
00910 if ($code)
00911 {
00912 $user = CacheBot::get("BaseJumper:auth:$code", self::$authCacheLife);
00913
00914
00915 if ($user instanceOf User)
00916 {
00917
00918 $me = $user;
00919 return true;
00920 }
00921 return false;
00922 }
00923 else
00924 throw new Exception("Authcode was blank.");
00925 }
00926
00937 public function generateHash()
00938 {
00939 $hash = '';
00940
00941
00942 if ($this->id)
00943 $hash = $this->id . "-" . sha1($this->id . "-" . $this->password . "-" . Config::get('auth_secret'));
00944
00945 return $hash;
00946 }
00947
00957 function loginProcess($user, $pass, $rememberMe = true)
00958 {
00959
00960 $where = $this->getAuthWhereSql($user, $pass);
00961 $userRs = dbQuery("
00962 SELECT id
00963 FROM $this->tableName
00964 $where
00965 ");
00966
00967
00968 if ($userAr = dbFetchAssoc($userRs))
00969 {
00970 $user = new User($userAr['id']);
00971 $user->doLogin($rememberMe);
00972
00973 return true;
00974 }
00975 else
00976 return false;
00977 }
00978
00984 public function doLogin($rememberMe = true, $login = true)
00985 {
00986
00987 $this->createSession($login);
00988
00989
00990 if ($rememberMe)
00991 $time = 31556926;
00992 else
00993 $time = 86400;
00994
00995
00996 $hash = $this->generateHash();
00997
00998
00999 setcookie('auth', $hash, time() + $time, '/', Config::get('site_hostname'));
01000 $_COOKIE['auth'] = $hash;
01001 }
01002
01013 protected function getAuthWhereSql($user, $pass)
01014 {
01015 $pass = sha1($pass);
01016
01017 $sql = "WHERE email = '$user' AND password = '$pass' ";
01018
01019 if (Config::get('users_must_activate'))
01020 $sql .= " AND activation_key = '' ";
01021
01022 return $sql;
01023 }
01024
01037 function createSession($login = false)
01038 {
01039 if ($this->id)
01040 {
01041
01042 $this->lookupData(false);
01043
01044
01045 if ($login)
01046 {
01047
01048 if ($this->hasField('login_count'))
01049 $this->login_count = $this->login_count + 1;
01050
01051
01052 if ($this->hasField('previous_login'))
01053 $this->previous_login = $this->last_login;
01054
01055
01056 if ($this->hasField('last_login'))
01057 $this->last_login = date('Y-m-d H:i:s');
01058 }
01059
01060
01061 $this->updateSession();
01062
01063
01064 $this->save();
01065
01066
01067 $this->saveSession();
01068
01069 return $this;
01070 }
01071 else
01072 throw new Exception('You cannot create a session for a non user.');
01073 }
01074
01079 protected function updateSession()
01080 {
01081 }
01082
01088 protected function saveSession()
01089 {
01090
01091 if ($this->id)
01092 CacheBot::set("BaseJumper:auth:" . $this->generateHash(), $this, self::$authCacheLife);
01093 else
01094 throw new Exception('You cannot save a session for a non user.');
01095 }
01096
01100 public function logoutProcess()
01101 {
01102
01103 CacheBot::delete("BaseJumper:auth:" . $this->generateHash());
01104
01105
01106 setcookie('auth', '', time()-42000, '/', Config::get('site_hostname'));
01107 }
01108
01116 function confirmUser()
01117 {
01118 global $me;
01119
01120
01121 $code = $_REQUEST['code'];
01122 $userId = $_REQUEST['id'];
01123
01124
01125 $userRs = dbQuery("
01126 SELECT *
01127 FROM users
01128 WHERE id = '$userId'
01129 AND code = '$code'
01130 AND approved = 0
01131 ");
01132
01133 if (dbGetNumRows($userRs))
01134 {
01135
01136 $me = new User(dbFetchAssoc($userRs));
01137
01138
01139 $me->code = md5(mt_rand());
01140 $me->approved = 1;
01141 $me->save();
01142
01143
01144 $_SESSION['user'] = $me;
01145
01146 return true;
01147 }
01148
01149 else
01150 return false;
01151 }
01152
01160 function mail($subject, $body, $html = null)
01161 {
01162 Mail::send($this->email, $subject, $body, $html);
01163 }
01164
01171 public function addAlert($url, $text)
01172 {
01173
01174 if (!class_exists(Alert))
01175 throw new Exception('You need to add the Alert class or define User::addAlert and make it blank.');
01176
01177
01178 $alert = new Alert();
01179 $alert->user_id = $this->id;
01180 $alert->url = $url;
01181 $alert->text = $text;
01182 $alert->save();
01183 }
01184
01192 function requestPassReset($email)
01193 {
01194 $this->id = $this->accountExists($email);
01195 if ($this->id)
01196 {
01197
01198 $this->reset_pass_code = substr(md5(mt_rand()), 1, 6);
01199 $this->save();
01200
01201
01202 $subject = "Lost Password Request";
01203 $body .= "You have recently asked to have your " . Config::get('site_name') . " password reset.\n\n";
01204 $body .= "You may now do this at:\n";
01205 $body .= $this->getTicket(array(
01206 'page' => 'editpass',
01207 )) . "\n\n";
01208 $body .= "Thank you,\nHappy " . Config::get('site_name') . " Password Reset Robot";
01209 $body .= "\n\nPS. For your convenience, this link will automatically log you in too!";
01210
01211
01212 $html .= "You have recently asked to have your " . Config::get('site_name') . " password reset.<br/><br/>";
01213 $html .= "You may now do this " . $this->getTicket(array(
01214 'page' => 'editpass',
01215 ), 'here') . ".<br/><br/>";
01216 $html .= "Thank you,<br/>Happy " . Config::get('site_name') . " Password Reset Robot";
01217 $html .= "<br/><br/>PS. For your convenience, this link will automatically log you in too!";
01218
01219
01220 $this->mail($subject, $body, $html);
01221
01222 return true;
01223 }
01224 else
01225 return false;
01226 }
01227
01233 function tryPassReset()
01234 {
01235 if ($this->id)
01236 {
01237 $code = $_REQUEST['code'];
01238 $pass = $_REQUEST['pass'];
01239
01240 if (dbGetNumRows(dbQuery("
01241 SELECT *
01242 FROM $this->tableName
01243 WHERE code = '$code'
01244 AND id = '$this-id'
01245 ")))
01246 {
01247 $this->password = md5($pass);
01248 $this->code = md5(mt_rand());
01249
01250
01251 }
01252
01253 }
01254
01255 }
01256
01262 function isMe()
01263 {
01264 global $me;
01265 if ($this->id == $me->id)
01266 return true;
01267 else
01268 return false;
01269 }
01270
01289 function getPagesXml()
01290 {
01291 $xml = parent::getPagesXml();
01292 $xml .= <<<XML
01293 <page name="register" desc="register a new user">
01294 <param name="email" desc="your email"/>
01295 <param name="password" desc="plaintext password"/>
01296 <param name="first_name" desc="your name" />
01297 <param name="last_name" desc="your name" />
01298 </page>
01299 <page name="activate" desc="activate your user account">
01300 <param name="id" value="id of the user" required="true"/>
01301 <param name="code" value="code to activate" required="true"/>
01302 </page>
01303 <page name="activatehelp">
01304 <param name="email" value="email of account"/>
01305 </page>
01306 <page name="lostpass" desc="request a pass change via email">
01307 <param name="email" desc="email of forgotten account"/>
01308 </page>
01309 <page name="editpass" desc="edit your password">
01310 <param name="password1" />
01311 <param name="password2" />
01312 </page>
01313 <page name="login" desc="the login process page" type="xml">
01314 <param name="redirect" value="url to redirect to" />
01315 <param name="status" value="status message." />
01316 </page>
01317 <page name="logout" desc="the logout process page"/>
01318 <page name="prefs" desc="manage your site preferences"/>
01319 <page name="emailprefs" desc="edit your email preferences" />
01320 <page name="deleteme">
01321 <param name="id" value="your id" required="1" type="int"/>
01322 </page>
01323 XML;
01324 return $xml;
01325 }
01326
01330 function initPrefsPage()
01331 {
01332 $this->assertLogin();
01333
01334 $this->pageTitle = 'Account Preferences';
01335 }
01336
01341 function drawPrefsPage()
01342 {
01343 global $me;
01344
01345 echo "\t<h3>" . $me->getLink(array('page' => 'edit'), 'Edit My Profile') . "</h3>\n";
01346 echo "<p>This is where you can change various things like your
01347 name, birthday, zipcode, biography, and so forth. Most of the
01348 information is shared on your profile. Don't put anything in here
01349 you would like to keep private.</p>";
01350
01351 echo "\t<h3>" . $me->getLink(array('page' => 'editpass'), 'Change My Password') . "</h3>\n";
01352 echo "<p>Here you can change your password.</p>";
01353
01354 echo "\t<h3>" . $me->getLink(array('page' => 'emailprefs'), 'Email Preferences') . "</h3>\n";
01355 echo "<p>On this page, you can modify your email preferences. By
01356 default we notify you via email whenever something cool
01357 happens.</p>";
01358 }
01359
01363 function initEmailPrefsPage()
01364 {
01365 $this->pageTitle = 'Email me whenever:';
01366 $this->id = $this->assertLogin();
01367 }
01368
01372 function drawEmailPrefsPage()
01373 {
01374 $form = $this->createEmailPrefsForm();
01375
01376 if ($form->isSubmitted())
01377 {
01378 $form->validate();
01379
01380 if (!$form->hasError())
01381 {
01382 $this->saveEmailPrefs($form);
01383 echo 'You email preferences have been saved.';
01384 }
01385 }
01386
01387 $form->setData($this->data);
01388
01389
01390 if ($form->needsDrawn())
01391 $form->drawAll();
01392 }
01393
01399 function createEmailPrefsForm()
01400 {
01401
01402 $form = new Form();
01403 $form->action = $this->getUrl('.emailprefs');
01404
01405
01406 $form = $this->emailPrefsFormAddFields($form);
01407
01408
01409 $form->addSubmit('Save Preferences');
01410
01411
01412 $form->setData($this->getData(false));
01413
01414 return $form;
01415 }
01416
01424 function emailPrefsFormAddFields($form)
01425 {
01426 if ($this->hasField('email_comments'))
01427 $form->add('CheckboxField', 'email_comments', array(
01428 'title' => '',
01429 'label' => 'Someone posts a comment on something I\'ve posted.',
01430 'value' => 1
01431 ));
01432
01433 if ($this->hasField('email_comment_replies'))
01434 $form->add('CheckboxField', 'email_comment_replies', array(
01435 'title' => '',
01436 'label' => 'Someone replies to one of my comments.',
01437 'value' => 1
01438 ));
01439
01440 if ($this->hasField('birthday'))
01441 $form->add('CheckboxField', 'email_happy_birthday', array(
01442 'title' => '',
01443 'label' => 'It\'s my birthday.',
01444 'value' => 1
01445 ));
01446
01447 return $form;
01448 }
01449
01455 function saveEmailPrefs($form)
01456 {
01457 $this->email_happy_birthday = (int)$form->getData('email_happy_birthday');
01458 $this->email_comment_replies = (int)$form->getData('email_comment_replies');
01459 $this->email_comments = (int)$form->getData('email_comments');
01460
01461 $this->save();
01462 }
01463
01469 function getLastLogin()
01470 {
01471 global $me;
01472
01473 return $me->formatDateTime($this->last_login);
01474 }
01475
01481 function getMemberSince()
01482 {
01483 global $me;
01484
01485 return $me->formatDateTime($this->member_since);
01486 }
01487
01491 function drawActivatePage()
01492 {
01493 global $me;
01494
01495
01496 $me = new User($this->params('id'));
01497 $me->doLogin();
01498
01499
01500 $me->activation_key = '';
01501 $me->save();
01502
01503
01504 Util::redirect($this->getUrl('.main'));
01505 }
01506
01510 function initActivateHelpPage()
01511 {
01512 $this->pageTitle = 'Account Activation Help';
01513 }
01514
01518 function drawActivateHelpPage()
01519 {
01520 $form = $this->createActivateHelpForm();
01521
01522 if ($form->isSubmitted())
01523 {
01524
01525 $form->validate();
01526
01527
01528 $user = new User();
01529 $email = $form->getData('email');
01530 $userId = $user->accountExists($email);
01531
01532
01533 if ($userId)
01534 {
01535 $user->id = $userId;
01536
01537
01538 if (!$user->activation_key)
01539 $form->setError('email', 'That account has already been activated, please log in normally.');
01540 }
01541
01542 else if ($email)
01543 $form->setError('email', 'That username does not exist, you may ' .
01544 $user->getLink(".register?email=$email", 'register') . " it.");
01545
01546
01547 if (!$form->hasError())
01548 {
01549 $user->sendActivationEmail();
01550 echo "<p>This new activation code overrides the old one, so
01551 make sure you have the right email. (It's the newest
01552 one.)</p>";
01553 }
01554 }
01555 else
01556 $form->setData($this->params());
01557
01558 if ($form->needsDrawn())
01559 {
01560 ?>
01561 <p>When you register a new account, before you can log in you must first
01562 activate your account. This can be done by following the link contained in
01563 an email titled 'New User Confirmation' that is sent when you register.</p>
01564
01565 <p>If for some reason, you lost the activation email, you can have the
01566 email re-sent to you via the form below. Simply enter your email and click
01567 the button.</p>
01568 <?
01569 $form->drawAll();
01570 }
01571 }
01572
01578 function createActivateHelpForm()
01579 {
01580 $form = new Form();
01581 $form->action = $this->getUrl('.activatehelp');
01582
01583 $form->add('EmailField', 'email', array(
01584 'required' => true,
01585 'size' => 45
01586 ));
01587 $form->addSubmit('Resend Activation Email');
01588
01589 return $form;
01590 }
01591
01595 function sendActivationEmail()
01596 {
01597
01598 $this->activation_key = substr(sha1(time()), 0, 8);
01599 $this->save();
01600
01601
01602 $subject = "New Account Confirmation";
01603 $body .= "You recently registered an account at " . Config::get('site_name') . ". Before you can start using this account, you must activate your account by visiting the following url:\n\n";
01604 $body .= $this->getUrl(array('page' => 'activate', 'id' => $this->id, 'code' => $this->activation_key), null, true) . "\n\n";
01605 $body .= "The entire url must be entered. If your mail client breaks the url into two lines, be sure to copy and paste both of them into your web browser.\n\n";
01606 $body .= "Once you have activated your account, you are free to start using the site.\n\n";
01607 $body .= "Thank you,\nThe " . Config::get('site_name') . " Happy Account Activation Robot\n\n";
01608 $body .= "PS. We really hope you like our website and if there is anything we can do, please contact us.";
01609
01610
01611 $html .= "You recently registered an account at " . Config::get('site_name') . ". Before you can start using this account, you must activate your account by ";
01612 $html .= $this->getLink(".activate?id=$this->id&code=$this->activation_key", 'clicking here', null, true) . "<br/><br/>";
01613 $html .= "Once you have activated your account, you are free to start using the site.<br/><br/>";
01614 $html .= "Thank you,<br/>The " . Config::get('site_name') . " Happy Account Activation Robot<br/><br/>";
01615 $html .= "PS. We really hope you like our website and if there is anything we can do, please contact us.";
01616
01617
01618 $this->mail($subject, $body, $html);
01619
01620
01621 echo "<p>Thank you, " . $this->getName() . ", we have emailed <b>$this->email</b> a confirmation email.</p>\n";
01622 echo "<p>Follow the instructions it contains to activate your account.</p>";
01623
01624 echo "<p>If it doesn't show up within a few minutes, email " . Linkify::email(Config::get('site_admin')) . " and he'll help you.</p>";
01625
01626
01627 if (preg_match('/(@hotmail|@msn|@aol)/', $this->email))
01628 echo "<p><b>Hotmail, AOL, or MSN may be blocking our emails. Check
01629 your Junk E-Mail folder.<br/><br/>If the email is in there, mark it
01630 as 'Not Junk Mail'.</b>";
01631 }
01632
01637 function drawDeletePage()
01638 {
01639 global $me;
01640
01641 $form = $this->createDeleteForm();
01642
01643 if ($form->isSubmitted())
01644 {
01645 $form->validate();
01646
01647 if (!$form->getData('acknowledge'))
01648 $form->setError('acknowledge', 'Since this is dangerous, you have to acknowledge that you really want to delete your account.');
01649
01650 if (!$form->hasError())
01651 $this->deletePost();
01652 }
01653
01654 if ($form->needsDrawn())
01655 $form->drawAll();
01656 }
01657
01666 protected function deletePost()
01667 {
01668 if ($this->isMe())
01669 {
01670 $this->emailDeleteConfirmation();
01671 echo "<p>In order to protect the safety of your account, we have
01672 emailed a delete confirmation email to you.</p><p>Follow the link inside
01673 and your account will be permanently deleted.</p>";
01674 }
01675 else
01676 {
01677 $this->delete();
01678 echo '<p>' . $this->getName() . ' has been deleted.</p>';
01679 }
01680 }
01681
01687 public function createDeleteForm()
01688 {
01689 $form = new Form();
01690 $form->action = $this->getUrl(".delete?id=$this->id");
01691
01692 $form->add('StaticField', 'warning', array(
01693 'text' => '<b>Warning:</b> Deleting your account is permanent. We
01694 cannot recover your account after we delete it. That means you
01695 should carefully consider whether you really want to delete
01696 your account or not.'
01697 ));
01698 $form->add('CheckboxField', 'acknowledge', array(
01699 'title' => '',
01700 'label' => 'I really do want to delete this account.'
01701 ));
01702 $form->addSubmit('Delete Account');
01703
01704 return $form;
01705 }
01706
01713 public function initDeleteMePage()
01714 {
01715 $this->assertLogin();
01716
01717 $this->initDeletePage();
01718
01719 $this->pageTitle = 'Account Deleted';
01720
01721 if (!$this->isMe())
01722 throw new PageError('You cannot delete other people with this page.');
01723 }
01724
01732 public function drawDeleteMePage()
01733 {
01734 global $me;
01735
01736 $me->delete();
01737 $me->logoutProcess();
01738
01739 echo '<p>Your account has been deleted.We\'re sorry to see you
01740 go.</p><p>If there\'s anything we can do to get you to come back,
01741 please email ' . Linkify::email(Config::get('site_admin')) . '</p>';
01742 }
01743
01747 protected function emailDeleteConfirmation()
01748 {
01749 $subject = 'Delete Account Confirmation';
01750
01751 $body = "Dear $this->first_name,\n\n";
01752 $body .= "Recently, someone using this account (probably you) requested that it be deleted.\n\n";
01753 $body .= "In order to protect your account and confirm that you really wanted to do this you must follow the link below.\n\n";
01754 $body .= "*** If this is not what you want, delete this email and nothing will happen. ***\n\n";
01755 $body .= "Follow this link to delete your account:\n";
01756 $body .= $this->getUrl(".deleteme?id=$this->id", null, true) . "\n\n";
01757 $body .= "Thank you,\nThe Sad " . Config::get('site_name') . " Account Deletion Robot\n\n";
01758 $body .= "PS. Please dont go! If you have problems with our site, email " . Config::get('site_admin') . " and we will try to fix them.";
01759
01760 $html = "Dear $this->first_name,<br/><br/>";
01761 $html .= "Recently, someone using this account (probably you) requested that it be deleted.<br/><br/>";
01762 $html .= "In order to protect your account and confirm that you really wanted to do this you must follow the link below.<br/><br/>";
01763 $html .= "*** <b>If this is not what you want, delete this email and nothing will happen.</b> ***<br/><br/>";
01764 $html .= $this->getLink(".deleteme?id=$this->id", "Follow this link to delete your account.", null, true) . "<br/><br/>";
01765 $html .= "Thank you,<br/>The Sad " . Config::get('site_name') . " Account Deletion Robot<br/><br/>";
01766 $html .= "PS. Please dont go! If you have problems with our site, email " . Config::get('site_admin') . " and we will try to fix them.";
01767
01768 $this->mail($subject, $body, $html);
01769 }
01770
01776 function canDelete()
01777 {
01778 global $me;
01779
01780 if (($me->id == $this->id || $me->isAdmin()) && $this->id)
01781 return true;
01782 return false;
01783 }
01784
01793 function getTicket($url, $html = null)
01794 {
01795 $redirUrl = $this->getUrl($url);
01796
01797 if ($this->id)
01798 {
01799
01800 $ticket = new Ticket();
01801 $ticket->generate($redirUrl, $this);
01802
01803
01804 $redirUrl = $ticket->url();
01805 }
01806
01807
01808 if ($html !== null)
01809 return "<a href=\"$redirUrl\">$html</a>";
01810 else
01811 return $redirUrl;
01812 }
01813
01821 function formatDate($date)
01822 {
01823 if (!$this->date_format)
01824 $date_format = 'F d, Y';
01825 else
01826 $date_format = $this->date_format;
01827
01828 if (!$date)
01829 $date = 0;
01830 $time = strtotime($date);
01831
01832 return date($date_format, $time);
01833 }
01834
01842 function formatDateTime($date)
01843 {
01844 if (!$this->datetime_format)
01845 $datetime_format = 'F d, Y g:ia';
01846 else
01847 $datetime_format = $this->datetime_format;
01848
01849 if (!$date)
01850 $date = 0;
01851
01852 $time = strtotime($date);
01853
01854 if ($time)
01855 return date($datetime_format, $time);
01856 else
01857 return '???';
01858 }
01859
01864 function getPublicData()
01865 {
01866 global $me;
01867
01868 $data = parent::getPublicData();
01869
01870 $data['last_login'] = $me->formatDateTime($this->last_login);
01871 $data['login_count'] = $this->login_count;
01872
01873 return $data;
01874 }
01875
01884 function getName($link = false, $icon = null)
01885 {
01886 global $me;
01887
01888 $rval = "";
01889
01890 if ($this->hasField('image_id') && $icon != null)
01891 $rval = $this->getIcon($icon) . " ";
01892
01893
01894 $rval .= $this->getTextName();
01895
01896 return parent::getName($link, $rval);
01897 }
01898
01902 function getTextName()
01903 {
01904 return $this->first_name . ' ' . $this->last_name;
01905 }
01906
01914 function getIcon($type = 'tiny')
01915 {
01916 return $this->profileImage->getImage($type, false, false);
01917 }
01918
01924 public function lookupData($deep = true)
01925 {
01926 parent::lookupData($deep);
01927
01928 $this->profileImage = new Image();
01929 $this->profileImage->load($this->image_id, false);
01930 }
01931
01942 public function getDataToCache($deep = true)
01943 {
01944 $data = parent::getDataToCache($deep);
01945
01946 $data['profileimage'] = $this->profileImage->getDataToCache(false);
01947
01948 return $data;
01949 }
01950
01961 public function setDataFromCache($data, $deep = true)
01962 {
01963 parent::setDataFromCache($data, $deep);
01964
01965
01966 $this->profileImage = new Image();
01967 $this->profileImage->load($data['profileimage'], false);
01968 }
01969
01975 function setProfileImage($imageId)
01976 {
01977 global $me;
01978
01979
01980 $this->profileImage = new Image($imageId);
01981 if ($this->profileImage->canView())
01982 {
01983 $this->image_id = $imageId;
01984 $this->save();
01985
01986 return true;
01987 }
01988 else
01989 return false;
01990 }
01991
01995 function initSetImagePage()
01996 {
01997 $this->assertLogin();
01998
01999 $this->pageTitle = 'Set Profile Image';
02000 }
02001
02005 function drawSetImagePage()
02006 {
02007 global $me;
02008
02009 if ($me->setProfileImage($this->params('image_id')))
02010 {
02011 $me->createSession();
02012 echo "<p>Your profile image has been updated. You may " . $this->getLink(".view?id=$me->id", "continue to your profile") . ".</p>";
02013 }
02014 else
02015 echo "<p>You must be able to view an image to set it as your profile image.</p>";
02016 }
02017
02023 public function getCreateFieldsArray()
02024 {
02025 $fields = parent::getCreateFieldsArray();
02026
02027 $fields['email'] = "email varchar(255) default '' not null";
02028 $fields['first_name'] = "first_name varchar(255) default '' not null";
02029 $fields['last_name'] = "last_name varchar(255) default '' not null";
02030 $fields['password'] = "password char(40) default '' not null";
02031 $fields['birthday'] = "birthday date not null";
02032 $fields['last_login'] = "last_login datetime not null";
02033 $fields['previous_login'] = "previous_login datetime not null";
02034 $fields['login_count'] = "login_count int(11) default 0 not null";
02035 $fields['activation_key'] = "activation_key char(8) default '' not null";
02036 $fields['is_admin'] = "is_admin tinyint(1) default 0 not null";
02037
02038 return $fields;
02039 }
02040
02046 public function getCreateIndexesArray()
02047 {
02048 $fields = parent::getCreateIndexesArray();
02049
02050 $fields['email'] = 'key (email)';
02051 $fields['password'] = 'key (password)';
02052
02053 return $fields;
02054 }
02055 }
02056 ?>