1  <?php
  2  /* Copyright (c) 2013, Geert Bergman (geert@scrivo.nl)
  3   * All rights reserved.
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions are met:
  7   *
  8   * 1. Redistributions of source code must retain the above copyright notice,
  9   *    this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright notice,
 11   *    this list of conditions and the following disclaimer in the documentation
 12   *    and/or other materials provided with the distribution.
 13   * 3. Neither the name of "Scrivo" nor the names of its contributors may be
 14   *    used to endorse or promote products derived from this software without
 15   *    specific prior written permission.
 16   *
 17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 18   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 19   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 20   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 21   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 23   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 24   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 25   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 26   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 27   * POSSIBILITY OF SUCH DAMAGE.
 28   *
 29   * $Id: User.php 866 2013-08-25 16:22:35Z geert $
 30   */
 31  
 32  /**
 33   * Implementation of the \Scrivo\User class.
 34   */
 35  
 36  namespace Scrivo;
 37  
 38  /**
 39   * The Scrivo User class represents the user entity in Scrivo. Access rights
 40   * to objects like pages and assets are determined using these user principals.
 41   *
 42   * An user is identified by it's object id, but also by it's user code.
 43   * Convention (but not mandatory) is to use email addresses for user codes.
 44   * Both the user id and user code can be used to retrieve a user.
 45   *
 46   * The User class defines some descriptive members for the user's name and
 47   * and email address. For additional user data to store along with a user
 48   * you can use the multipurose 'customData' field.
 49   *
 50   * Peudo users
 51   *
 52   * The situation that you'll need to autenticate to different user database
 53   * arises frequently. Since the existence of Scrivo users is a requirement
 54   * for dertermining access to pages and assest it is suggested to use psuedo
 55   * users. After autenticating to an other database the user should adopt a
 56   * predefined (or progamatically defined) Scrivo user to access Scrivo
 57   * resources. This is already the case for annymous access to Scrivo resources,
 58   * in that case the user will adopt the Scrivo anymous user identity.
 59   *
 60   * TODO currently object ids 1 and 2 are used for reserved user ids, this is
 61   * not in line with the policy for object ids. In the code the are patched to
 62   * the values 3 and 4.
 63   *
 64   * @property-read object $customData A facility for 'free' storage.
 65   * @property-read int $id The user's user id (DB key)
 66   * @property-read array[] $roles An array of role-ids representing the roles
 67   *    for the user.
 68   * @property \Scrivo\String $emailAddress The user's email address.
 69   * @property \Scrivo\String $familyName The user's family name.
 70   * @property \Scrivo\String $familyNamePrefix A prefix for the user's family
 71   *    name.
 72   * @property \Scrivo\String $givenName The user's given name.
 73   * @property \Scrivo\String $password The user's password (encrypted).
 74   * @property int $status The user's status, one out of the constants
 75   *    self::STATUS_*
 76   * @property \Scrivo\String $userCode A more descriptive identification
 77   *       for the user than the user id.*
 78   */
 79  class User {
 80  
 81      /**
 82       * Status value indicating an admin user.
 83       */
 84      const STATUS_ADMIN 1;
 85  
 86      /**
 87       * Status value indicating an editor.
 88       */
 89      const STATUS_EDITOR 2;
 90  
 91      /**
 92       * Status value indicating a member.
 93       */
 94      const STATUS_MEMBER 3;
 95  
 96      /**
 97       * Reserved user id for primary admin user.
 98       */
 99      const PRIMARY_ADMIN_ID 4;
100  
101      /**
102       * Reserved user id for the anonymous user.
103       */
104      const ANONYMOUS_USER_ID 3;
105  
106      /**
107       * The user's user id (DB key)
108       * @var int
109       */
110      private $id 0;
111  
112      /**
113       * The user status, one out of the constants self::STATUS_*
114       * @var int
115       */
116      private $status self::STATUS_MEMBER;
117  
118      /**
119       * A more descriptive identification for the user than the user id.
120       * @var \Scrivo\String
121       */
122      private $userCode null;
123  
124      /**
125       * The user's password (encrypted).
126       * @var \Scrivo\String
127       */
128      private $password null;
129  
130      /**
131       * The user's given name.
132       * @var \Scrivo\String
133       */
134      private $givenName null;
135  
136      /**
137       * A prefix for the user's family name.
138       * @var \Scrivo\String
139       */
140      private $familyNamePrefix null;
141  
142      /**
143       * The user's family name.
144       * @var \Scrivo\String
145       */
146      private $familyName null;
147  
148      /**
149       * The user's email address.
150       * @var \Scrivo\String
151       */
152      private $emailAddress null;
153  
154      /**
155       * An array of role-ids representing the roles for the user.
156       * @var array[]
157       */
158      private $roles null;
159  
160      /**
161       * A facility for 'free' storage.
162       * @var object
163       */
164      private $customData null;
165  
166      /**
167       * A Scrivo context.
168       * @var Context
169       */
170      private $context null;
171  
172      /**
173       * Create an empty user object.
174       *
175       * @param \Scrivo\Context $context A Scrivo context.
176       */
177      public function __construct(\Scrivo\Context $context=null) {
178          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(null), 0);
179  
180          if ($context) {
181              $this->userCode = new \Scrivo\String();
182              $this->password = new \Scrivo\String();
183              $this->givenName = new \Scrivo\String();
184              $this->familyNamePrefix = new \Scrivo\String();
185              $this->familyName = new \Scrivo\String();
186              $this->emailAddress = new \Scrivo\String();
187              $this->customData = new \stdClass;
188  
189              $this->context $context;
190          }
191      }
192  
193      /**
194       * Implementation of the readable properties using the PHP magic
195       * method __get().
196       *
197       * @param string $name The name of the property to get.
198       *
199       * @return mixed The value of the requested property.
200       */
201      public function __get($name) {
202          switch($name) {
203              case "id": return $this->id;
204              case "status": return $this->status;
205              case "userCode": return $this->userCode;
206              case "password": return $this->password;
207              case "givenName": return $this->givenName;
208              case "familyNamePrefix": return $this->familyNamePrefix;
209              case "familyName": return $this->familyName;
210              case "emailAddress": return $this->emailAddress;
211              case "roles": return $this->getRoles();
212              case "customData": return $this->customData;
213          }
214          throw new \Scrivo\SystemException("No such get-property '$name'.");
215      }
216  
217      /**
218       * Implementation of the writable properties using the PHP magic
219       * method __set().
220       *
221       * @param string $name The name of the property to set.
222       * @param mixed $value The value of the property to set.
223       */
224      public function __set($name$value) {
225          switch($name) {
226              case "status"$this->setStatus($value); return;
227              case "userCode"$this->setUserCode($value); return;
228              case "password"$this->setPassword($value); return;
229              case "givenName"$this->setGivenName($value); return;
230              case "familyNamePrefix"$this->setFamilyNamePrefix($value); return;
231              case "familyName"$this->setFamilyName($value); return;
232              case "emailAddress"$this->setEmailAddress($value); return;
233              case "context"$this->setContext($value); return;
234          }
235          throw new \Scrivo\SystemException("No such set-property '$name'.");
236      }
237  
238      /**
239       * Convenience method to set the fields of a user object from
240       * an array (result set row).
241       *
242       * @param \Scrivo\Context $context A Scrivo context.
243       * @param array $rd An array containing the field data using the database
244       *    field names as keys.
245       */
246      private function setFields(\Scrivo\Context $context, array $rd) {
247  
248          $id intval($rd["user_id"]);
249          if ($id === 1) {
250              $id self::ANONYMOUS_USER_ID;
251          }
252          if ($id === 2) {
253              $id self::PRIMARY_ADMIN_ID;
254          }
255  
256          $this->id $id;
257          $this->status intval($rd["status"]);
258          $this->userCode = new \Scrivo\String($rd["user_code"]);
259          $this->password = new \Scrivo\String("- hidden -");
260          $this->givenName = new \Scrivo\String($rd["given_name"]);
261          $this->familyNamePrefix = new \Scrivo\String($rd["family_name_prefix"]);
262          $this->familyName = new \Scrivo\String($rd["family_name"]);
263          $this->emailAddress = new \Scrivo\String($rd["email_address"]);
264          $this->customData unserialize($rd["custom_data"]);
265          if (is_array($this->customData)) {
266              $this->customData = (object)$this->customData;
267          }
268  
269          $this->context $context;
270      }
271  
272      /**
273       * Utility to patch legacy ids 1 and 2.
274       * @param int $id The id to patch.
275       * @return int The patched id.
276       */
277      public static function patchId($id) {
278          if ($id === self::ANONYMOUS_USER_ID) {
279              return 1;
280          }
281          if ($id === self::PRIMARY_ADMIN_ID) {
282              return 2;
283          }
284          return $id;
285      }
286  
287      /**
288       * Get the user-roles for this user.
289       *
290       * @return UserRole[roleId] The set of user-roles for this user.
291       */
292      private function getRoles() {
293          if (!is_array($this->roles)) {
294              $this->roles UserRole::select($this->context$this);
295              $this->context->cache["U".$this->id] = $this;
296          }
297          return $this->roles;
298      }
299  
300      /**
301       * Set the user status, one out of the constants User::STATUS_*
302       *
303       * @param int The user status, one out of the constants User::STATUS_*
304       */
305      private function setStatus($status) {
306          if ($status != self::STATUS_ADMIN && $status != self::STATUS_EDITOR
307                  && $status != self::STATUS_MEMBER) {
308              throw new \Scrivo\SystemException("Not a valid status");
309          }
310          $this->status $status;
311      }
312  
313      /**
314       * Set the user code.
315       *
316       * @param \Scrivo\String A more descriptive identification for the
317       *   user than the user id.
318       */
319      private function setUserCode(\Scrivo\String $userCode) {
320          $this->userCode $userCode;
321      }
322  
323      /**
324       * Set the user's password (not encrypted).
325       *
326       * @param \Scrivo\String The user's password (not encrypted).
327       */
328      private function setPassword(\Scrivo\String $password) {
329          $this->password = new \Scrivo\String($this->encrypt($password));
330      }
331  
332      /**
333       * Set the user's given name.
334       *
335       * @param \Scrivo\String The user's given name.
336       */
337      private function setGivenName(\Scrivo\String $givenName) {
338          $this->givenName $givenName;
339      }
340  
341      /**
342       * Set a prefix for the user's family name.
343       *
344       * @param \Scrivo\String A prefix for the user's family name.
345       */
346      private function setFamilyNamePrefix(\Scrivo\String $familyNamePrefix) {
347          $this->familyNamePrefix $familyNamePrefix;
348      }
349  
350      /**
351       * Set the user's family name.
352       *
353       * @param \Scrivo\String The user's family name.
354       */
355      private function setFamilyName(\Scrivo\String $familyName) {
356          $this->familyName $familyName;
357      }
358  
359      /**
360       * Set the user's email address
361       *
362       * @param \Scrivo\String The user's email address
363       */
364      private function setEmailAddress(\Scrivo\String $emailAddress) {
365          $this->emailAddress $emailAddress;
366      }
367  
368      /**
369       * Set the user's context.
370       *
371       * @param \Scrivo\Context $context A Scrivo context.
372       */
373      private function setContext(\Scrivo\Context $context) {
374          $this->context $context;
375      }
376  
377      /**
378       * Check if the current user code is valid.
379       *
380       * The user code must be unique in the current database instance and at
381       * least three characters in length.
382       *
383       * @throws ApplicationException If the user code does not comply.
384       */
385      private function checkUserCode() {
386  
387          if ($this->userCode->length 3) {
388              throw new ApplicationException("User code too short",
389                  StatusCodes::USER_CODE_TOO_SHORT);
390          }
391  
392          $sth $this->context->connection->prepare(
393              "SELECT COUNT(*) FROM user
394              WHERE instance_id = :instId AND user_code = :userCode
395                AND user_id <> :id");
396  
397          $this->context->connection->bindInstance($sth);
398          $sth->bindValue(":id"self::patchId($this->id), \PDO::PARAM_INT);
399          $sth->bindValue(":userCode"$this->userCode, \PDO::PARAM_STR);
400          $sth->execute();
401  
402          $count $sth->fetchColumn();
403  
404          if ($count != 0) {
405              throw new \Scrivo\ApplicationException("User code already in use",
406                  StatusCodes::USER_CODE_IN_USE);
407          }
408  
409      }
410  
411      /**
412       * Encrypt a password.
413       *
414       * @param \Scrivo\String $password
415       *
416       * @return string
417       */
418      private function encrypt(\Scrivo\String $password) {
419          $salt base_convert(md5(mt_rand(01000000)), 1636);
420          $c =  crypt($password"\$2a\$07\$".$salt."\$");
421          return $c;
422      }
423  
424      /**
425       * Check if this user object can be inserted into the database.
426       *
427       * @throws \Scrivo\ApplicationException If the data is not accessible or
428       *   one or more of the fields contain invalid data.
429       */
430      private function validateInsert() {
431          $this->context->checkPermission(\Scrivo\AccessController::WRITE_ACCESS);
432          $this->checkUserCode();
433      }
434  
435      /**
436       * Insert new user object data into the database.
437       *
438       * First it is checked if the data of this user object can be inserted
439       * into the database, then the data is inserted into the database. If no id
440       * was set a new object id is generated.
441       *
442       * @throws \Scrivo\ApplicationException If the data is not accessible or
443       *   one or more of the fields contain invalid data.
444       */
445      public function insert() {
446          try {
447              $this->validateInsert();
448  
449              if (!$this->id) {
450                  $this->id $this->context->connection->generateId();
451              }
452  
453              $sth $this->context->connection->prepare(
454                  "INSERT INTO user (
455                      instance_id, user_id, status, user_code, password, 
456                      given_name, family_name_prefix, family_name, email_address,
457                      custom_data
458                  ) VALUES (
459                      :instId, :id, :status, :userCode, :password, 
460                      :givenName, :familyNamePrefix, :familyName, :emailAddress, 
461                      :customData
462                  )");
463  
464              $this->context->connection->bindInstance($sth);
465              $sth->bindValue(":id"$this->id, \PDO::PARAM_INT);
466              $sth->bindValue(":status"$this->status, \PDO::PARAM_INT);
467              $sth->bindValue(":userCode"$this->userCode, \PDO::PARAM_STR);
468              $sth->bindValue(":password"$this->password, \PDO::PARAM_STR);
469              $sth->bindValue(":givenName"$this->givenName, \PDO::PARAM_STR);
470              $sth->bindValue(
471                  ":familyNamePrefix"$this->familyNamePrefix, \PDO::PARAM_STR);
472              $sth->bindValue(":familyName"$this->familyName, \PDO::PARAM_STR);
473              $sth->bindValue(
474                  ":emailAddress"$this->emailAddress, \PDO::PARAM_STR);
475              $sth->bindValue(
476                  ":customData"serialize($this->customData), \PDO::PARAM_STR);
477  
478              $sth->execute();
479  
480          } catch(\PDOException $e) {
481              throw new \Scrivo\ResourceException($e);
482          }
483      }
484  
485      /**
486       * Check if this user object can be updated in the database.
487       *
488       * @throws \Scrivo\ApplicationException If the data is not accessible or
489       *   one or more of the fields contain invalid data.
490       */
491      private function validateUpdate() {
492          $this->context->checkPermission(\Scrivo\AccessController::WRITE_ACCESS);
493          $this->checkUserCode();
494      }
495  
496      /**
497       * Update existing user object data in the database.
498       *
499       * First it is checked if the data of this user object can be updated
500       * in the database, then the data is updated in the database.
501       *
502       * The user's password cannot be updated with this method. Use
503       * User::updatePassword() in order to do that.
504       *
505       * @throws \Scrivo\ApplicationException If the data is not accessible or
506       *   one or more of the fields contain invalid data.
507       */
508      public function update() {
509          try {
510              $this->validateUpdate();
511  
512              $sth $this->context->connection->prepare(
513                  "UPDATE user SET
514                      status = :status, user_code = :userCode,
515                      given_name = :givenName,
516                      family_name_prefix = :familyNamePrefix, 
517                      family_name = :familyName, email_address = :emailAddress, 
518                      custom_data = :customData
519                  WHERE instance_id = :instId AND user_id = :id");
520  
521              $this->context->connection->bindInstance($sth);
522              $sth->bindValue(":id"self::patchId($this->id), \PDO::PARAM_INT);
523  
524              $sth->bindValue(":status"$this->status, \PDO::PARAM_INT);
525              $sth->bindValue(":userCode"$this->userCode, \PDO::PARAM_STR);
526              $sth->bindValue(":givenName"$this->givenName, \PDO::PARAM_STR);
527              $sth->bindValue(
528                  ":familyNamePrefix"$this->familyNamePrefix, \PDO::PARAM_STR);
529              $sth->bindValue(":familyName"$this->familyName, \PDO::PARAM_STR);
530              $sth->bindValue(
531                  ":emailAddress"$this->emailAddress, \PDO::PARAM_STR);
532              $sth->bindValue(
533                  ":customData"serialize($this->customData), \PDO::PARAM_STR);
534  
535              $sth->execute();
536  
537              unset($this->context->cache["U".$this->id]);
538  
539          } catch(\PDOException $e) {
540              throw new \Scrivo\ResourceException($e);
541          }
542      }
543  
544      /**
545       * Update the password of this user.
546       *
547       * The password property should be set to its new value. When saving
548       * a encrypted value will be stored into the database. When this user
549       * object is loaded again its password property will contain the encrypted
550       * value for the new password.
551       *
552       * @throws ApplicationException If one or more of the fields contain
553       *   invalid data.
554       */
555      public function updatePassword() {
556          try {
557              $this->context->checkPermission(\Scrivo\AccessController::WRITE_ACCESS);
558  
559              $sth $this->context->connection->prepare(
560                  "UPDATE user SET password = :password
561                  WHERE instance_id = :instId AND user_id = :id");
562  
563              $this->context->connection->bindInstance($sth);
564              $sth->bindValue(":id"self::patchId($this->id), \PDO::PARAM_INT);
565  
566              $sth->bindValue(
567                  ":password"$this->password, \PDO::PARAM_STR);
568  
569              $sth->execute();
570  
571          } catch(\PDOException $e) {
572              throw new \Scrivo\ResourceException($e);
573          }
574      }
575  
576      /**
577       * Check if a given password matches with the one of this user.
578       *
579       * @param \Scrivo\String $toTest Password to test.
580       *
581       * @return boolean True if given password matches the user's, false if not.
582       */
583      public function checkPassword(\Scrivo\String $toTest) {
584          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(null));
585          try {
586  
587              $sth $this->context->connection->prepare(
588                  "SELECT password FROM user
589                  WHERE instance_id = :instId AND user_id = :id");
590  
591              $this->context->connection->bindInstance($sth);
592              $sth->bindValue(":id"self::patchId($this->id), \PDO::PARAM_INT);
593  
594              $sth->execute();
595  
596              if ($sth->rowCount() != 1) {
597                  throw new \Scrivo\SystemException("Failed to load User");
598              }
599  
600              $pw $sth->fetchColumn();
601  
602              return $pw == crypt($toTest$pw);
603  
604          } catch(\PDOException $e) {
605              throw new \Scrivo\ResourceException($e);
606          }
607      }
608  
609      /**
610       * Check if deletion of user object data does not violate any
611       * business rules.
612       *
613       * @param \Scrivo\Context $context A Scrivo context.
614       * @param int $id a valid object id.
615       *
616       * @throws \Scrivo\ApplicationException If the data is not accessible or
617       *   if it is not possible to delete the user data.
618       */
619      private function validateDelete(\Scrivo\Context $context$id) {
620          $context->checkPermission(\Scrivo\AccessController::WRITE_ACCESS);
621          if ($id == self::PRIMARY_ADMIN_ID || $id == self::ANONYMOUS_USER_ID) {
622              throw new \Scrivo\ApplicationException("Can't delete system users",
623                  StatusCodes::CANNOT_DELETE_SYSTEM_USERS);
624          }
625      }
626  
627      /**
628       * Delete existing user data from the database.
629       *
630       * First it is is checked if it's possible to delete user data,
631       * then the user data including its dependecies is deleted from
632       * the database.
633       *
634       * @param \Scrivo\Context $context A Scrivo context.
635       * @param int $id a valid object id.
636       *
637       * @throws \Scrivo\ApplicationException If the data is not accessible or
638       *   if it is not possible to delete the user data.
639       */
640      public static function delete(\Scrivo\Context $context$id) {
641          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
642              null,
643              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
644          ));
645          try {
646              self::validateDelete($context$id);
647  
648              foreach (array("user""user_role") as $table) {
649  
650                  $sth $context->connection->prepare(
651                      "DELETE FROM $table
652                      WHERE instance_id = :instId AND user_id = :id");
653  
654                  $context->connection->bindInstance($sth);
655                  $sth->bindValue(":id"$id, \PDO::PARAM_INT);
656  
657                  $sth->execute();
658              }
659  
660              unset($context->cache["U".$id]);
661  
662          } catch(\PDOException $e) {
663              throw new \Scrivo\ResourceException($e);
664          }
665      }
666  
667      /**
668       * Assign user roles to this user.
669       *
670       * The user roles to set is either an array of UserRole or stdObject
671       * objects. stdObject need to contain the members roleId and isPublisher.
672       *
673       * Note: this sets all the roles for the user at once. So not giving the
674       * the roles effectivily clears the roles for the given user.
675       *
676       * @param UserRole[]|object[] $roles A new set of user-roles for the given
677       *   user.
678       */
679      public function assignRoles($roles) {
680          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(null));
681  
682          UserRole::set($this->context$this$roles);
683      }
684  
685      /**
686       * Fetch a user object from the database using the object id or
687       * user code.
688       *
689       * @param \Scrivo\Context $context A Scrivo context.
690       * @param int|\Scrivo\String $id a valid object id or user code.
691       *
692       * @return \Scrivo\User The requested user object.
693       *
694       * @throws \Scrivo\ApplicationException If the given user code was
695       *    not found (when selecting by user code).
696       */
697      public static function fetch(\Scrivo\Context $context$id) {
698          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
699              null,
700              array(array(\Scrivo\ArgumentCheck::TYPE_INTEGER"Scrivo\String"))
701          ));
702          try {
703  
704              if ($id instanceof \Scrivo\String) {
705                  $byUserCode true;
706              } else if (is_int($id)) {
707                  $byUserCode false;
708              } else {
709                  throw new \Scrivo\SystemException("Invalid argument type");
710              }
711  
712              // Try to retieve the user from the cache ...
713              if (!$byUserCode && isset($context->cache["U".$id])) {
714                  // ... get it from the cache and set the context.
715                  $user $context->cache["U".$id];
716                  $user->context $context;
717              } else {
718                  // ... else retrieve it and set it in the cache.
719                  $sth $context->connection->prepare(
720                      "SELECT user_id, status, user_code,
721                          given_name, family_name_prefix, family_name, email_address, custom_data
722                      FROM user
723                      WHERE instance_id = :instId AND
724                          ".($byUserCode "user_code" "user_id")." = :id");
725  
726                  $user = new \Scrivo\User($context);
727  
728                  $context->connection->bindInstance($sth);
729                  if ($byUserCode) {
730                      $sth->bindValue(":id"$id, \PDO::PARAM_STR);
731                  } else {
732                      $sth->bindValue(
733                          ":id"$user->patchId($id), \PDO::PARAM_INT);
734                  }
735  
736                  $sth->execute();
737  
738                  if ($sth->rowCount() != 1) {
739                      if ($byUserCode) {
740                          throw new \Scrivo\ApplicationException(
741                              "Failed to load User $byUserCode");
742                      }
743                      throw new \Scrivo\SystemException("Failed to load User");
744                  }
745  
746                  $rd $sth->fetch(\PDO::FETCH_ASSOC);
747  
748                  $user->setFields($context$rd);
749  
750                  $context->cache["U".$user->id] = $user;
751              }
752  
753              return $user;
754  
755          } catch(\PDOException $e) {
756              throw new \Scrivo\ResourceException($e);
757          }
758      }
759  
760      /**
761       * Select users from the database.
762       *
763       * Depending on the given arguments all users or all users for a given
764       * role can be retrieved.
765       *
766       * @param \Scrivo\Context $context A Scrivo context.
767       * @param int $roleId Optional role id for which to retrieve the users.
768       *
769       * @return User[id] An array containing the selected users.
770       */
771      public static function select(\Scrivo\Context $context$roleId null) {
772          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
773              null,
774              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
775          ), 1);
776          try {
777              $sth $context->connection->prepare(
778                  "SELECT U.user_id, U.status, U.user_code, U.given_name, U.family_name_prefix,
779                      U.family_name, U.email_address, U.custom_data
780                  FROM user U" .($roleId", user_role""")."
781                  WHERE U.instance_id = :instId" .($roleId
782                      " AND user_role.instance_id = :instId AND
783                      user_role.user_id =    U.user_id AND role_id = :roleId"
784                      ""));
785  
786              $context->connection->bindInstance($sth);
787              if ($roleId) {
788                  $sth->bindValue(":roleId"$roleId, \PDO::PARAM_INT);
789              }
790  
791              $sth->execute();
792  
793              $res = array();
794  
795              while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
796  
797                  $li = new User();
798                  $li->setFields($context$rd);
799  
800                  $res[$li->id] = $li;
801              }
802  
803              return $res;
804  
805          } catch(\PDOException $e) {
806              throw new \Scrivo\ResourceException($e);
807          }
808      }
809  
810  }
811  
812  ?>

Documentation generated by phpDocumentor 2.0.0a12 and ScrivoDocumentor on August 29, 2013