diff options
Diffstat (limited to 'app/Model')
-rw-r--r-- | app/Model/AvatarFile.php | 112 | ||||
-rw-r--r-- | app/Model/Base.php | 24 | ||||
-rw-r--r-- | app/Model/Comment.php | 7 | ||||
-rw-r--r-- | app/Model/Config.php | 2 | ||||
-rw-r--r-- | app/Model/File.php | 20 | ||||
-rw-r--r-- | app/Model/Project.php | 14 | ||||
-rw-r--r-- | app/Model/ProjectActivity.php | 152 | ||||
-rw-r--r-- | app/Model/ProjectGroupRoleFilter.php | 89 | ||||
-rw-r--r-- | app/Model/ProjectPermission.php | 18 | ||||
-rw-r--r-- | app/Model/ProjectUserRole.php | 4 | ||||
-rw-r--r-- | app/Model/ProjectUserRoleFilter.php | 88 | ||||
-rw-r--r-- | app/Model/Setting.php | 1 | ||||
-rw-r--r-- | app/Model/SubtaskTimeTracking.php | 88 | ||||
-rw-r--r-- | app/Model/TaskFilter.php | 745 | ||||
-rw-r--r-- | app/Model/TaskFinder.php | 23 | ||||
-rw-r--r-- | app/Model/User.php | 9 | ||||
-rw-r--r-- | app/Model/UserFilter.php | 80 | ||||
-rw-r--r-- | app/Model/UserNotification.php | 19 | ||||
-rw-r--r-- | app/Model/UserNotificationFilter.php | 9 |
19 files changed, 222 insertions, 1282 deletions
diff --git a/app/Model/AvatarFile.php b/app/Model/AvatarFile.php new file mode 100644 index 00000000..c49f9fd5 --- /dev/null +++ b/app/Model/AvatarFile.php @@ -0,0 +1,112 @@ +<?php + +namespace Kanboard\Model; + +use Exception; + +/** + * Avatar File + * + * @package model + * @author Frederic Guillot + */ +class AvatarFile extends Base +{ + /** + * Path prefix + * + * @var string + */ + const PATH_PREFIX = 'avatars'; + + /** + * Get image filename + * + * @access public + * @param integer $user_id + * @return string + */ + public function getFilename($user_id) + { + return $this->db->table(User::TABLE)->eq('id', $user_id)->findOneColumn('avatar_path'); + } + + /** + * Add avatar in the user profile + * + * @access public + * @param integer $user_id Foreign key + * @param string $path Path on the disk + * @return bool + */ + public function create($user_id, $path) + { + $result = $this->db->table(User::TABLE)->eq('id', $user_id)->update(array( + 'avatar_path' => $path, + )); + + $this->userSession->refresh($user_id); + + return $result; + } + + /** + * Remove avatar from the user profile + * + * @access public + * @param integer $user_id Foreign key + * @return bool + */ + public function remove($user_id) + { + try { + $this->objectStorage->remove($this->getFilename($user_id)); + $result = $this->db->table(User::TABLE)->eq('id', $user_id)->update(array('avatar_path' => '')); + $this->userSession->refresh($user_id); + return $result; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Upload avatar image + * + * @access public + * @param integer $user_id + * @param array $file + * @return boolean + */ + public function uploadFile($user_id, array $file) + { + try { + if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { + $destination_filename = $this->generatePath($user_id, $file['name']); + $this->objectStorage->moveUploadedFile($file['tmp_name'], $destination_filename); + $this->create($user_id, $destination_filename); + } else { + throw new Exception('File not uploaded: '.var_export($file['error'], true)); + } + + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + + return true; + } + + /** + * Generate the path for a new filename + * + * @access public + * @param integer $user_id + * @param string $filename + * @return string + */ + public function generatePath($user_id, $filename) + { + return implode(DIRECTORY_SEPARATOR, array(self::PATH_PREFIX, $user_id, hash('sha1', $filename.time()))); + } +} diff --git a/app/Model/Base.php b/app/Model/Base.php index 714b4308..a27560c8 100644 --- a/app/Model/Base.php +++ b/app/Model/Base.php @@ -31,28 +31,4 @@ abstract class Base extends \Kanboard\Core\Base return (int) $db->getLastId(); }); } - - /** - * Build SQL condition for a given time range - * - * @access protected - * @param string $start_time Start timestamp - * @param string $end_time End timestamp - * @param string $start_column Start column name - * @param string $end_column End column name - * @return string - */ - protected function getCalendarCondition($start_time, $end_time, $start_column, $end_column) - { - $start_column = $this->db->escapeIdentifier($start_column); - $end_column = $this->db->escapeIdentifier($end_column); - - $conditions = array( - "($start_column >= '$start_time' AND $start_column <= '$end_time')", - "($start_column <= '$start_time' AND $end_column >= '$start_time')", - "($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))", - ); - - return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')'; - } } diff --git a/app/Model/Comment.php b/app/Model/Comment.php index 6eb4a1e5..c5091d89 100644 --- a/app/Model/Comment.php +++ b/app/Model/Comment.php @@ -48,7 +48,8 @@ class Comment extends Base self::TABLE.'.comment', User::TABLE.'.username', User::TABLE.'.name', - User::TABLE.'.email' + User::TABLE.'.email', + User::TABLE.'.avatar_path' ) ->join(User::TABLE, 'id', 'user_id') ->orderBy(self::TABLE.'.date_creation', $sorting) @@ -75,7 +76,9 @@ class Comment extends Base self::TABLE.'.comment', self::TABLE.'.reference', User::TABLE.'.username', - User::TABLE.'.name' + User::TABLE.'.name', + User::TABLE.'.email', + User::TABLE.'.avatar_path' ) ->join(User::TABLE, 'id', 'user_id') ->eq(self::TABLE.'.id', $comment_id) diff --git a/app/Model/Config.php b/app/Model/Config.php index 7b254c8d..0c363fb0 100644 --- a/app/Model/Config.php +++ b/app/Model/Config.php @@ -90,6 +90,7 @@ class Config extends Setting 'fi_FI' => 'Suomi', 'sv_SE' => 'Svenska', 'tr_TR' => 'Türkçe', + 'ko_KR' => '한국어', 'zh_CN' => '中文(简体)', 'ja_JP' => '日本語', 'th_TH' => 'ไทย', @@ -129,6 +130,7 @@ class Config extends Setting 'fi_FI' => 'fi', 'sv_SE' => 'sv', 'tr_TR' => 'tr', + 'ko_KR' => 'ko', 'zh_CN' => 'zh-cn', 'ja_JP' => 'ja', 'th_TH' => 'th', diff --git a/app/Model/File.php b/app/Model/File.php index 03ea691d..e383235c 100644 --- a/app/Model/File.php +++ b/app/Model/File.php @@ -3,8 +3,8 @@ namespace Kanboard\Model; use Exception; +use Kanboard\Core\Thumbnail; use Kanboard\Event\FileEvent; -use Kanboard\Core\Tool; use Kanboard\Core\ObjectStorage\ObjectStorageException; /** @@ -315,15 +315,15 @@ abstract class File extends Base */ public function generateThumbnailFromData($destination_filename, &$data) { - $temp_filename = tempnam(sys_get_temp_dir(), 'datafile'); + $blob = Thumbnail::createFromString($data) + ->resize() + ->toString(); - file_put_contents($temp_filename, $data); - $this->generateThumbnailFromFile($temp_filename, $destination_filename); - unlink($temp_filename); + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); } /** - * Generate thumbnail from a blob + * Generate thumbnail from a local file * * @access public * @param string $uploaded_filename @@ -331,8 +331,10 @@ abstract class File extends Base */ 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)); + $blob = Thumbnail::createFromFile($uploaded_filename) + ->resize() + ->toString(); + + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); } } diff --git a/app/Model/Project.php b/app/Model/Project.php index d2e5b7ce..6e3c2326 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -35,6 +35,20 @@ class Project extends Base const INACTIVE = 0; /** + * Value for private project + * + * @var integer + */ + const TYPE_PRIVATE = 1; + + /** + * Value for team project + * + * @var integer + */ + const TYPE_TEAM = 0; + + /** * Get a project by the id * * @access public diff --git a/app/Model/ProjectActivity.php b/app/Model/ProjectActivity.php index 74df26a1..d993015b 100644 --- a/app/Model/ProjectActivity.php +++ b/app/Model/ProjectActivity.php @@ -2,6 +2,8 @@ namespace Kanboard\Model; +use PicoDb\Table; + /** * Project activity model * @@ -51,113 +53,26 @@ class ProjectActivity extends Base } /** - * Get all events for the given project - * - * @access public - * @param integer $project_id Project id - * @param integer $limit Maximum events number - * @param integer $start Timestamp of earliest activity - * @param integer $end Timestamp of latest activity - * @return array - */ - public function getProject($project_id, $limit = 50, $start = null, $end = null) - { - return $this->getProjects(array($project_id), $limit, $start, $end); - } - - /** - * Get all events for the given projects list + * Get query * * @access public - * @param integer[] $project_ids Projects id - * @param integer $limit Maximum events number - * @param integer $start Timestamp of earliest activity - * @param integer $end Timestamp of latest activity - * @return array + * @return Table */ - public function getProjects(array $project_ids, $limit = 50, $start = null, $end = null) + public function getQuery() { - if (empty($project_ids)) { - return array(); - } - - $query = $this - ->db - ->table(self::TABLE) - ->columns( - self::TABLE.'.*', - User::TABLE.'.username AS author_username', - User::TABLE.'.name AS author_name', - User::TABLE.'.email' - ) - ->in('project_id', $project_ids) - ->join(User::TABLE, 'id', 'creator_id') - ->desc(self::TABLE.'.id') - ->limit($limit); - - return $this->getEvents($query, $start, $end); - } - - /** - * Get all events for the given task - * - * @access public - * @param integer $task_id Task id - * @param integer $limit Maximum events number - * @param integer $start Timestamp of earliest activity - * @param integer $end Timestamp of latest activity - * @return array - */ - public function getTask($task_id, $limit = 50, $start = null, $end = null) - { - $query = $this - ->db - ->table(self::TABLE) - ->columns( - self::TABLE.'.*', - User::TABLE.'.username AS author_username', - User::TABLE.'.name AS author_name', - User::TABLE.'.email' - ) - ->eq('task_id', $task_id) - ->join(User::TABLE, 'id', 'creator_id') - ->desc(self::TABLE.'.id') - ->limit($limit); - - return $this->getEvents($query, $start, $end); - } - - /** - * Common function to return events - * - * @access public - * @param \PicoDb\Table $query PicoDb Query - * @param integer $start Timestamp of earliest activity - * @param integer $end Timestamp of latest activity - * @return array - */ - private function getEvents(\PicoDb\Table $query, $start, $end) - { - if (! is_null($start)) { - $query->gte('date_creation', $start); - } - - if (! is_null($end)) { - $query->lte('date_creation', $end); - } - - $events = $query->findAll(); - - foreach ($events as &$event) { - $event += $this->decode($event['data']); - unset($event['data']); - - $event['author'] = $event['author_name'] ?: $event['author_username']; - $event['event_title'] = $this->notification->getTitleWithAuthor($event['author'], $event['event_name'], $event); - $event['event_content'] = $this->getContent($event); - } - - return $events; + return $this + ->db + ->table(ProjectActivity::TABLE) + ->columns( + ProjectActivity::TABLE.'.*', + 'uc.username AS author_username', + 'uc.name AS author_name', + 'uc.email', + 'uc.avatar_path' + ) + ->join(Task::TABLE, 'id', 'task_id') + ->join(Project::TABLE, 'id', 'project_id') + ->left(User::TABLE, 'uc', 'id', ProjectActivity::TABLE, 'creator_id'); } /** @@ -175,35 +90,4 @@ class ProjectActivity extends Base $this->db->table(self::TABLE)->in('id', $ids)->remove(); } } - - /** - * Get the event html content - * - * @access public - * @param array $params Event properties - * @return string - */ - public function getContent(array $params) - { - return $this->template->render( - 'event/'.str_replace('.', '_', $params['event_name']), - $params - ); - } - - /** - * Decode event data, supports unserialize() and json_decode() - * - * @access public - * @param string $data Serialized data - * @return array - */ - public function decode($data) - { - if ($data{0} === 'a') { - return unserialize($data); - } - - return json_decode($data, true) ?: array(); - } } diff --git a/app/Model/ProjectGroupRoleFilter.php b/app/Model/ProjectGroupRoleFilter.php deleted file mode 100644 index 989d3073..00000000 --- a/app/Model/ProjectGroupRoleFilter.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -namespace Kanboard\Model; - -/** - * Project Group Role Filter - * - * @package model - * @author Frederic Guillot - */ -class ProjectGroupRoleFilter extends Base -{ - /** - * Query - * - * @access protected - * @var \PicoDb\Table - */ - protected $query; - - /** - * Initialize filter - * - * @access public - * @return UserFilter - */ - public function create() - { - $this->query = $this->db->table(ProjectGroupRole::TABLE); - return $this; - } - - /** - * Get all results of the filter - * - * @access public - * @param string $column - * @return array - */ - public function findAll($column = '') - { - if ($column !== '') { - return $this->query->asc($column)->findAllByColumn($column); - } - - return $this->query->findAll(); - } - - /** - * Get the PicoDb query - * - * @access public - * @return \PicoDb\Table - */ - public function getQuery() - { - return $this->query; - } - - /** - * Filter by project id - * - * @access public - * @param integer $project_id - * @return ProjectUserRoleFilter - */ - public function filterByProjectId($project_id) - { - $this->query->eq(ProjectGroupRole::TABLE.'.project_id', $project_id); - return $this; - } - - /** - * Filter by username - * - * @access public - * @param string $input - * @return ProjectUserRoleFilter - */ - public function startWithUsername($input) - { - $this->query - ->join(GroupMember::TABLE, 'group_id', 'group_id', ProjectGroupRole::TABLE) - ->join(User::TABLE, 'id', 'user_id', GroupMember::TABLE) - ->ilike(User::TABLE.'.username', $input.'%'); - - return $this; - } -} diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php index db1573ae..59af2b58 100644 --- a/app/Model/ProjectPermission.php +++ b/app/Model/ProjectPermission.php @@ -3,6 +3,10 @@ namespace Kanboard\Model; use Kanboard\Core\Security\Role; +use Kanboard\Filter\ProjectGroupRoleProjectFilter; +use Kanboard\Filter\ProjectGroupRoleUsernameFilter; +use Kanboard\Filter\ProjectUserRoleProjectFilter; +use Kanboard\Filter\ProjectUserRoleUsernameFilter; /** * Project Permission @@ -53,8 +57,18 @@ class ProjectPermission extends Base */ public function findUsernames($project_id, $input) { - $userMembers = $this->projectUserRoleFilter->create()->filterByProjectId($project_id)->startWithUsername($input)->findAll('username'); - $groupMembers = $this->projectGroupRoleFilter->create()->filterByProjectId($project_id)->startWithUsername($input)->findAll('username'); + $userMembers = $this->projectUserRoleQuery + ->withFilter(new ProjectUserRoleProjectFilter($project_id)) + ->withFilter(new ProjectUserRoleUsernameFilter($input)) + ->getQuery() + ->findAllByColumn('username'); + + $groupMembers = $this->projectGroupRoleQuery + ->withFilter(new ProjectGroupRoleProjectFilter($project_id)) + ->withFilter(new ProjectGroupRoleUsernameFilter($input)) + ->getQuery() + ->findAllByColumn('username'); + $members = array_unique(array_merge($userMembers, $groupMembers)); sort($members); diff --git a/app/Model/ProjectUserRole.php b/app/Model/ProjectUserRole.php index 56da679c..2956c524 100644 --- a/app/Model/ProjectUserRole.php +++ b/app/Model/ProjectUserRole.php @@ -251,8 +251,8 @@ class ProjectUserRole extends Base /** * Copy user access from a project to another one * - * @param integer $project_src_id Project Template - * @return integer $project_dst_id Project that receives the copy + * @param integer $project_src_id + * @param integer $project_dst_id * @return boolean */ public function duplicate($project_src_id, $project_dst_id) diff --git a/app/Model/ProjectUserRoleFilter.php b/app/Model/ProjectUserRoleFilter.php deleted file mode 100644 index 64403643..00000000 --- a/app/Model/ProjectUserRoleFilter.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php - -namespace Kanboard\Model; - -/** - * Project User Role Filter - * - * @package model - * @author Frederic Guillot - */ -class ProjectUserRoleFilter extends Base -{ - /** - * Query - * - * @access protected - * @var \PicoDb\Table - */ - protected $query; - - /** - * Initialize filter - * - * @access public - * @return UserFilter - */ - public function create() - { - $this->query = $this->db->table(ProjectUserRole::TABLE); - return $this; - } - - /** - * Get all results of the filter - * - * @access public - * @param string $column - * @return array - */ - public function findAll($column = '') - { - if ($column !== '') { - return $this->query->asc($column)->findAllByColumn($column); - } - - return $this->query->findAll(); - } - - /** - * Get the PicoDb query - * - * @access public - * @return \PicoDb\Table - */ - public function getQuery() - { - return $this->query; - } - - /** - * Filter by project id - * - * @access public - * @param integer $project_id - * @return ProjectUserRoleFilter - */ - public function filterByProjectId($project_id) - { - $this->query->eq(ProjectUserRole::TABLE.'.project_id', $project_id); - return $this; - } - - /** - * Filter by username - * - * @access public - * @param string $input - * @return ProjectUserRoleFilter - */ - public function startWithUsername($input) - { - $this->query - ->join(User::TABLE, 'id', 'user_id') - ->ilike(User::TABLE.'.username', $input.'%'); - - return $this; - } -} diff --git a/app/Model/Setting.php b/app/Model/Setting.php index f98d7ce1..c5a4765c 100644 --- a/app/Model/Setting.php +++ b/app/Model/Setting.php @@ -22,6 +22,7 @@ abstract class Setting extends Base * * @abstract * @access public + * @param array $values * @return array */ abstract public function prepare(array $values); diff --git a/app/Model/SubtaskTimeTracking.php b/app/Model/SubtaskTimeTracking.php index b766b542..be04ee1b 100644 --- a/app/Model/SubtaskTimeTracking.php +++ b/app/Model/SubtaskTimeTracking.php @@ -146,94 +146,6 @@ class SubtaskTimeTracking extends Base } /** - * Get user calendar events - * - * @access public - * @param integer $user_id - * @param string $start ISO-8601 format - * @param string $end - * @return array - */ - public function getUserCalendarEvents($user_id, $start, $end) - { - $hook = 'model:subtask-time-tracking:calendar:events'; - $events = $this->getUserQuery($user_id) - ->addCondition($this->getCalendarCondition( - $this->dateParser->getTimestampFromIsoFormat($start), - $this->dateParser->getTimestampFromIsoFormat($end), - 'start', - 'end' - )) - ->findAll(); - - if ($this->hook->exists($hook)) { - $events = $this->hook->first($hook, array( - 'user_id' => $user_id, - 'events' => $events, - 'start' => $start, - 'end' => $end, - )); - } - - return $this->toCalendarEvents($events); - } - - /** - * Get project calendar events - * - * @access public - * @param integer $project_id - * @param integer $start - * @param integer $end - * @return array - */ - public function getProjectCalendarEvents($project_id, $start, $end) - { - $result = $this - ->getProjectQuery($project_id) - ->addCondition($this->getCalendarCondition( - $this->dateParser->getTimestampFromIsoFormat($start), - $this->dateParser->getTimestampFromIsoFormat($end), - 'start', - 'end' - )) - ->findAll(); - - return $this->toCalendarEvents($result); - } - - /** - * Convert a record set to calendar events - * - * @access private - * @param array $rows - * @return array - */ - private function toCalendarEvents(array $rows) - { - $events = array(); - - foreach ($rows as $row) { - $user = isset($row['username']) ? ' ('.($row['user_fullname'] ?: $row['username']).')' : ''; - - $events[] = array( - 'id' => $row['id'], - 'subtask_id' => $row['subtask_id'], - 'title' => t('#%d', $row['task_id']).' '.$row['subtask_title'].$user, - 'start' => date('Y-m-d\TH:i:s', $row['start']), - 'end' => date('Y-m-d\TH:i:s', $row['end'] ?: time()), - 'backgroundColor' => $this->color->getBackgroundColor($row['color_id']), - 'borderColor' => $this->color->getBorderColor($row['color_id']), - 'textColor' => 'black', - 'url' => $this->helper->url->to('task', 'show', array('task_id' => $row['task_id'], 'project_id' => $row['project_id'])), - 'editable' => false, - ); - } - - return $events; - } - - /** * Return true if a timer is started for this use and subtask * * @access public diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php deleted file mode 100644 index 1883298d..00000000 --- a/app/Model/TaskFilter.php +++ /dev/null @@ -1,745 +0,0 @@ -<?php - -namespace Kanboard\Model; - -/** - * Task Filter - * - * @package model - * @author Frederic Guillot - */ -class TaskFilter extends Base -{ - /** - * Filters mapping - * - * @access private - * @var array - */ - private $filters = array( - 'T_ASSIGNEE' => 'filterByAssignee', - 'T_COLOR' => 'filterByColors', - 'T_DUE' => 'filterByDueDate', - 'T_UPDATED' => 'filterByModificationDate', - 'T_CREATED' => 'filterByCreationDate', - 'T_TITLE' => 'filterByTitle', - 'T_STATUS' => 'filterByStatusName', - 'T_DESCRIPTION' => 'filterByDescription', - 'T_CATEGORY' => 'filterByCategoryName', - 'T_PROJECT' => 'filterByProjectName', - 'T_COLUMN' => 'filterByColumnName', - 'T_REFERENCE' => 'filterByReference', - 'T_SWIMLANE' => 'filterBySwimlaneName', - 'T_LINK' => 'filterByLinkName', - ); - - /** - * Query - * - * @access public - * @var \PicoDb\Table - */ - public $query; - - /** - * Apply filters according to the search input - * - * @access public - * @param string $input - * @return TaskFilter - */ - public function search($input) - { - $tree = $this->lexer->map($this->lexer->tokenize($input)); - $this->query = $this->taskFinder->getExtendedQuery(); - - if (empty($tree)) { - $this->filterByTitle($input); - } - - foreach ($tree as $filter => $value) { - $method = $this->filters[$filter]; - $this->$method($value); - } - - return $this; - } - - /** - * Create a new query - * - * @access public - * @return TaskFilter - */ - public function create() - { - $this->query = $this->db->table(Task::TABLE); - $this->query->left(User::TABLE, 'ua', 'id', Task::TABLE, 'owner_id'); - $this->query->left(User::TABLE, 'uc', 'id', Task::TABLE, 'creator_id'); - - $this->query->columns( - Task::TABLE.'.*', - 'ua.email AS assignee_email', - 'ua.name AS assignee_name', - 'ua.username AS assignee_username', - 'uc.email AS creator_email', - 'uc.username AS creator_username' - ); - - return $this; - } - - /** - * Create a new subtask query - * - * @access public - * @return \PicoDb\Table - */ - public function createSubtaskQuery() - { - return $this->db->table(Subtask::TABLE) - ->columns( - Subtask::TABLE.'.user_id', - Subtask::TABLE.'.task_id', - User::TABLE.'.name', - User::TABLE.'.username' - ) - ->join(User::TABLE, 'id', 'user_id', Subtask::TABLE) - ->neq(Subtask::TABLE.'.status', Subtask::STATUS_DONE); - } - - /** - * Create a new link query - * - * @access public - * @return \PicoDb\Table - */ - public function createLinkQuery() - { - return $this->db->table(TaskLink::TABLE) - ->columns( - TaskLink::TABLE.'.task_id', - Link::TABLE.'.label' - ) - ->join(Link::TABLE, 'id', 'link_id', TaskLink::TABLE); - } - - /** - * Clone the filter - * - * @access public - * @return TaskFilter - */ - public function copy() - { - $filter = new static($this->container); - $filter->query = clone($this->query); - $filter->query->condition = clone($this->query->condition); - return $filter; - } - - /** - * Exclude a list of task_id - * - * @access public - * @param integer[] $task_ids - * @return TaskFilter - */ - public function excludeTasks(array $task_ids) - { - $this->query->notin(Task::TABLE.'.id', $task_ids); - return $this; - } - - /** - * Filter by id - * - * @access public - * @param integer $task_id - * @return TaskFilter - */ - public function filterById($task_id) - { - if ($task_id > 0) { - $this->query->eq(Task::TABLE.'.id', $task_id); - } - - return $this; - } - - /** - * Filter by reference - * - * @access public - * @param string $reference - * @return TaskFilter - */ - public function filterByReference($reference) - { - if (! empty($reference)) { - $this->query->eq(Task::TABLE.'.reference', $reference); - } - - return $this; - } - - /** - * Filter by title - * - * @access public - * @param string $title - * @return TaskFilter - */ - public function filterByDescription($title) - { - $this->query->ilike(Task::TABLE.'.description', '%'.$title.'%'); - return $this; - } - - /** - * Filter by title or id if the string is like #123 or an integer - * - * @access public - * @param string $title - * @return TaskFilter - */ - public function filterByTitle($title) - { - if (ctype_digit($title) || (strlen($title) > 1 && $title{0} === '#' && ctype_digit(substr($title, 1)))) { - $this->query->beginOr(); - $this->query->eq(Task::TABLE.'.id', str_replace('#', '', $title)); - $this->query->ilike(Task::TABLE.'.title', '%'.$title.'%'); - $this->query->closeOr(); - } else { - $this->query->ilike(Task::TABLE.'.title', '%'.$title.'%'); - } - - return $this; - } - - /** - * Filter by a list of project id - * - * @access public - * @param array $project_ids - * @return TaskFilter - */ - public function filterByProjects(array $project_ids) - { - $this->query->in(Task::TABLE.'.project_id', $project_ids); - return $this; - } - - /** - * Filter by project id - * - * @access public - * @param integer $project_id - * @return TaskFilter - */ - public function filterByProject($project_id) - { - if ($project_id > 0) { - $this->query->eq(Task::TABLE.'.project_id', $project_id); - } - - return $this; - } - - /** - * Filter by project name - * - * @access public - * @param array $values List of project name - * @return TaskFilter - */ - public function filterByProjectName(array $values) - { - $this->query->beginOr(); - - foreach ($values as $project) { - if (ctype_digit($project)) { - $this->query->eq(Task::TABLE.'.project_id', $project); - } else { - $this->query->ilike(Project::TABLE.'.name', $project); - } - } - - $this->query->closeOr(); - } - - /** - * Filter by swimlane name - * - * @access public - * @param array $values List of swimlane name - * @return TaskFilter - */ - public function filterBySwimlaneName(array $values) - { - $this->query->beginOr(); - - foreach ($values as $swimlane) { - if ($swimlane === 'default') { - $this->query->eq(Task::TABLE.'.swimlane_id', 0); - } else { - $this->query->ilike(Swimlane::TABLE.'.name', $swimlane); - $this->query->addCondition(Task::TABLE.'.swimlane_id=0 AND '.Project::TABLE.'.default_swimlane '.$this->db->getDriver()->getOperator('ILIKE')." '$swimlane'"); - } - } - - $this->query->closeOr(); - } - - /** - * Filter by category id - * - * @access public - * @param integer $category_id - * @return TaskFilter - */ - public function filterByCategory($category_id) - { - if ($category_id >= 0) { - $this->query->eq(Task::TABLE.'.category_id', $category_id); - } - - return $this; - } - - /** - * Filter by category - * - * @access public - * @param array $values List of assignees - * @return TaskFilter - */ - public function filterByCategoryName(array $values) - { - $this->query->beginOr(); - - foreach ($values as $category) { - if ($category === 'none') { - $this->query->eq(Task::TABLE.'.category_id', 0); - } else { - $this->query->eq(Category::TABLE.'.name', $category); - } - } - - $this->query->closeOr(); - } - - /** - * Filter by assignee - * - * @access public - * @param integer $owner_id - * @return TaskFilter - */ - public function filterByOwner($owner_id) - { - if ($owner_id >= 0) { - $this->query->eq(Task::TABLE.'.owner_id', $owner_id); - } - - return $this; - } - - /** - * Filter by assignee names - * - * @access public - * @param array $values List of assignees - * @return TaskFilter - */ - public function filterByAssignee(array $values) - { - $this->query->beginOr(); - - foreach ($values as $assignee) { - switch ($assignee) { - case 'me': - $this->query->eq(Task::TABLE.'.owner_id', $this->userSession->getId()); - break; - case 'nobody': - $this->query->eq(Task::TABLE.'.owner_id', 0); - break; - default: - $this->query->ilike(User::TABLE.'.username', '%'.$assignee.'%'); - $this->query->ilike(User::TABLE.'.name', '%'.$assignee.'%'); - } - } - - $this->filterBySubtaskAssignee($values); - - $this->query->closeOr(); - - return $this; - } - - /** - * Filter by subtask assignee names - * - * @access public - * @param array $values List of assignees - * @return TaskFilter - */ - public function filterBySubtaskAssignee(array $values) - { - $subtaskQuery = $this->createSubtaskQuery(); - $subtaskQuery->beginOr(); - - foreach ($values as $assignee) { - if ($assignee === 'me') { - $subtaskQuery->eq(Subtask::TABLE.'.user_id', $this->userSession->getId()); - } else { - $subtaskQuery->ilike(User::TABLE.'.username', '%'.$assignee.'%'); - $subtaskQuery->ilike(User::TABLE.'.name', '%'.$assignee.'%'); - } - } - - $subtaskQuery->closeOr(); - - $this->query->in(Task::TABLE.'.id', $subtaskQuery->findAllByColumn('task_id')); - - return $this; - } - - /** - * Filter by color - * - * @access public - * @param string $color_id - * @return TaskFilter - */ - public function filterByColor($color_id) - { - if ($color_id !== '') { - $this->query->eq(Task::TABLE.'.color_id', $color_id); - } - - return $this; - } - - /** - * Filter by colors - * - * @access public - * @param array $colors - * @return TaskFilter - */ - public function filterByColors(array $colors) - { - $this->query->beginOr(); - - foreach ($colors as $color) { - $this->filterByColor($this->color->find($color)); - } - - $this->query->closeOr(); - - return $this; - } - - /** - * Filter by column - * - * @access public - * @param integer $column_id - * @return TaskFilter - */ - public function filterByColumn($column_id) - { - if ($column_id >= 0) { - $this->query->eq(Task::TABLE.'.column_id', $column_id); - } - - return $this; - } - - /** - * Filter by column name - * - * @access public - * @param array $values List of column name - * @return TaskFilter - */ - public function filterByColumnName(array $values) - { - $this->query->beginOr(); - - foreach ($values as $project) { - $this->query->ilike(Column::TABLE.'.title', $project); - } - - $this->query->closeOr(); - } - - /** - * Filter by swimlane - * - * @access public - * @param integer $swimlane_id - * @return TaskFilter - */ - public function filterBySwimlane($swimlane_id) - { - if ($swimlane_id >= 0) { - $this->query->eq(Task::TABLE.'.swimlane_id', $swimlane_id); - } - - return $this; - } - - /** - * Filter by status name - * - * @access public - * @param string $status - * @return TaskFilter - */ - public function filterByStatusName($status) - { - if ($status === 'open' || $status === 'closed') { - $this->filterByStatus($status === 'open' ? Task::STATUS_OPEN : Task::STATUS_CLOSED); - } - - return $this; - } - - /** - * Filter by status - * - * @access public - * @param integer $is_active - * @return TaskFilter - */ - public function filterByStatus($is_active) - { - if ($is_active >= 0) { - $this->query->eq(Task::TABLE.'.is_active', $is_active); - } - - return $this; - } - - /** - * Filter by link - * - * @access public - * @param array $values List of links - * @return TaskFilter - */ - public function filterByLinkName(array $values) - { - $this->query->beginOr(); - - $link_query = $this->createLinkQuery()->in(Link::TABLE.'.label', $values); - $matching_task_ids = $link_query->findAllByColumn('task_id'); - if (empty($matching_task_ids)) { - $this->query->eq(Task::TABLE.'.id', 0); - } else { - $this->query->in(Task::TABLE.'.id', $matching_task_ids); - } - - $this->query->closeOr(); - - return $this; - } - - /** - * Filter by due date - * - * @access public - * @param string $date ISO8601 date format - * @return TaskFilter - */ - public function filterByDueDate($date) - { - $this->query->neq(Task::TABLE.'.date_due', 0); - $this->query->notNull(Task::TABLE.'.date_due'); - return $this->filterWithOperator(Task::TABLE.'.date_due', $date, true); - } - - /** - * Filter by due date (range) - * - * @access public - * @param string $start - * @param string $end - * @return TaskFilter - */ - public function filterByDueDateRange($start, $end) - { - $this->query->gte('date_due', $this->dateParser->getTimestampFromIsoFormat($start)); - $this->query->lte('date_due', $this->dateParser->getTimestampFromIsoFormat($end)); - - return $this; - } - - /** - * Filter by start date (range) - * - * @access public - * @param string $start - * @param string $end - * @return TaskFilter - */ - public function filterByStartDateRange($start, $end) - { - $this->query->addCondition($this->getCalendarCondition( - $this->dateParser->getTimestampFromIsoFormat($start), - $this->dateParser->getTimestampFromIsoFormat($end), - 'date_started', - 'date_completed' - )); - - return $this; - } - - /** - * Filter by creation date - * - * @access public - * @param string $date ISO8601 date format - * @return TaskFilter - */ - public function filterByCreationDate($date) - { - if ($date === 'recently') { - return $this->filterRecentlyDate(Task::TABLE.'.date_creation'); - } - - return $this->filterWithOperator(Task::TABLE.'.date_creation', $date, true); - } - - /** - * Filter by creation date - * - * @access public - * @param string $start - * @param string $end - * @return TaskFilter - */ - public function filterByCreationDateRange($start, $end) - { - $this->query->addCondition($this->getCalendarCondition( - $this->dateParser->getTimestampFromIsoFormat($start), - $this->dateParser->getTimestampFromIsoFormat($end), - 'date_creation', - 'date_completed' - )); - - return $this; - } - - /** - * Filter by modification date - * - * @access public - * @param string $date ISO8601 date format - * @return TaskFilter - */ - public function filterByModificationDate($date) - { - if ($date === 'recently') { - return $this->filterRecentlyDate(Task::TABLE.'.date_modification'); - } - - return $this->filterWithOperator(Task::TABLE.'.date_modification', $date, true); - } - - /** - * Get all results of the filter - * - * @access public - * @return array - */ - public function findAll() - { - return $this->query->asc(Task::TABLE.'.id')->findAll(); - } - - /** - * Get the PicoDb query - * - * @access public - * @return \PicoDb\Table - */ - public function getQuery() - { - return $this->query; - } - - /** - * Get swimlanes and tasks to display the board - * - * @access public - * @return array - */ - public function getBoard($project_id) - { - $tasks = $this->filterByProject($project_id)->query->asc(Task::TABLE.'.position')->findAll(); - - return $this->board->getBoard($project_id, function ($project_id, $column_id, $swimlane_id) use ($tasks) { - return array_filter($tasks, function (array $task) use ($column_id, $swimlane_id) { - return $task['column_id'] == $column_id && $task['swimlane_id'] == $swimlane_id; - }); - }); - } - - /** - * Filter with an operator - * - * @access public - * @param string $field - * @param string $value - * @param boolean $is_date - * @return TaskFilter - */ - private function filterWithOperator($field, $value, $is_date) - { - $operators = array( - '<=' => 'lte', - '>=' => 'gte', - '<' => 'lt', - '>' => 'gt', - ); - - foreach ($operators as $operator => $method) { - if (strpos($value, $operator) === 0) { - $value = substr($value, strlen($operator)); - $this->query->$method($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value); - return $this; - } - } - - if ($is_date) { - $timestamp = $this->dateParser->getTimestampFromIsoFormat($value); - $this->query->gte($field, $timestamp); - $this->query->lte($field, $timestamp + 86399); - } else { - $this->query->eq($field, $value); - } - - return $this; - } - - /** - * Use the board_highlight_period for the "recently" keyword - * - * @access private - * @param string $field - * @return TaskFilter - */ - private function filterRecentlyDate($field) - { - $duration = $this->config->get('board_highlight_period', 0); - - if ($duration > 0) { - $this->query->gte($field, time() - $duration); - } - - return $this; - } -} diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index d67372cc..d406b794 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -128,6 +128,7 @@ class TaskFinder extends Base User::TABLE.'.username AS assignee_username', User::TABLE.'.name AS assignee_name', User::TABLE.'.email AS assignee_email', + User::TABLE.'.avatar_path AS assignee_avatar_path', Category::TABLE.'.name AS category_name', Category::TABLE.'.description AS category_description', Column::TABLE.'.title AS column_name', @@ -137,6 +138,7 @@ class TaskFinder extends Base Project::TABLE.'.name AS project_name' ) ->join(User::TABLE, 'id', 'owner_id', Task::TABLE) + ->left(User::TABLE, 'uc', 'id', Task::TABLE, 'creator_id') ->join(Category::TABLE, 'id', 'category_id', Task::TABLE) ->join(Column::TABLE, 'id', 'column_id', Task::TABLE) ->join(Swimlane::TABLE, 'id', 'swimlane_id', Task::TABLE) @@ -362,6 +364,27 @@ class TaskFinder extends Base } /** + * Get iCal query + * + * @access public + * @return \PicoDb\Table + */ + public function getICalQuery() + { + return $this->db->table(Task::TABLE) + ->left(User::TABLE, 'ua', 'id', Task::TABLE, 'owner_id') + ->left(User::TABLE, 'uc', 'id', Task::TABLE, 'creator_id') + ->columns( + Task::TABLE.'.*', + 'ua.email AS assignee_email', + 'ua.name AS assignee_name', + 'ua.username AS assignee_username', + 'uc.email AS creator_email', + 'uc.username AS creator_username' + ); + } + + /** * Count all tasks for a given project and status * * @access public diff --git a/app/Model/User.php b/app/Model/User.php index 0e11422b..57993002 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -283,12 +283,7 @@ class User extends Base { $this->prepare($values); $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); - - // If the user is connected refresh his session - if ($this->userSession->getId() == $values['id']) { - $this->userSession->initialize($this->getById($this->userSession->getId())); - } - + $this->userSession->refresh($values['id']); return $result; } @@ -325,6 +320,8 @@ class User extends Base */ public function remove($user_id) { + $this->avatarFile->remove($user_id); + return $this->db->transaction(function (Database $db) use ($user_id) { // All assigned tasks are now unassigned (no foreign key) diff --git a/app/Model/UserFilter.php b/app/Model/UserFilter.php deleted file mode 100644 index ff546e96..00000000 --- a/app/Model/UserFilter.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -namespace Kanboard\Model; - -/** - * User Filter - * - * @package model - * @author Frederic Guillot - */ -class UserFilter extends Base -{ - /** - * Search query - * - * @access private - * @var string - */ - private $input; - - /** - * Query - * - * @access protected - * @var \PicoDb\Table - */ - protected $query; - - /** - * Initialize filter - * - * @access public - * @param string $input - * @return UserFilter - */ - public function create($input) - { - $this->query = $this->db->table(User::TABLE); - $this->input = $input; - return $this; - } - - /** - * Filter users by name or username - * - * @access public - * @return UserFilter - */ - public function filterByUsernameOrByName() - { - $this->query->beginOr() - ->ilike('username', '%'.$this->input.'%') - ->ilike('name', '%'.$this->input.'%') - ->closeOr(); - - return $this; - } - - /** - * Get all results of the filter - * - * @access public - * @return array - */ - public function findAll() - { - return $this->query->findAll(); - } - - /** - * Get the PicoDb query - * - * @access public - * @return \PicoDb\Table - */ - public function getQuery() - { - return $this->query; - } -} diff --git a/app/Model/UserNotification.php b/app/Model/UserNotification.php index e8a967ac..7795da2e 100644 --- a/app/Model/UserNotification.php +++ b/app/Model/UserNotification.php @@ -117,23 +117,20 @@ class UserNotification extends Base */ public function saveSettings($user_id, array $values) { - $this->db->startTransaction(); + $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); - if (isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1) { + if (! empty($types)) { $this->enableNotification($user_id); - - $filter = empty($values['notifications_filter']) ? UserNotificationFilter::FILTER_BOTH : $values['notifications_filter']; - $projects = empty($values['notification_projects']) ? array() : array_keys($values['notification_projects']); - $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); - - $this->userNotificationFilter->saveFilter($user_id, $filter); - $this->userNotificationFilter->saveSelectedProjects($user_id, $projects); - $this->userNotificationType->saveSelectedTypes($user_id, $types); } else { $this->disableNotification($user_id); } - $this->db->closeTransaction(); + $filter = empty($values['notifications_filter']) ? UserNotificationFilter::FILTER_BOTH : $values['notifications_filter']; + $project_ids = empty($values['notification_projects']) ? array() : array_keys($values['notification_projects']); + + $this->userNotificationFilter->saveFilter($user_id, $filter); + $this->userNotificationFilter->saveSelectedProjects($user_id, $project_ids); + $this->userNotificationType->saveSelectedTypes($user_id, $types); } /** diff --git a/app/Model/UserNotificationFilter.php b/app/Model/UserNotificationFilter.php index d4afd278..780ddfc7 100644 --- a/app/Model/UserNotificationFilter.php +++ b/app/Model/UserNotificationFilter.php @@ -61,10 +61,11 @@ class UserNotificationFilter extends Base * @access public * @param integer $user_id * @param string $filter + * @return boolean */ public function saveFilter($user_id, $filter) { - $this->db->table(User::TABLE)->eq('id', $user_id)->update(array( + return $this->db->table(User::TABLE)->eq('id', $user_id)->update(array( 'notifications_filter' => $filter, )); } @@ -87,17 +88,21 @@ class UserNotificationFilter extends Base * @access public * @param integer $user_id * @param integer[] $project_ids + * @return boolean */ public function saveSelectedProjects($user_id, array $project_ids) { + $results = array(); $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->remove(); foreach ($project_ids as $project_id) { - $this->db->table(self::PROJECT_TABLE)->insert(array( + $results[] = $this->db->table(self::PROJECT_TABLE)->insert(array( 'user_id' => $user_id, 'project_id' => $project_id, )); } + + return !in_array(false, $results, true); } /** |