From f4d471baacad50526b8ee722a810998379f8c414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20T=C3=A4nzer?= Date: Mon, 20 Jun 2011 13:41:52 +0200 Subject: [PATCH] Factor out functionality into a model so the controllers get more lightweight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Tänzer --- .../controllers/ManageAccountController.php | 125 +------ manager/application/models/User.php | 310 ++++++++++++++++++ 2 files changed, 317 insertions(+), 118 deletions(-) create mode 100644 manager/application/models/User.php diff --git a/manager/application/controllers/ManageAccountController.php b/manager/application/controllers/ManageAccountController.php index b07285b..f0701b4 100644 --- a/manager/application/controllers/ManageAccountController.php +++ b/manager/application/controllers/ManageAccountController.php @@ -6,7 +6,6 @@ class ManageAccountController extends Zend_Controller_Action { const MAX_POINTS_PER_ASSURANCE = 35; - const MAX_ASSURANCE_POINTS = 100; const MAX_POINTS_TOTAL = 150; const ADMIN_INCREASE_FRAGMENT_SIZE = 2; @@ -57,55 +56,28 @@ class ManageAccountController extends Zend_Controller_Action // Form is valid -> get values for processing $values = $form->getValues(); - // Get user data - $user['id'] = $this->getUserId(); - $user['points'] = $this->getPoints($user['id']); + // Get the current user + $user = Application_Model_User::findCurrentUser(); - - // Do the actual assurances - $assurance = array(); // Make sure the array is empty - $assurance['to'] = $user['id']; - $assurance['location'] = $values['location']; - $assurance['date'] = $values['date']; - $assurance['when'] = new Zend_Db_Expr('now()'); $this->view->assurancesDone = array(); - $quantity = $values['quantity']; do { // split up into multiple assurances if ($quantity > self::MAX_POINTS_PER_ASSURANCE) { - $assurance['awarded'] = self::MAX_POINTS_PER_ASSURANCE; + $points = self::MAX_POINTS_PER_ASSURANCE; $quantity -= self::MAX_POINTS_PER_ASSURANCE; } else { - $assurance['awarded'] = $quantity; + $points = $quantity; $quantity = 0; } // Get the assurer for this assurance - $assurance['from'] = $this->getNewAssurer($user['id']); + $issued = $user->findNewAssurer() + ->assure($user, $points, $values['location'], $values['date']); - // only assign points whithin the limit - if ($user['points'] + $assurance['awarded'] > self::MAX_ASSURANCE_POINTS){ - $assurance['points'] = self::MAX_ASSURANCE_POINTS - $user['points']; - } else { - $assurance['points'] = $assurance['awarded']; - } - - // Only assign positive amounts - if ($assurance['points'] < 0){ - $assurance['points'] = 0; - } - - $this->db->insert('notary', $assurance); - - $user['points'] += $assurance['points']; - $this->view->assurancesDone[] = $assurance['points']; + $this->view->assurancesDone[] = $issued; } while ($quantity > 0); - - // Maybe user is now assurer - $this->fixAssurerFlag($user['id']); - return; } @@ -229,89 +201,6 @@ class ManageAccountController extends Zend_Controller_Action return; } - /** - * Get and check the user ID of the current user - * - * @return int The ID of the current user - */ - protected function getUserId() - { - $session = Zend_Registry::get('session'); - if ($session->authdata['authed'] !== true) { - throw new Exception(__METHOD__ . ': you need to log in to use this feature'); - } - - // Check if the ID is present on the test server - $query = 'select `id` from `users` where `id` = :user'; - $query_params['user'] = $session->authdata['authed_id']; - $result = $this->db->query($query, $query_params); - if ($result->rowCount() !== 1) { - throw new Exception(__METHOD__ . ': user ID not found in the data base'); - } - $row = $result->fetch(); - - return $row['id']; - } - - /** - * Get current points of the user - * - * @param int $user_id ID of the user - * @return int the amount of points the user currently has - */ - protected function getPoints($user_id) - { - $query = 'select sum(`points`) as `total` from `notary` where `to` = :user'; - $query_params['user'] = $user_id; - $row = $this->db->query($query, $query_params)->fetch(); - if ($row['total'] === NULL) $row['total'] = 0; - - return $row['total']; - } - - /** - * Get the first assurer who didn't already assure the user - * - * @param int $user_id The ID of the user who should get assured - * @return int The ID of the selected assurer - */ - protected function getNewAssurer($user_id) - { - $query = 'select min(`id`) as `assurer` from `users` ' . - 'where `email` like \'john.doe-___@example.com\' and ' . - '`id` not in (select `from` from `notary` where `to` = :user)'; - $query_params['user'] = $user_id; - $row = $this->db->query($query, $query_params)->fetch(); - - if ($row['assurer'] === NULL) { - throw new Exception(__METHOD__ . ': no more assurers that haven\'t '. - 'already assured this account'); - } - - return $row['assurer']; - } - - /** - * Fix the assurer flag for the given user - * - * @param $user_id ID of the user - */ - protected function fixAssurerFlag($user_id) - { - // TODO: unset flag if requirements are not met - - $query = 'UPDATE `users` SET `assurer` = 1 WHERE `users`.`id` = :user AND '. - - 'EXISTS(SELECT * FROM `cats_passed` AS `cp`, `cats_variant` AS `cv` '. - 'WHERE `cp`.`variant_id` = `cv`.`id` AND `cv`.`type_id` = 1 AND '. - '`cp`.`user_id` = :user) AND '. - - '(SELECT SUM(`points`) FROM `notary` WHERE `to` = :user AND '. - '`expire` < now()) >= 100'; - $query_params['user'] = $user_id; - $this->db->query($query, $query_params); - } - protected function getAssuranceForm() { $form = new Zend_Form(); diff --git a/manager/application/models/User.php b/manager/application/models/User.php new file mode 100644 index 0000000..29bd1e7 --- /dev/null +++ b/manager/application/models/User.php @@ -0,0 +1,310 @@ +db = $db; + $this->id = $id; + } + + /** + * Get an user object for the given ID + * + * @param $id int + * @return Application_Model_User + */ + public static function findById($id) { + // Get database connection + $config = new Zend_Config_Ini( + APPLICATION_PATH . '/configs/application.ini', + APPLICATION_ENV); + $db = Zend_Db::factory($config->ca_mgr->db->auth->pdo, + $config->ca_mgr->db->auth); + + // Check if the ID is present on the test server + $query = 'select `id` from `users` where `id` = :user'; + $query_params['user'] = $id; + $result = $db->query($query, $query_params); + if ($result->rowCount() !== 1) { + throw new Exception( + __METHOD__ . ': user ID not found in the data base'); + } + $row = $result->fetch(); + + return new Application_Model_User($db, $row['id']); + } + + /** + * Get an user object for the currently logged in user + * + * @return Application_Model_User + */ + public static function findCurrentUser() { + $session = Zend_Registry::get('session'); + if ($session->authdata['authed'] !== true) { + throw new Exception( + __METHOD__ . ': you need to log in to use this feature'); + } + + return self::findById($session->authdata['authed_id']); + } + + /** + * Get the first assurer who didn't already assure the user + * + * @return Application_Model_User + */ + public function findNewAssurer() + { + $query = 'select min(`id`) as `assurer` from `users` ' . + 'where `email` like \'john.doe-___@example.com\' and ' . + '`id` not in (select `from` from `notary` where `to` = :user)'; + $query_params['user'] = $this->id; + $row = $this->db->query($query, $query_params)->fetch(); + + if ($row['assurer'] === NULL) { + throw new Exception( + __METHOD__ . ': no more assurers that haven\'t already '. + 'assured this account'); + } + + return new Application_Model_User($this->db, $row['assurer']); + } + + /** + * Refresh the current value of points from the test server + * + * Needed if operations outside this class are made, that might affect the + * user's points + */ + public function refreshPoints() { + $query = 'select sum(`points`) as `total` from `notary` '. + 'where `to` = :user'; + $query_params['user'] = $this->id; + $row = $this->db->query($query, $query_params)->fetch(); + if ($row['total'] === null) $row['total'] = 0; + + $this->points = $row['total']; + } + + /** + * Get points of the user + * + * @return int + * The amount of points the user has + */ + public function getPoints() + { + if ($this->points === null) { + $this->refreshPoints(); + } + + return $this->points; + } + + /** + * Fix the assurer flag for the user + */ + public function fixAssurerFlag() + { + // TODO: unset flag if requirements are not met + + $query = 'UPDATE `users` SET `assurer` = 1 WHERE `users`.`id` = :user AND '. + + 'EXISTS(SELECT * FROM `cats_passed` AS `cp`, `cats_variant` AS `cv` '. + 'WHERE `cp`.`variant_id` = `cv`.`id` AND `cv`.`type_id` = 1 AND '. + '`cp`.`user_id` = :user) AND '. + + '(SELECT SUM(`points`) FROM `notary` WHERE `to` = :user AND '. + '`expire` < now()) >= 100'; + $query_params['user'] = $this->id; + $this->db->query($query, $query_params); + } + + /** + * @return boolean + */ + public function getAssurerStatus() { + $query = 'SELECT 1 FROM `users` WHERE `users`.`id` = :user AND '. + '`assurer_blocked` = 0 AND'. + + 'EXISTS(SELECT * FROM `cats_passed` AS `cp`, `cats_variant` AS `cv` '. + 'WHERE `cp`.`variant_id` = `cv`.`id` AND `cv`.`type_id` = 1 AND '. + '`cp`.`user_id` = :user) AND '. + + '(SELECT SUM(`points`) FROM `notary` WHERE `to` = :user AND '. + '`expire` < now()) >= 100'; + $query_params['user'] = $this->id; + $result = $this->db->query($query, $query_params); + if ($result->rowCount() === 1) { + return true; + } + + return false; + } + + /** + * @return Zend_Date + */ + public function getDob() { + $query = 'select `dob` from `users` where `id` = :user'; + $query_params['user'] = $this->id; + $row = $this->db->query($query, $query_params)->fetch(); + + return new Zend_Date($row['dob'], Zend_Date::ISO_8601); + } + + /** + * @return int + */ + public function getAge() { + $now = new Zend_Date(); + return $now->sub($this->getDob())->toValue(Zend_Date::YEAR); + } + + /** + * Assure another user. Usual restrictions apply + * + * @param $assuree Application_Model_User + * @param $points int + * @param $location string + * @param $date string + * @throws Exception + * + * @return int + * The amount of points that have been issued (might be less than + * $points) + */ + public function assure(Application_Model_User $assuree, $points, $location, + $date) { + // Sanitize inputs + $points = intval($points); + $location = stripslashes($location); + $date = stripslashes($date); + + if (!$this->getAssurerStatus()) { + throw new Exception( + __METHOD__ . ': '.$this->id.' needs to be an assurer to do '. + 'assurances'); + } + + if ($this->id === $assuree->id) { + throw new Exception( + __METHOD__ . ': '.$this->id.' is not allowed to assure '. + 'himself'); + } + + $query = 'select * from `notary` where `from`= :assurer and '. + '`to`= :assuree'; + $query_params['assurer'] = $this->id; + $query_params['assuree'] = $assuree->id; + $result = $this->db->query($query, $query_params); + if ($result->rowCount() > 0 && $this->getPoints() < 200) { + throw new Exception( + __METHOD__ . ': '.$this->id.' is not allowed to assure '. + $assuree->id .' more than once'); + } + + // Respect the maximum points + $max = $this->maxpoints(); + $points = min($points, $max); + + $rounddown = $points; + if ($max < 100) { + if ($assuree->getPoints() + $points > 100) + $rounddown = 100 - $assuree->getPoints(); + } else { + if ($assuree->getPoints() + $points > $max) + $rounddown = $max - $assuree->getPoints(); + } + if ($rounddown < 0) $rounddown = 0; + + $query = 'select * from `notary` where `from` = :assurer and '. + '`to` = :assuree and `awarded` = :points and '. + '`location` = :location and `date` = :date'; + $query_params['assurer'] = $this->id; + $query_params['assuree'] = $assuree->id; + $query_params['points'] = $points; + $query_params['location'] = $location; + $query_params['date'] = $date; + $result = $this->db->query($query, $query_params); + if ($result->rowCount() > 0) { + throw new Exception( + __METHOD__ . ': '.$this->id.' is not allowed to do the same '. + 'assurance to '.$assuree->id.' more than once'); + } + + // Make sure it is empty + $assurance = array(); + $assurance['from'] = $this->id; + $assurance['to'] = $assuree->id; + $assurance['points'] = $rounddown; + $assurance['awarded'] = $points; + $assurance['location'] = $location; + $assurance['date'] = $date; + $assurance['when'] = new Zend_Db_Expr('now()'); + + $this->db->insert('notary', $assurance); + $assuree->points += $rounddown; + $assuree->fixAssurerFlag(); + + if ($this->getPoints() < 150) { + $addpoints = 0; + if ($this->getPoints() < 149 && $this->getPoints() >= 100) { + $addpoints = 2; + } elseif ($this->getPoints() === 149) { + $addpoints = 1; + } + + $increase = array(); + $increase['from'] = $this->id; + $increase['to'] = $this->id; + $increase['points'] = $addpoints; + $increase['awarded'] = $addpoints; + $increase['location'] = $location; + $increase['date'] = $date; + $increase['method'] = 'Administrative Increase'; + $increase['when'] = new Zend_Db_Expr('now()'); + + $this->db->insert('notary', $increase); + $this->points += $addpoints; + // No need to fix assurer flag here + } + + return $rounddown; + } + + /** + * Maximum number of points the user may issue + * + * @return int + */ + private function maxpoints() { + if (!$this->getAssurerStatus()) return 0; + + if ($this->getAge() < 18) return 10; + + $points = $this->getPoints(); + if ($points >= 300) return 200; + if ($points >= 200) return 150; + if ($points >= 150) return 35; + if ($points >= 140) return 30; + if ($points >= 130) return 25; + if ($points >= 120) return 20; + if ($points >= 110) return 15; + if ($points >= 100) return 10; + + // Should not get here + throw new Exception( + __METHOD__ . ': '.$this->id.' We have reached unreachable code'); + } +} \ No newline at end of file