Guest User

Untitled

a guest
Mar 24th, 2016
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.74 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. // New fields
  190. $user->title = $data['title'];
  191. $user->position = $data['position'];
  192. $user->school_id = $data['school_id'];
  193.  
  194.  
  195.  
  196.  
  197. if ($user->isValid()) {
  198.  
  199. // Generate a random token for activation and base64 encode it so it's URL safe
  200. $token = base64_encode(uniqid(rand(), true));
  201. $hashed_token = sha1($token);
  202.  
  203. try {
  204.  
  205. $db = Database::getInstance();
  206.  
  207. //$stmt = $db->prepare('INSERT INTO users (name, surname, email, password, activation_token) VALUES (:name, :surname, :email, :password, :token)');
  208. $stmt = $db->prepare('INSERT INTO users (name, surname, email, password, activation_token, school_id, title, position) VALUES (:name, :surname, :email, :password, :token, :school_id, :title, :position)');
  209. $stmt->bindParam(':name', $user->name);
  210. $stmt->bindParam(':surname', $user->surname);
  211. $stmt->bindParam(':email', $user->email);
  212. $stmt->bindParam(':password', Hash::make($user->password));
  213. $stmt->bindParam(':token', $hashed_token);
  214. // new fields
  215. $stmt->bindParam(':school_id', $user->school_id);
  216.  
  217. $stmt->bindParam(':title', $user->title);
  218. $stmt->bindParam(':position', $user->position);
  219.  
  220.  
  221.  
  222.  
  223. $stmt->execute();
  224.  
  225. // Send activation email
  226. $user->_sendActivationEmail($token);
  227.  
  228. } catch(PDOException $exception) {
  229.  
  230. // Log the exception message
  231. error_log($exception->getMessage());
  232. }
  233. }
  234.  
  235. return $user;
  236. }
  237.  
  238.  
  239. /**
  240. * Find the user by remember token
  241. *
  242. * @param string $token token
  243. * @return mixed User object if found, null otherwise
  244. */
  245. public static function findByRememberToken($token)
  246. {
  247. try {
  248.  
  249. $db = Database::getInstance();
  250.  
  251. $stmt = $db->prepare('SELECT u.* FROM users u JOIN remembered_logins r ON u.id = r.user_id WHERE token = :token');
  252. $stmt->execute([':token' => $token]);
  253. $user = $stmt->fetchObject('User');
  254.  
  255. if ($user !== false) {
  256. return $user;
  257. }
  258.  
  259. } catch(PDOException $exception) {
  260.  
  261. error_log($exception->getMessage());
  262. }
  263. }
  264.  
  265.  
  266. /**
  267. * Deleted expired remember me tokens
  268. *
  269. * @return integer Number of tokens deleted
  270. */
  271. public static function deleteExpiredTokens()
  272. {
  273. try {
  274.  
  275. $db = Database::getInstance();
  276.  
  277. $stmt = $db->prepare("DELETE FROM remembered_logins WHERE expires_at < '" . date('Y-m-d H:i:s') . "'");
  278. $stmt->execute();
  279.  
  280. return $stmt->rowCount();
  281.  
  282. } catch(PDOException $exception) {
  283.  
  284. // Log the detailed exception
  285. error_log($exception->getMessage());
  286. }
  287.  
  288. return 0;
  289. }
  290.  
  291.  
  292. /**
  293. * Find the user for password reset, by the specified token and check the token hasn't expired
  294. *
  295. * @param string $token Reset token
  296. * @return mixed User object if found and the token hasn't expired, null otherwise
  297. */
  298. public static function findForPasswordReset($token)
  299. {
  300. $hashed_token = sha1($token);
  301.  
  302. try {
  303.  
  304. $db = Database::getInstance();
  305.  
  306. $stmt = $db->prepare('SELECT * FROM users WHERE password_reset_token = :token LIMIT 1');
  307. $stmt->execute([':token' => $hashed_token]);
  308. $user = $stmt->fetchObject('User');
  309.  
  310. if ($user !== false) {
  311.  
  312. // Check the token hasn't expired
  313. $expiry = DateTime::createFromFormat('Y-m-d H:i:s', $user->password_reset_expires_at);
  314.  
  315. if ($expiry !== false) {
  316. if ($expiry->getTimestamp() > time()) {
  317. return $user;
  318. }
  319. }
  320. }
  321.  
  322. } catch(PDOException $exception) {
  323.  
  324. error_log($exception->getMessage());
  325. }
  326. }
  327.  
  328.  
  329. /**
  330. * Activate the user account, nullifying the activation token and setting the is_active flag
  331. *
  332. * @param string $token Activation token
  333. * @return void
  334. */
  335. public static function activateAccount($token)
  336. {
  337. $hashed_token = sha1($token);
  338.  
  339. try {
  340.  
  341. $db = Database::getInstance();
  342.  
  343. $stmt = $db->prepare('UPDATE users SET activation_token = NULL, is_active = TRUE WHERE activation_token = :token');
  344. $stmt->execute([':token' => $hashed_token]);
  345.  
  346. } catch(PDOException $exception) {
  347.  
  348. // Log the detailed exception
  349. error_log($exception->getMessage());
  350. }
  351. }
  352.  
  353.  
  354. /**
  355. * Delete the user.
  356. *
  357. * @return void
  358. */
  359. public function delete()
  360. {
  361. try {
  362.  
  363. $db = Database::getInstance();
  364.  
  365. $stmt = $db->prepare('DELETE FROM users WHERE id = :id');
  366. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  367. $stmt->execute();
  368.  
  369. } catch(PDOException $exception) {
  370.  
  371. // Log the detailed exception
  372. error_log($exception->getMessage());
  373. }
  374. }
  375.  
  376. /**
  377. * select a school from the list.
  378. *
  379. * @return void
  380. */
  381. public function selectschools()
  382. {
  383. try {
  384.  
  385. $db = Database::getInstance();
  386.  
  387. $sql= "SELECT id, name FROM schools";
  388.  
  389. $stmt = $pdo->query($sql);
  390.  
  391. $schools = $stmt->fetchAll(PDO::FETCH_ASSOC);
  392.  
  393. } catch(PDOException $exception) {
  394.  
  395. // Log the detailed exception
  396. error_log($exception->getMessage());
  397. }
  398. }
  399.  
  400.  
  401. /**
  402. * Update or insert the user's details based on the data. Data is validated and $this->errors is set if
  403. * if any values are invalid.
  404. *
  405. * @param array $data Data ($_POST array)
  406. * @return boolean True if the values were updated / inserted successfully, false otherwise.
  407. */
  408. public function save($data)
  409. {
  410. $this->name = $data['name'];
  411. $this->surname = $data['surname'];
  412. $this->email = $data['email'];
  413.  
  414. // If editing a user, only validate and update the password if a value provided
  415. if (isset($this->id) && empty($data['password'])) {
  416. unset($this->password);
  417. } else {
  418. $this->password = $data['password'];
  419. }
  420.  
  421. // Convert values of the checkboxes to boolean
  422. $this->is_active = isset($data['is_active']) && ($data['is_active'] == '1');
  423. $this->is_admin = isset($data['is_admin']) && ($data['is_admin'] == '1');
  424.  
  425. if ($this->isValid()) {
  426.  
  427. try {
  428.  
  429. $db = Database::getInstance();
  430.  
  431. // Prepare the SQL: Update the existing record if editing, or insert new if adding
  432. if (isset($this->id)) {
  433.  
  434. $sql = 'UPDATE users SET name = :name, surname = :surname, email = :email, is_active = :is_active, is_admin = :is_admin';
  435.  
  436. if (isset($this->password)) { // only update password if set
  437. $sql .= ', password = :password';
  438. }
  439.  
  440. $sql .= ' WHERE id = :id';
  441.  
  442. } else {
  443.  
  444. $sql = 'INSERT INTO users (name, surname, email, password, is_active, is_admin) VALUES (:name, :surname, :email, :password, :is_active, :is_admin)';
  445. }
  446.  
  447. // Bind the parameters
  448. $stmt = $db->prepare($sql);
  449. $stmt->bindParam(':name', $this->name);
  450. $stmt->bindParam(':surname', $this->surname);
  451. $stmt->bindParam(':email', $this->email);
  452. $stmt->bindParam(':is_active', $this->is_active);
  453. $stmt->bindParam(':is_admin', $this->is_admin);
  454.  
  455. if (isset($this->id)) {
  456. if (isset($this->password)) { // only update password if set
  457. $stmt->bindParam(':password', Hash::make($this->password));
  458. }
  459.  
  460. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  461.  
  462. } else {
  463. $stmt->bindParam(':password', Hash::make($this->password));
  464. }
  465.  
  466. $stmt->execute();
  467.  
  468. // Set the ID if a new record
  469. if ( ! isset($this->id)) {
  470. $this->id = $db->lastInsertId();
  471. }
  472.  
  473. return true;
  474.  
  475. } catch(PDOException $exception) {
  476.  
  477. // Set generic error message and log the detailed exception
  478. $this->errors = ['error' => 'A database error occurred.'];
  479. error_log($exception->getMessage());
  480. }
  481. }
  482.  
  483. return false;
  484. }
  485.  
  486.  
  487. /**
  488. * Remember the login by storing a unique token associated with the user ID
  489. *
  490. * @param integer $expiry Expiry timestamp
  491. * @return mixed The token if remembered successfully, false otherwise
  492. */
  493. public function rememberLogin($expiry)
  494. {
  495.  
  496. // Generate a unique token
  497. $token = uniqid($this->email, true);
  498.  
  499. try {
  500.  
  501. $db = Database::getInstance();
  502.  
  503. $stmt = $db->prepare('INSERT INTO remembered_logins (token, user_id, expires_at) VALUES (:token, :user_id, :expires_at)');
  504. $stmt->bindParam(':token', sha1($token)); // store a hash of the token
  505. $stmt->bindParam(':user_id', $this->id, PDO::PARAM_INT);
  506. $stmt->bindParam(':expires_at', date('Y-m-d H:i:s', $expiry));
  507. $stmt->execute();
  508.  
  509. if ($stmt->rowCount() == 1) {
  510. return $token;
  511. }
  512.  
  513. } catch(PDOException $exception) {
  514.  
  515. // Log the detailed exception
  516. error_log($exception->getMessage());
  517. }
  518.  
  519. return false;
  520. }
  521.  
  522.  
  523. /**
  524. * Forget the login based on the token value
  525. *
  526. * @param string $token Remember token
  527. * @return void
  528. */
  529. public function forgetLogin($token)
  530. {
  531. if ($token !== null) {
  532.  
  533. try {
  534.  
  535. $db = Database::getInstance();
  536.  
  537. $stmt = $db->prepare('DELETE FROM remembered_logins WHERE token = :token');
  538. $stmt->bindParam(':token', $token);
  539. $stmt->execute();
  540.  
  541. } catch(PDOException $exception) {
  542.  
  543. // Log the detailed exception
  544. error_log($exception->getMessage());
  545. }
  546. }
  547. }
  548.  
  549.  
  550. /**
  551. * Start the password reset process by generating a unique token and expiry and saving them in the user model
  552. *
  553. * @return boolean True if the user model was updated successfully, false otherwise
  554. */
  555. public function startPasswordReset()
  556. {
  557. // Generate a random token and base64 encode it so it's URL safe
  558. $token = base64_encode(uniqid(rand(), true));
  559. $hashed_token = sha1($token);
  560.  
  561. // Set the token to expire in one hour
  562. $expires_at = date('Y-m-d H:i:s', time() + 60 * 60);
  563.  
  564. try {
  565.  
  566. $db = Database::getInstance();
  567.  
  568. $stmt = $db->prepare('UPDATE users SET password_reset_token = :token, password_reset_expires_at = :expires_at WHERE id = :id');
  569. $stmt->bindParam(':token', $hashed_token);
  570. $stmt->bindParam(':expires_at', $expires_at);
  571. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  572. $stmt->execute();
  573.  
  574. if ($stmt->rowCount() == 1) {
  575. $this->password_reset_token = $token;
  576. $this->password_reset_expires_at = $expires_at;
  577.  
  578. return true;
  579. }
  580.  
  581. } catch(PDOException $exception) {
  582.  
  583. // Log the detailed exception
  584. error_log($exception->getMessage());
  585. }
  586.  
  587. return false;
  588. }
  589.  
  590.  
  591. /**
  592. * Reset the password
  593. *
  594. * @return boolean true if the password was changed successfully, false otherwise
  595. */
  596. public function resetPassword()
  597. {
  598. $password_error = $this->_validatePassword();
  599.  
  600. if ($password_error === null) {
  601.  
  602. try {
  603.  
  604. $db = Database::getInstance();
  605.  
  606. $stmt = $db->prepare('UPDATE users SET password = :password, password_reset_token = NULL, password_reset_expires_at = NULL WHERE id = :id');
  607. $stmt->bindParam(':password', Hash::make($this->password));
  608. $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
  609. $stmt->execute();
  610.  
  611. if ($stmt->rowCount() == 1) {
  612. return true;
  613. }
  614.  
  615. } catch(PDOException $exception) {
  616.  
  617. // Set generic error message and log the detailed exception
  618. $this->errors = ['error' => 'A database error occurred.'];
  619. error_log($exception->getMessage());
  620. }
  621.  
  622. } else {
  623. $this->errors['password'] = $password_error;
  624. }
  625.  
  626. return false;
  627. }
  628.  
  629.  
  630. /**
  631. * Validate the properties and set $this->errors if any are invalid
  632. *
  633. * @return boolean true if valid, false otherwise
  634. */
  635. public function isValid()
  636. {
  637. $this->errors = [];
  638.  
  639. //
  640. // name
  641. //
  642. if ($this->name == '') {
  643. $this->errors['name'] = 'Please enter a valid name';
  644. }
  645.  
  646. //
  647. // email address
  648. //
  649. if (filter_var($this->email, FILTER_VALIDATE_EMAIL) === false) {
  650. $this->errors['email'] = 'Please enter a valid email address';
  651. }
  652.  
  653. if ($this->_emailTaken($this->email)) {
  654. $this->errors['email'] = 'That email address is already taken';
  655. }
  656.  
  657. //
  658. // password
  659. //
  660. $password_error = $this->_validatePassword();
  661. if ($password_error !== null) {
  662. $this->errors['password'] = $password_error;
  663. }
  664.  
  665. return empty($this->errors);
  666. }
  667.  
  668.  
  669. /**
  670. * Get the total number of users
  671. *
  672. * @return integer
  673. */
  674. private static function _getTotalUsers()
  675. {
  676. try {
  677.  
  678. $db = Database::getInstance();
  679. $count = (int) $db->query('SELECT COUNT(*) FROM users')->fetchColumn();
  680.  
  681. } catch(PDOException $exception) {
  682.  
  683. error_log($exception->getMessage());
  684. $count = 0;
  685. }
  686.  
  687. return $count;
  688. }
  689.  
  690.  
  691. /**
  692. * See if the email address is taken (already exists), ignoring the current user if already saved.
  693. *
  694. * @param string $email Email address
  695. * @return boolean True if the email is taken, false otherwise
  696. */
  697. private function _emailTaken($email)
  698. {
  699. $isTaken = false;
  700. $user = $this->findByEmail($email);
  701.  
  702. if ($user !== null) {
  703.  
  704. if (isset($this->id)) { // existing user
  705.  
  706. if ($this->id != $user->id) { // different user
  707. $isTaken = true;
  708. }
  709.  
  710. } else { // new user
  711. $isTaken = true;
  712. }
  713. }
  714.  
  715. return $isTaken;
  716. }
  717.  
  718.  
  719. /**
  720. * Validate the password
  721. *
  722. * @return mixed The first error message if invalid, null otherwise
  723. */
  724. private function _validatePassword()
  725. {
  726. if (isset($this->password) && (strlen($this->password) < 5)) {
  727. return 'Please enter a longer password';
  728. }
  729.  
  730. if (isset($this->password_confirmation) && ($this->password != $this->password_confirmation)) {
  731. return 'Please enter the same password';
  732. }
  733. }
  734.  
  735.  
  736. /**
  737. * Send activation email to the user based on the token
  738. *
  739. * @param string $token Activation token
  740. * @return mixed User object if authenticated correctly, null otherwise
  741. */
  742. private function _sendActivationEmail($token)
  743. {
  744. // Note hardcoded protocol
  745. $url = 'http://'.$_SERVER['HTTP_HOST'].'/activate_account.php?token=' . $token;
  746.  
  747. $body = <<<EOT
  748.  
  749. <p>Please click on the following link to activate your account.</p>
  750.  
  751. <p><a href="$url">$url</a></p>
  752.  
  753. EOT;
  754.  
  755. Mail::send($this->name, $this->email, 'Activate account', $body);
  756. }
  757.  
  758. }
Add Comment
Please, Sign In to add comment