Guest User

Untitled

a guest
Mar 20th, 2016
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.09 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4. * User class
  5. */
  6.  
  7. class User
  8. {
  9.  
  10. public $errors;
  11.  
  12.  
  13. /**
  14. * Magic getter - read data from a property that isn't set yet
  15. *
  16. * @param string $name Property name
  17. * @return mixed
  18. */
  19. public function __get($name)
  20. {
  21. }
  22.  
  23.  
  24. /**
  25. * Get a page of user records and the previous and next page (if there are any)
  26. *
  27. * @param string $page Page number
  28. * @return array Previous page, next page and user data. Page elements are null if there isn't a previous or next page.
  29. */
  30. public static function paginate($page)
  31. {
  32. $data = [];
  33. $users_per_page = 5;
  34.  
  35. // Calculate the total number of pages
  36. $total_users = static::_getTotalUsers();
  37. $total_pages = (int) ceil($total_users / $users_per_page);
  38.  
  39.  
  40. // Make sure the current page is valid
  41. $page = (int) $page;
  42.  
  43. if ($page < 1) {
  44. $page = 1;
  45. } elseif ($page > $total_pages) {
  46. $page = $total_pages;
  47. }
  48.  
  49.  
  50. // Calculate the next and previous pages
  51. $data['previous'] = $page == 1 ? null : $page - 1;
  52. $data['next'] = $page == $total_pages ? null : $page + 1;
  53.  
  54.  
  55. // Get the page of users
  56. try {
  57.  
  58. $db = Database::getInstance();
  59.  
  60. $offset = ($page - 1) * $users_per_page;
  61.  
  62. $data['users'] = $db->query("SELECT * FROM users ORDER BY email LIMIT $users_per_page OFFSET $offset")->fetchAll();
  63.  
  64. } catch(PDOException $exception) {
  65.  
  66. error_log($exception->getMessage());
  67.  
  68. $data['users'] = [];
  69. }
  70.  
  71. return $data;
  72. }
  73.  
  74.  
  75. /**
  76. * Authenticate a user by email and password
  77. *
  78. * @param string $email Email address
  79. * @param string $password Password
  80. * @return mixed User object if authenticated correctly, null otherwise
  81. */
  82. public static function authenticate($email, $password)
  83. {
  84. $user = static::findByEmail($email);
  85.  
  86. if ($user !== null) {
  87.  
  88. // Check the user has been activated
  89. if ($user->is_active) {
  90.  
  91. // Check the hashed password stored in the user record matches the supplied password
  92. if (Hash::check($password, $user->password)) {
  93. return $user;
  94. }
  95. }
  96. }
  97. }
  98.  
  99.  
  100. /**
  101. * Find the user with the specified ID
  102. *
  103. * @param string $id ID
  104. * @return mixed User object if found, null otherwise
  105. */
  106. public static function findByID($id)
  107. {
  108. try {
  109.  
  110. $db = Database::getInstance();
  111.  
  112. $stmt = $db->prepare('SELECT * FROM users WHERE id = :id LIMIT 1');
  113. $stmt->execute([':id' => $id]);
  114. $user = $stmt->fetchObject('User');
  115.  
  116. if ($user !== false) {
  117. return $user;
  118. }
  119.  
  120. } catch(PDOException $exception) {
  121.  
  122. error_log($exception->getMessage());
  123. }
  124. }
  125.  
  126.  
  127. /**
  128. * Get the user by ID or display a 404 Not Found page if not found.
  129. *
  130. * @param array $data $_GET data
  131. * @return mixed User object if found, null otherwise
  132. */
  133. public static function getByIDor404($data)
  134. {
  135. if (isset($data['id'])) {
  136. $user = static::findByID($data['id']);
  137.  
  138. if ($user !== null) {
  139. return $user;
  140. }
  141. }
  142.  
  143. Util::showNotFound();
  144. }
  145.  
  146.  
  147. /**
  148. * Find the user with the specified email address
  149. *
  150. * @param string $email email address
  151. * @return mixed User object if found, null otherwise
  152. */
  153. public static function findByEmail($email)
  154. {
  155. try {
  156.  
  157. $db = Database::getInstance();
  158.  
  159. $stmt = $db->prepare('SELECT * FROM users WHERE email = :email LIMIT 1');
  160. $stmt->execute([':email' => $email]);
  161. $user = $stmt->fetchObject('User');
  162.  
  163. if ($user !== false) {
  164. return $user;
  165. }
  166.  
  167. } catch(PDOException $exception) {
  168.  
  169. error_log($exception->getMessage());
  170. }
  171. }
  172.  
  173.  
  174. /**
  175. * Signup a new user
  176. *
  177. * @param array $data POST data
  178. * @return User
  179. */
  180. public static function signup($data)
  181. {
  182. // Create a new user model and set the attributes
  183. $user = new static();
  184.  
  185. $user->name = $data['name'];
  186. $user->surname = $data['surname'];
  187. $user->email = $data['email'];
  188. $user->password = $data['password'];
  189.  
  190. if ($user->isValid()) {
  191.  
  192. // Generate a random token for activation and base64 encode it so it's URL safe
  193. $token = base64_encode(uniqid(rand(), true));
  194. $hashed_token = sha1($token);
  195.  
  196. try {
  197.  
  198. $db = Database::getInstance();
  199.  
  200. $stmt = $db->prepare('INSERT INTO users (name, surname, email, password, activation_token) VALUES (:name, :surname, :email, :password, :token)');
  201. $stmt->bindParam(':name', $user->name);
  202. $stmt->bindParam(':surname', $user->surname);
  203. $stmt->bindParam(':email', $user->email);
  204. $stmt->bindParam(':password', Hash::make($user->password));
  205. $stmt->bindParam(':token', $hashed_token);
  206.  
  207.  
  208. $stmt->execute();
  209.  
  210. // Send activation email
  211. $user->_sendActivationEmail($token);
  212.  
  213. } catch(PDOException $exception) {
  214.  
  215. // Log the exception message
  216. error_log($exception->getMessage());
  217. }
  218. }
  219.  
  220. return $user;
  221. }
  222.  
  223.  
  224. /**
  225. * Find the user by remember token
  226. *
  227. * @param string $token token
  228. * @return mixed User object if found, null otherwise
  229. */
  230. public static function findByRememberToken($token)
  231. {
  232. try {
  233.  
  234. $db = Database::getInstance();
  235.  
  236. $stmt = $db->prepare('SELECT u.* FROM users u JOIN remembered_logins r ON u.id = r.user_id WHERE token = :token');
  237. $stmt->execute([':token' => $token]);
  238. $user = $stmt->fetchObject('User');
  239.  
  240. if ($user !== false) {
  241. return $user;
  242. }
  243.  
  244. } catch(PDOException $exception) {
  245.  
  246. error_log($exception->getMessage());
  247. }
  248. }
  249.  
  250.  
  251. /**
  252. * Deleted expired remember me tokens
  253. *
  254. * @return integer Number of tokens deleted
  255. */
  256. public static function deleteExpiredTokens()
  257. {
  258. try {
  259.  
  260. $db = Database::getInstance();
  261.  
  262. $stmt = $db->prepare("DELETE FROM remembered_logins WHERE expires_at < '" . date('Y-m-d H:i:s') . "'");
  263. $stmt->execute();
  264.  
  265. return $stmt->rowCount();
  266.  
  267. } catch(PDOException $exception) {
  268.  
  269. // Log the detailed exception
  270. error_log($exception->getMessage());
  271. }
  272.  
  273. return 0;
  274. }
  275.  
  276.  
  277. /**
  278. * Find the user for password reset, by the specified token and check the token hasn't expired
  279. *
  280. * @param string $token Reset token
  281. * @return mixed User object if found and the token hasn't expired, null otherwise
  282. */
  283. public static function findForPasswordReset($token)
  284. {
  285. $hashed_token = sha1($token);
  286.  
  287. try {
  288.  
  289. $db = Database::getInstance();
  290.  
  291. $stmt = $db->prepare('SELECT * FROM users WHERE password_reset_token = :token LIMIT 1');
  292. $stmt->execute([':token' => $hashed_token]);
  293. $user = $stmt->fetchObject('User');
  294.  
  295. if ($user !== false) {
  296.  
  297. // Check the token hasn't expired
  298. $expiry = DateTime::createFromFormat('Y-m-d H:i:s', $user->password_reset_expires_at);
  299.  
  300. if ($expiry !== false) {
  301. if ($expiry->getTimestamp() > time()) {
  302. return $user;
  303. }
  304. }
  305. }
  306.  
  307. } catch(PDOException $exception) {
  308.  
  309. error_log($exception->getMessage());
  310. }
  311. }
  312.  
  313.  
  314. /**
  315. * Activate the user account, nullifying the activation token and setting the is_active flag
  316. *
  317. * @param string $token Activation token
  318. * @return void
  319. */
  320. public static function activateAccount($token)
  321. {
  322. $hashed_token = sha1($token);
  323.  
  324. try {
  325.  
  326. $db = Database::getInstance();
  327.  
  328. $stmt = $db->prepare('UPDATE users SET activation_token = NULL, is_active = TRUE WHERE activation_token = :token');
  329. $stmt->execute([':token' => $hashed_token]);
  330.  
  331. } catch(PDOException $exception) {
  332.  
  333. // Log the detailed exception
  334. error_log($exception->getMessage());
  335. }
  336. }
  337.  
  338.  
  339. /**
  340. * Delete the user.
  341. *
  342. * @return void
  343. */
  344. public function delete()
  345. {
  346. try {
  347.  
  348. $db = Database::getInstance();
  349.  
  350. $stmt = $db->prepare('DELETE FROM users WHERE id = :id');
  351. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  352. $stmt->execute();
  353.  
  354. } catch(PDOException $exception) {
  355.  
  356. // Log the detailed exception
  357. error_log($exception->getMessage());
  358. }
  359. }
  360.  
  361.  
  362.  
  363. /**
  364. * Select schools fro drop down in sign up page
  365. */
  366. public function selectschool()
  367. {
  368. try {
  369.  
  370. $db = Database::getInstance();
  371.  
  372. $sql= "SELECT id, name FROM schools";
  373. $stmt = $pdo->query($sql);
  374. $schools = $stmt->fetchAll(PDO::FETCH_ASSOC);
  375. $stmt->execute();
  376.  
  377. }
  378. }
  379.  
  380.  
  381. /**
  382. * Update or insert the user's details based on the data. Data is validated and $this->errors is set if
  383. * if any values are invalid.
  384. *
  385. * @param array $data Data ($_POST array)
  386. * @return boolean True if the values were updated / inserted successfully, false otherwise.
  387. */
  388. public function save($data)
  389. {
  390. $this->name = $data['name'];
  391. $this->surname = $data['surname'];
  392. $this->email = $data['email'];
  393.  
  394. // If editing a user, only validate and update the password if a value provided
  395. if (isset($this->id) && empty($data['password'])) {
  396. unset($this->password);
  397. } else {
  398. $this->password = $data['password'];
  399. }
  400.  
  401. // Convert values of the checkboxes to boolean
  402. $this->is_active = isset($data['is_active']) && ($data['is_active'] == '1');
  403. $this->is_admin = isset($data['is_admin']) && ($data['is_admin'] == '1');
  404.  
  405. if ($this->isValid()) {
  406.  
  407. try {
  408.  
  409. $db = Database::getInstance();
  410.  
  411. // Prepare the SQL: Update the existing record if editing, or insert new if adding
  412. if (isset($this->id)) {
  413.  
  414. $sql = 'UPDATE users SET name = :name, surname = :surname, email = :email, is_active = :is_active, is_admin = :is_admin';
  415.  
  416. if (isset($this->password)) { // only update password if set
  417. $sql .= ', password = :password';
  418. }
  419.  
  420. $sql .= ' WHERE id = :id';
  421.  
  422. } else {
  423.  
  424. $sql = 'INSERT INTO users (name, surname, email, password, is_active, is_admin) VALUES (:name, :surname, :email, :password, :is_active, :is_admin)';
  425. }
  426.  
  427. // Bind the parameters
  428. $stmt = $db->prepare($sql);
  429. $stmt->bindParam(':name', $this->name);
  430. $stmt->bindParam(':surname', $this->surname);
  431. $stmt->bindParam(':email', $this->email);
  432. $stmt->bindParam(':is_active', $this->is_active);
  433. $stmt->bindParam(':is_admin', $this->is_admin);
  434.  
  435. if (isset($this->id)) {
  436. if (isset($this->password)) { // only update password if set
  437. $stmt->bindParam(':password', Hash::make($this->password));
  438. }
  439.  
  440. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  441.  
  442. } else {
  443. $stmt->bindParam(':password', Hash::make($this->password));
  444. }
  445.  
  446. $stmt->execute();
  447.  
  448. // Set the ID if a new record
  449. if ( ! isset($this->id)) {
  450. $this->id = $db->lastInsertId();
  451. }
  452.  
  453. return true;
  454.  
  455. } catch(PDOException $exception) {
  456.  
  457. // Set generic error message and log the detailed exception
  458. $this->errors = ['error' => 'A database error occurred.'];
  459. error_log($exception->getMessage());
  460. }
  461. }
  462.  
  463. return false;
  464. }
  465.  
  466.  
  467. /**
  468. * Remember the login by storing a unique token associated with the user ID
  469. *
  470. * @param integer $expiry Expiry timestamp
  471. * @return mixed The token if remembered successfully, false otherwise
  472. */
  473. public function rememberLogin($expiry)
  474. {
  475.  
  476. // Generate a unique token
  477. $token = uniqid($this->email, true);
  478.  
  479. try {
  480.  
  481. $db = Database::getInstance();
  482.  
  483. $stmt = $db->prepare('INSERT INTO remembered_logins (token, user_id, expires_at) VALUES (:token, :user_id, :expires_at)');
  484. $stmt->bindParam(':token', sha1($token)); // store a hash of the token
  485. $stmt->bindParam(':user_id', $this->id, PDO::PARAM_INT);
  486. $stmt->bindParam(':expires_at', date('Y-m-d H:i:s', $expiry));
  487. $stmt->execute();
  488.  
  489. if ($stmt->rowCount() == 1) {
  490. return $token;
  491. }
  492.  
  493. } catch(PDOException $exception) {
  494.  
  495. // Log the detailed exception
  496. error_log($exception->getMessage());
  497. }
  498.  
  499. return false;
  500. }
  501.  
  502.  
  503. /**
  504. * Forget the login based on the token value
  505. *
  506. * @param string $token Remember token
  507. * @return void
  508. */
  509. public function forgetLogin($token)
  510. {
  511. if ($token !== null) {
  512.  
  513. try {
  514.  
  515. $db = Database::getInstance();
  516.  
  517. $stmt = $db->prepare('DELETE FROM remembered_logins WHERE token = :token');
  518. $stmt->bindParam(':token', $token);
  519. $stmt->execute();
  520.  
  521. } catch(PDOException $exception) {
  522.  
  523. // Log the detailed exception
  524. error_log($exception->getMessage());
  525. }
  526. }
  527. }
  528.  
  529.  
  530. /**
  531. * Start the password reset process by generating a unique token and expiry and saving them in the user model
  532. *
  533. * @return boolean True if the user model was updated successfully, false otherwise
  534. */
  535. public function startPasswordReset()
  536. {
  537. // Generate a random token and base64 encode it so it's URL safe
  538. $token = base64_encode(uniqid(rand(), true));
  539. $hashed_token = sha1($token);
  540.  
  541. // Set the token to expire in one hour
  542. $expires_at = date('Y-m-d H:i:s', time() + 60 * 60);
  543.  
  544. try {
  545.  
  546. $db = Database::getInstance();
  547.  
  548. $stmt = $db->prepare('UPDATE users SET password_reset_token = :token, password_reset_expires_at = :expires_at WHERE id = :id');
  549. $stmt->bindParam(':token', $hashed_token);
  550. $stmt->bindParam(':expires_at', $expires_at);
  551. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  552. $stmt->execute();
  553.  
  554. if ($stmt->rowCount() == 1) {
  555. $this->password_reset_token = $token;
  556. $this->password_reset_expires_at = $expires_at;
  557.  
  558. return true;
  559. }
  560.  
  561. } catch(PDOException $exception) {
  562.  
  563. // Log the detailed exception
  564. error_log($exception->getMessage());
  565. }
  566.  
  567. return false;
  568. }
  569.  
  570.  
  571. /**
  572. * Reset the password
  573. *
  574. * @return boolean true if the password was changed successfully, false otherwise
  575. */
  576. public function resetPassword()
  577. {
  578. $password_error = $this->_validatePassword();
  579.  
  580. if ($password_error === null) {
  581.  
  582. try {
  583.  
  584. $db = Database::getInstance();
  585.  
  586. $stmt = $db->prepare('UPDATE users SET password = :password, password_reset_token = NULL, password_reset_expires_at = NULL WHERE id = :id');
  587. $stmt->bindParam(':password', Hash::make($this->password));
  588. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  589. $stmt->execute();
  590.  
  591. if ($stmt->rowCount() == 1) {
  592. return true;
  593. }
  594.  
  595. } catch(PDOException $exception) {
  596.  
  597. // Set generic error message and log the detailed exception
  598. $this->errors = ['error' => 'A database error occurred.'];
  599. error_log($exception->getMessage());
  600. }
  601.  
  602. } else {
  603. $this->errors['password'] = $password_error;
  604. }
  605.  
  606. return false;
  607. }
  608.  
  609.  
  610. /**
  611. * Validate the properties and set $this->errors if any are invalid
  612. *
  613. * @return boolean true if valid, false otherwise
  614. */
  615. public function isValid()
  616. {
  617. $this->errors = [];
  618.  
  619. //
  620. // name
  621. //
  622. if ($this->name == '') {
  623. $this->errors['name'] = 'Please enter a valid name';
  624. }
  625.  
  626. //
  627. // email address
  628. //
  629. if (filter_var($this->email, FILTER_VALIDATE_EMAIL) === false) {
  630. $this->errors['email'] = 'Please enter a valid email address';
  631. }
  632.  
  633. if ($this->_emailTaken($this->email)) {
  634. $this->errors['email'] = 'That email address is already taken';
  635. }
  636.  
  637. //
  638. // password
  639. //
  640. $password_error = $this->_validatePassword();
  641. if ($password_error !== null) {
  642. $this->errors['password'] = $password_error;
  643. }
  644.  
  645. return empty($this->errors);
  646. }
  647.  
  648.  
  649. /**
  650. * Get the total number of users
  651. *
  652. * @return integer
  653. */
  654. private static function _getTotalUsers()
  655. {
  656. try {
  657.  
  658. $db = Database::getInstance();
  659. $count = (int) $db->query('SELECT COUNT(*) FROM users')->fetchColumn();
  660.  
  661. } catch(PDOException $exception) {
  662.  
  663. error_log($exception->getMessage());
  664. $count = 0;
  665. }
  666.  
  667. return $count;
  668. }
  669.  
  670.  
  671. /**
  672. * See if the email address is taken (already exists), ignoring the current user if already saved.
  673. *
  674. * @param string $email Email address
  675. * @return boolean True if the email is taken, false otherwise
  676. */
  677. private function _emailTaken($email)
  678. {
  679. $isTaken = false;
  680. $user = $this->findByEmail($email);
  681.  
  682. if ($user !== null) {
  683.  
  684. if (isset($this->id)) { // existing user
  685.  
  686. if ($this->id != $user->id) { // different user
  687. $isTaken = true;
  688. }
  689.  
  690. } else { // new user
  691. $isTaken = true;
  692. }
  693. }
  694.  
  695. return $isTaken;
  696. }
  697.  
  698.  
  699. /**
  700. * Validate the password
  701. *
  702. * @return mixed The first error message if invalid, null otherwise
  703. */
  704. private function _validatePassword()
  705. {
  706. if (isset($this->password) && (strlen($this->password) < 5)) {
  707. return 'Please enter a longer password';
  708. }
  709.  
  710. if (isset($this->password_confirmation) && ($this->password != $this->password_confirmation)) {
  711. return 'Please enter the same password';
  712. }
  713. }
  714.  
  715.  
  716. /**
  717. * Send activation email to the user based on the token
  718. *
  719. * @param string $token Activation token
  720. * @return mixed User object if authenticated correctly, null otherwise
  721. */
  722. private function _sendActivationEmail($token)
  723. {
  724. // Note hardcoded protocol
  725. $url = 'http://'.$_SERVER['HTTP_HOST'].'/activate_account.php?token=' . $token;
  726.  
  727. $body = <<<EOT
  728.  
  729. <p>Please click on the following link to activate your account.</p>
  730.  
  731. <p><a href="$url">$url</a></p>
  732.  
  733. EOT;
  734.  
  735. Mail::send($this->name, $this->email, 'Activate account', $body);
  736. }
  737.  
  738. }
Add Comment
Please, Sign In to add comment