diff options
Diffstat (limited to 'app/Model')
-rw-r--r-- | app/Model/Acl.php | 13 | ||||
-rw-r--r-- | app/Model/Budget.php | 214 | ||||
-rw-r--r-- | app/Model/File.php | 244 | ||||
-rw-r--r-- | app/Model/HourlyRate.php | 121 | ||||
-rw-r--r-- | app/Model/Subtask.php | 41 | ||||
-rw-r--r-- | app/Model/SubtaskTimeTracking.php | 19 |
6 files changed, 166 insertions, 486 deletions
diff --git a/app/Model/Acl.php b/app/Model/Acl.php index 8c28cb1a..9a227cf5 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -64,7 +64,6 @@ class Acl extends Base 'export' => '*', 'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'), 'swimlane' => '*', - 'budget' => '*', 'gantt' => array('project', 'savetaskdate', 'task', 'savetask'), ); @@ -95,6 +94,18 @@ class Acl extends Base ); /** + * Extend ACL rules + * + * @access public + * @param string $acl_name + * @param aray $rules + */ + public function extend($acl_name, array $rules) + { + $this->$acl_name = array_merge($this->$acl_name, $rules); + } + + /** * Return true if the specified controller/action match the given acl * * @access public diff --git a/app/Model/Budget.php b/app/Model/Budget.php deleted file mode 100644 index 76c42ca9..00000000 --- a/app/Model/Budget.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php - -namespace Model; - -use DateInterval; -use DateTime; -use SimpleValidator\Validator; -use SimpleValidator\Validators; - -/** - * Budget - * - * @package model - * @author Frederic Guillot - */ -class Budget extends Base -{ - /** - * SQL table name - * - * @var string - */ - const TABLE = 'budget_lines'; - - /** - * Get all budget lines for a project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getAll($project_id) - { - return $this->db->table(self::TABLE)->eq('project_id', $project_id)->desc('date')->findAll(); - } - - /** - * Get the current total of the budget - * - * @access public - * @param integer $project_id - * @return float - */ - public function getTotal($project_id) - { - $result = $this->db->table(self::TABLE)->columns('SUM(amount) as total')->eq('project_id', $project_id)->findOne(); - return isset($result['total']) ? (float) $result['total'] : 0; - } - - /** - * Get breakdown by tasks/subtasks/users - * - * @access public - * @param integer $project_id - * @return \PicoDb\Table - */ - public function getSubtaskBreakdown($project_id) - { - return $this->db - ->table(SubtaskTimeTracking::TABLE) - ->columns( - SubtaskTimeTracking::TABLE.'.id', - SubtaskTimeTracking::TABLE.'.user_id', - SubtaskTimeTracking::TABLE.'.subtask_id', - SubtaskTimeTracking::TABLE.'.start', - SubtaskTimeTracking::TABLE.'.time_spent', - Subtask::TABLE.'.task_id', - Subtask::TABLE.'.title AS subtask_title', - Task::TABLE.'.title AS task_title', - Task::TABLE.'.project_id', - User::TABLE.'.username', - User::TABLE.'.name' - ) - ->join(Subtask::TABLE, 'id', 'subtask_id') - ->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE) - ->join(User::TABLE, 'id', 'user_id') - ->eq(Task::TABLE.'.project_id', $project_id) - ->callback(array($this, 'applyUserRate')); - } - - /** - * Gather necessary information to display the budget graph - * - * @access public - * @param integer $project_id - * @return array - */ - public function getDailyBudgetBreakdown($project_id) - { - $out = array(); - $in = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->gt('amount', 0)->asc('date')->getAll('date', 'amount'); - $time_slots = $this->getSubtaskBreakdown($project_id)->findAll(); - - foreach ($time_slots as $slot) { - $date = date('Y-m-d', $slot['start']); - - if (! isset($out[$date])) { - $out[$date] = 0; - } - - $out[$date] += $slot['cost']; - } - - $start = key($in) ?: key($out); - $end = new DateTime; - $left = 0; - $serie = array(); - - for ($today = new DateTime($start); $today <= $end; $today->add(new DateInterval('P1D'))) { - - $date = $today->format('Y-m-d'); - $today_in = isset($in[$date]) ? (int) $in[$date] : 0; - $today_out = isset($out[$date]) ? (int) $out[$date] : 0; - - if ($today_in > 0 || $today_out > 0) { - - $left += $today_in; - $left -= $today_out; - - $serie[] = array( - 'date' => $date, - 'in' => $today_in, - 'out' => -$today_out, - 'left' => $left, - ); - } - } - - return $serie; - } - - /** - * Filter callback to apply the rate according to the effective date - * - * @access public - * @param array $records - * @return array - */ - public function applyUserRate(array $records) - { - $rates = $this->hourlyRate->getAllByProject($records[0]['project_id']); - - foreach ($records as &$record) { - - $hourly_price = 0; - - foreach ($rates as $rate) { - - if ($rate['user_id'] == $record['user_id'] && date('Y-m-d', $rate['date_effective']) <= date('Y-m-d', $record['start'])) { - $hourly_price = $this->currency->getPrice($rate['currency'], $rate['rate']); - break; - } - } - - $record['cost'] = $hourly_price * $record['time_spent']; - } - - return $records; - } - - /** - * Add a new budget line in the database - * - * @access public - * @param integer $project_id - * @param float $amount - * @param string $comment - * @param string $date - * @return boolean|integer - */ - public function create($project_id, $amount, $comment, $date = '') - { - $values = array( - 'project_id' => $project_id, - 'amount' => $amount, - 'comment' => $comment, - 'date' => $date ?: date('Y-m-d'), - ); - - return $this->persist(self::TABLE, $values); - } - - /** - * Remove a specific budget line - * - * @access public - * @param integer $budget_id - * @return boolean - */ - public function remove($budget_id) - { - return $this->db->table(self::TABLE)->eq('id', $budget_id)->remove(); - } - - /** - * Validate creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - $v = new Validator($values, array( - new Validators\Required('project_id', t('Field required')), - new Validators\Required('amount', t('Field required')), - )); - - return array( - $v->execute(), - $v->getErrors() - ); - } -}
\ No newline at end of file diff --git a/app/Model/File.php b/app/Model/File.php index f884e460..7adab42b 100644 --- a/app/Model/File.php +++ b/app/Model/File.php @@ -3,6 +3,8 @@ namespace Model; use Event\FileEvent; +use Core\Tool; +use Core\ObjectStorage\ObjectStorageException; /** * File model @@ -47,14 +49,17 @@ class File extends Base */ public function remove($file_id) { - $file = $this->getbyId($file_id); + try { - if (! empty($file)) { - @unlink(FILES_DIR.$file['path']); - return $this->db->table(self::TABLE)->eq('id', $file_id)->remove(); - } + $file = $this->getbyId($file_id); + $this->objectStorage->remove($file['path']); - return false; + return $this->db->table(self::TABLE)->eq('id', $file['id'])->remove(); + } + catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } } /** @@ -66,11 +71,11 @@ class File extends Base */ public function removeAll($task_id) { - $files = $this->getAll($task_id); + $file_ids = $this->db->table(self::TABLE)->eq('task_id', $task_id)->asc('id')->findAllByColumn('id'); $results = array(); - foreach ($files as $file) { - $results[] = $this->remove($file['id']); + foreach ($file_ids as $file_id) { + $results[] = $this->remove($file_id); } return ! in_array(false, $results, true); @@ -196,6 +201,30 @@ class File extends Base } /** + * Return the image mimetype based on the file extension + * + * @access public + * @param $filename + * @return string + */ + public function getImageMimeType($filename) + { + $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + + switch ($extension) { + case 'jpeg': + case 'jpg': + return 'image/jpeg'; + case 'png': + return 'image/png'; + case 'gif': + return 'image/gif'; + default: + return 'image/jpeg'; + } + } + + /** * Generate the path for a new filename * * @access public @@ -210,6 +239,18 @@ class File extends Base } /** + * Generate the path for a thumbnails + * + * @access public + * @param string $key Storage key + * @return string + */ + public function getThumbnailPath($key) + { + return 'thumbnails'.DIRECTORY_SEPARATOR.$key; + } + + /** * Handle file upload * * @access public @@ -218,11 +259,13 @@ class File extends Base * @param string $form_name File form name * @return bool */ - public function upload($project_id, $task_id, $form_name) + public function uploadFiles($project_id, $task_id, $form_name) { - $results = array(); + try { - if (! empty($_FILES[$form_name])) { + if (empty($_FILES[$form_name])) { + return false; + } foreach ($_FILES[$form_name]['error'] as $key => $error) { @@ -232,22 +275,27 @@ class File extends Base $uploaded_filename = $_FILES[$form_name]['tmp_name'][$key]; $destination_filename = $this->generatePath($project_id, $task_id, $original_filename); - @mkdir(FILES_DIR.dirname($destination_filename), 0755, true); + if ($this->isImage($original_filename)) { + $this->generateThumbnailFromFile($uploaded_filename, $destination_filename); + } - if (@move_uploaded_file($uploaded_filename, FILES_DIR.$destination_filename)) { + $this->objectStorage->moveUploadedFile($uploaded_filename, $destination_filename); - $results[] = $this->create( - $task_id, - $original_filename, - $destination_filename, - $_FILES[$form_name]['size'][$key] - ); - } + $this->create( + $task_id, + $original_filename, + $destination_filename, + $_FILES[$form_name]['size'][$key] + ); } } - } - return ! in_array(false, $results, true); + return true; + } + catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } } /** @@ -261,129 +309,77 @@ class File extends Base */ public function uploadScreenshot($project_id, $task_id, $blob) { - $data = base64_decode($blob); - - if (empty($data)) { - return false; - } - $original_filename = e('Screenshot taken %s', dt('%B %e, %Y at %k:%M %p', time())).'.png'; - $destination_filename = $this->generatePath($project_id, $task_id, $original_filename); - - @mkdir(FILES_DIR.dirname($destination_filename), 0755, true); - @file_put_contents(FILES_DIR.$destination_filename, $data); - - return $this->create( - $task_id, - $original_filename, - $destination_filename, - strlen($data) - ); + return $this->uploadContent($project_id, $task_id, $original_filename, $blob); } /** * Handle file upload (base64 encoded content) * * @access public - * @param integer $project_id Project id - * @param integer $task_id Task id - * @param string $filename Filename - * @param string $blob Base64 encoded image + * @param integer $project_id Project id + * @param integer $task_id Task id + * @param string $original_filename Filename + * @param string $blob Base64 encoded file * @return bool|integer */ - public function uploadContent($project_id, $task_id, $filename, $blob) + public function uploadContent($project_id, $task_id, $original_filename, $blob) { - $data = base64_decode($blob); + try { - if (empty($data)) { - return false; - } + $data = base64_decode($blob); - $destination_filename = $this->generatePath($project_id, $task_id, $filename); + if (empty($data)) { + return false; + } + + $destination_filename = $this->generatePath($project_id, $task_id, $original_filename); + $this->objectStorage->put($destination_filename, $data); - @mkdir(FILES_DIR.dirname($destination_filename), 0755, true); - @file_put_contents(FILES_DIR.$destination_filename, $data); + if ($this->isImage($original_filename)) { + $this->generateThumbnailFromData($destination_filename, $data); + } - return $this->create( - $task_id, - $filename, - $destination_filename, - strlen($data) - ); + return $this->create( + $task_id, + $original_filename, + $destination_filename, + strlen($data) + ); + } + catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } } /** - * Generate a jpeg thumbnail from an image (output directly the image) + * Generate thumbnail from a blob * * @access public - * @param string $filename Source image - * @param integer $resize_width Desired image width - * @param integer $resize_height Desired image height + * @param string $destination_filename + * @param string $data */ - public function generateThumbnail($filename, $resize_width, $resize_height) + public function generateThumbnailFromData($destination_filename, &$data) { - $metadata = getimagesize($filename); - $src_width = $metadata[0]; - $src_height = $metadata[1]; - $dst_y = 0; - $dst_x = 0; - - if (empty($metadata['mime'])) { - return; - } - - if ($resize_width == 0 && $resize_height == 0) { - $resize_width = 100; - $resize_height = 100; - } + $temp_filename = tempnam(sys_get_temp_dir(), 'datafile'); - if ($resize_width > 0 && $resize_height == 0) { - $dst_width = $resize_width; - $dst_height = floor($src_height * ($resize_width / $src_width)); - $dst_image = imagecreatetruecolor($dst_width, $dst_height); - } - elseif ($resize_width == 0 && $resize_height > 0) { - $dst_width = floor($src_width * ($resize_height / $src_height)); - $dst_height = $resize_height; - $dst_image = imagecreatetruecolor($dst_width, $dst_height); - } - else { - - $src_ratio = $src_width / $src_height; - $resize_ratio = $resize_width / $resize_height; - - if ($src_ratio <= $resize_ratio) { - $dst_width = $resize_width; - $dst_height = floor($src_height * ($resize_width / $src_width)); - - $dst_y = ($dst_height - $resize_height) / 2 * (-1); - } - else { - $dst_width = floor($src_width * ($resize_height / $src_height)); - $dst_height = $resize_height; - - $dst_x = ($dst_width - $resize_width) / 2 * (-1); - } - - $dst_image = imagecreatetruecolor($resize_width, $resize_height); - } - - switch ($metadata['mime']) { - case 'image/jpeg': - case 'image/jpg': - $src_image = imagecreatefromjpeg($filename); - break; - case 'image/png': - $src_image = imagecreatefrompng($filename); - break; - case 'image/gif': - $src_image = imagecreatefromgif($filename); - break; - default: - return; - } + file_put_contents($temp_filename, $data); + $this->generateThumbnailFromFile($temp_filename, $destination_filename); + unlink($temp_filename); + } - imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $src_width, $src_height); - imagejpeg($dst_image); + /** + * Generate thumbnail from a blob + * + * @access public + * @param string $uploaded_filename + * @param string $destination_filename + */ + public function generateThumbnailFromFile($uploaded_filename, $destination_filename) + { + $thumbnail_filename = tempnam(sys_get_temp_dir(), 'thumbnail'); + Tool::generateThumbnail($uploaded_filename, $thumbnail_filename); + $this->objectStorage->moveFile($thumbnail_filename, $this->getThumbnailPath($destination_filename)); } } diff --git a/app/Model/HourlyRate.php b/app/Model/HourlyRate.php deleted file mode 100644 index 1550bdae..00000000 --- a/app/Model/HourlyRate.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php - -namespace Model; - -use SimpleValidator\Validator; -use SimpleValidator\Validators; - -/** - * Hourly Rate - * - * @package model - * @author Frederic Guillot - */ -class HourlyRate extends Base -{ - /** - * SQL table name - * - * @var string - */ - const TABLE = 'hourly_rates'; - - /** - * Get all user rates for a given project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getAllByProject($project_id) - { - $members = $this->projectPermission->getMembers($project_id); - - if (empty($members)) { - return array(); - } - - return $this->db->table(self::TABLE)->in('user_id', array_keys($members))->desc('date_effective')->findAll(); - } - - /** - * Get all rates for a given user - * - * @access public - * @param integer $user_id User id - * @return array - */ - public function getAllByUser($user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findAll(); - } - - /** - * Get current rate for a given user - * - * @access public - * @param integer $user_id User id - * @return float - */ - public function getCurrentRate($user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findOneColumn('rate') ?: 0; - } - - /** - * Add a new rate in the database - * - * @access public - * @param integer $user_id User id - * @param float $rate Hourly rate - * @param string $currency Currency code - * @param string $date ISO8601 date format - * @return boolean|integer - */ - public function create($user_id, $rate, $currency, $date) - { - $values = array( - 'user_id' => $user_id, - 'rate' => $rate, - 'currency' => $currency, - 'date_effective' => $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($date)), - ); - - return $this->persist(self::TABLE, $values); - } - - /** - * Remove a specific rate - * - * @access public - * @param integer $rate_id - * @return boolean - */ - public function remove($rate_id) - { - return $this->db->table(self::TABLE)->eq('id', $rate_id)->remove(); - } - - /** - * Validate creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - $v = new Validator($values, array( - new Validators\Required('user_id', t('Field required')), - new Validators\Required('rate', t('Field required')), - new Validators\Numeric('rate', t('This value must be numeric')), - new Validators\Required('date_effective', t('Field required')), - new Validators\Required('currency', t('Field required')), - )); - - return array( - $v->execute(), - $v->getErrors() - ); - } -} diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php index d8a44aff..24508c91 100644 --- a/app/Model/Subtask.php +++ b/app/Model/Subtask.php @@ -49,6 +49,7 @@ class Subtask extends Base */ const EVENT_UPDATE = 'subtask.update'; const EVENT_CREATE = 'subtask.create'; + const EVENT_DELETE = 'subtask.delete'; /** * Get available status @@ -174,6 +175,23 @@ class Subtask extends Base } /** + * Prepare data before insert + * + * @access public + * @param array $values Form values + */ + public function prepareCreation(array &$values) + { + $this->prepare($values); + + $values['position'] = $this->getLastPosition($values['task_id']) + 1; + $values['status'] = isset($values['status']) ? $values['status'] : self::STATUS_TODO; + $values['time_estimated'] = isset($values['time_estimated']) ? $values['time_estimated'] : 0; + $values['time_spent'] = isset($values['time_spent']) ? $values['time_spent'] : 0; + $values['user_id'] = isset($values['user_id']) ? $values['user_id'] : 0; + } + + /** * Get the position of the last column for a given project * * @access public @@ -198,9 +216,7 @@ class Subtask extends Base */ public function create(array $values) { - $this->prepare($values); - $values['position'] = $this->getLastPosition($values['task_id']) + 1; - + $this->prepareCreation($values); $subtask_id = $this->persist(self::TABLE, $values); if ($subtask_id) { @@ -223,14 +239,13 @@ class Subtask extends Base public function update(array $values) { $this->prepare($values); + $subtask = $this->getById($values['id']); $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values); if ($result) { - - $this->container['dispatcher']->dispatch( - self::EVENT_UPDATE, - new SubtaskEvent($values) - ); + $event = $subtask; + $event['changes'] = array_diff_assoc($values, $subtask); + $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new SubtaskEvent($event)); } return $result; @@ -302,7 +317,6 @@ class Subtask extends Base $positions = array_flip($subtasks); if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] < count($subtasks)) { - $position = ++$subtasks[$subtask_id]; $subtasks[$positions[$position]]--; @@ -402,7 +416,14 @@ class Subtask extends Base */ public function remove($subtask_id) { - return $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove(); + $subtask = $this->getById($subtask_id); + $result = $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove(); + + if ($result) { + $this->container['dispatcher']->dispatch(self::EVENT_DELETE, new SubtaskEvent($subtask)); + } + + return $result; } /** diff --git a/app/Model/SubtaskTimeTracking.php b/app/Model/SubtaskTimeTracking.php index 997031e8..56998769 100644 --- a/app/Model/SubtaskTimeTracking.php +++ b/app/Model/SubtaskTimeTracking.php @@ -339,20 +339,7 @@ class SubtaskTimeTracking extends Base */ public function updateTaskTimeTracking($task_id) { - $result = $this->calculateSubtaskTime($task_id); - $values = array(); - - if ($result['total_spent'] > 0) { - $values['time_spent'] = $result['total_spent']; - } - - if ($result['total_estimated'] > 0) { - $values['time_estimated'] = $result['total_estimated']; - } - - if (empty($values)) { - return true; - } + $values = $this->calculateSubtaskTime($task_id); return $this->db ->table(Task::TABLE) @@ -373,8 +360,8 @@ class SubtaskTimeTracking extends Base ->table(Subtask::TABLE) ->eq('task_id', $task_id) ->columns( - 'SUM(time_spent) AS total_spent', - 'SUM(time_estimated) AS total_estimated' + 'SUM(time_spent) AS time_spent', + 'SUM(time_estimated) AS time_estimated' ) ->findOne(); } |