diff options
Diffstat (limited to 'plugins/Group_assign/Model')
-rw-r--r-- | plugins/Group_assign/Model/GroupAssignCalendarModel.php | 89 | ||||
-rw-r--r-- | plugins/Group_assign/Model/GroupAssignTaskDuplicationModel.php | 175 | ||||
-rw-r--r-- | plugins/Group_assign/Model/GroupColorExtension.php | 28 | ||||
-rw-r--r-- | plugins/Group_assign/Model/MultiselectMemberModel.php | 169 | ||||
-rw-r--r-- | plugins/Group_assign/Model/MultiselectModel.php | 143 | ||||
-rw-r--r-- | plugins/Group_assign/Model/NewMetaMagikSubquery.php | 28 | ||||
-rw-r--r-- | plugins/Group_assign/Model/NewTaskFinderModel.php | 491 | ||||
-rw-r--r-- | plugins/Group_assign/Model/NewUserNotificationFilterModel.php | 218 | ||||
-rw-r--r-- | plugins/Group_assign/Model/OldMetaMagikSubquery.php | 28 | ||||
-rw-r--r-- | plugins/Group_assign/Model/OldTaskFinderModel.php | 489 | ||||
-rw-r--r-- | plugins/Group_assign/Model/TaskProjectDuplicationModel.php | 94 | ||||
-rw-r--r-- | plugins/Group_assign/Model/TaskProjectMoveModel.php | 96 | ||||
-rw-r--r-- | plugins/Group_assign/Model/TaskRecurrenceModel.php | 154 |
13 files changed, 2202 insertions, 0 deletions
diff --git a/plugins/Group_assign/Model/GroupAssignCalendarModel.php b/plugins/Group_assign/Model/GroupAssignCalendarModel.php new file mode 100644 index 00000000..5b45ca09 --- /dev/null +++ b/plugins/Group_assign/Model/GroupAssignCalendarModel.php @@ -0,0 +1,89 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use DateTime; +use Kanboard\Model\GroupMemberModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectMemberModel; +use Kanboard\Model\TimezoneModel; +use Kanboard\Model\TaskFinderModel; +use Kanboard\Model\ColorModel; +use Kanboard\Core\Base; + +/** + * Group_assign Calendar Model + * + * @package Kanboard\Plugin\Group_assign + * @author Craig Crosby + */ +class GroupAssignCalendarModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'tasks'; + /** + * Get query to fetch all users + * + * @access public + * @param integer $group_id + * @return \PicoDb\Table + */ + public function getUserCalendarEvents($user_id, $start, $end) + { + $getMS_Ids = $this->db->table(MultiselectMemberModel::TABLE) + ->eq('user_id', $user_id) + ->findAllByColumn('group_id'); + + $getGr_Ids = $this->db->table(GroupMemberModel::TABLE) + ->eq('user_id', $user_id) + ->findAllByColumn('group_id'); + + $tasks = $this->db->table(self::TABLE) + ->beginOr() + ->eq('owner_id', $user_id) + ->in('owner_gp', $getGr_Ids) + ->in('owner_ms', $getMS_Ids) + ->closeOr() + ->gte('date_due', strtotime($start)) + ->lte('date_due', strtotime($end)) + ->neq('is_active', 0); + + $tasks = $tasks->findAll(); + + $events = array(); + + foreach ($tasks as $task) { + + $startDate = new DateTime(); + $startDate->setTimestamp($task['date_started']); + + $endDate = new DateTime(); + $endDate->setTimestamp($task['date_due']); + + if ($startDate->getTimestamp() == 0) { $startDate = $endDate; } + + $allDay = $startDate == $endDate && $endDate->format('Hi') == '0000'; + $format = $allDay ? 'Y-m-d' : 'Y-m-d\TH:i:s'; + + $events[] = array( + 'timezoneParam' => $this->timezoneModel->getCurrentTimezone(), + 'id' => $task['id'], + 'title' => t('#%d', $task['id']).' '.$task['title'], + 'backgroundColor' => $this->colorModel->getBackgroundColor('dark_grey'), + 'borderColor' => $this->colorModel->getBorderColor($task['color_id']), + 'textColor' => 'white', + 'url' => $this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), + 'start' => $startDate->format($format), + 'end' => $endDate->format($format), + 'editable' => $allDay, + 'allday' => $allDay, + ); + } + + return $events; + } + +} diff --git a/plugins/Group_assign/Model/GroupAssignTaskDuplicationModel.php b/plugins/Group_assign/Model/GroupAssignTaskDuplicationModel.php new file mode 100644 index 00000000..179f6630 --- /dev/null +++ b/plugins/Group_assign/Model/GroupAssignTaskDuplicationModel.php @@ -0,0 +1,175 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Plugin\Group_assign\Model\MultiselectModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectMemberModel; +use Kanboard\Model\TagDuplicationModel; +use Kanboard\Model\ProjectPermissionModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Model\CategoryModel; +use Kanboard\Model\SwimlaneModel; +use Kanboard\Model\ColumnModel; +use Kanboard\Model\ProjectTaskPriorityModel; +use Kanboard\Model\TaskFinderModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\SubtaskModel; +use Kanboard\Core\Base; + +/** + * Task Duplication + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class GroupAssignTaskDuplicationModel extends Base +{ + /** + * Fields to copy when duplicating a task + * + * @access protected + * @var string[] + */ + protected $fieldsToDuplicate = array( + 'title', + 'description', + 'date_due', + 'color_id', + 'project_id', + 'column_id', + 'owner_id', + 'score', + 'priority', + 'category_id', + 'time_estimated', + 'swimlane_id', + 'recurrence_status', + 'recurrence_trigger', + 'recurrence_factor', + 'recurrence_timeframe', + 'recurrence_basedate', + 'external_provider', + 'external_uri', + 'reference', + 'owner_ms', + 'owner_gp', + ); + + /** + * Duplicate a task to the same project + * + * @access public + * @param integer $task_id Task id + * @return boolean|integer Duplicated task id + */ + public function duplicate($task_id) + { + $values = $this->copyFields($task_id); + $values['title'] = t('[DUPLICATE]').' '.$values['title']; + + $new_task_id = $this->save($task_id, $values); + + if ($new_task_id !== false) { + $this->tagDuplicationModel->duplicateTaskTags($task_id, $new_task_id); + $this->taskLinkModel->create($new_task_id, $task_id, 4); + } + + return $new_task_id; + } + + /** + * Check if the assignee and the category are available in the destination project + * + * @access public + * @param array $values + * @return array + */ + public function checkDestinationProjectValues(array &$values) + { + // Check if the assigned user is allowed for the destination project + if ($values['owner_id'] > 0 && ! $this->projectPermissionModel->isUserAllowed($values['project_id'], $values['owner_id'])) { + $values['owner_id'] = 0; + } + + // Check if the category exists for the destination project + if ($values['category_id'] > 0) { + $values['category_id'] = $this->categoryModel->getIdByName( + $values['project_id'], + $this->categoryModel->getNameById($values['category_id']) + ); + } + + // Check if the swimlane exists for the destination project + $values['swimlane_id'] = $this->swimlaneModel->getIdByName( + $values['project_id'], + $this->swimlaneModel->getNameById($values['swimlane_id']) + ); + + if ($values['swimlane_id'] == 0) { + $values['swimlane_id'] = $this->swimlaneModel->getFirstActiveSwimlaneId($values['project_id']); + } + + // Check if the column exists for the destination project + if ($values['column_id'] > 0) { + $values['column_id'] = $this->columnModel->getColumnIdByTitle( + $values['project_id'], + $this->columnModel->getColumnTitleById($values['column_id']) + ); + + $values['column_id'] = $values['column_id'] ?: $this->columnModel->getFirstColumnId($values['project_id']); + } + + // Check if priority exists for destination project + $values['priority'] = $this->projectTaskPriorityModel->getPriorityForProject( + $values['project_id'], + empty($values['priority']) ? 0 : $values['priority'] + ); + + return $values; + } + + /** + * Duplicate fields for the new task + * + * @access protected + * @param integer $task_id Task id + * @return array + */ + protected function copyFields($task_id) + { + $task = $this->taskFinderModel->getById($task_id); + $values = array(); + + foreach ($this->fieldsToDuplicate as $field) { + $values[$field] = $task[$field]; + } + + $ms_id = $this->multiselectModel->create(); + $users_in_ms = $this->multiselectMemberModel->getMembers($values['owner_ms']); + $values['owner_ms'] = $ms_id; + foreach ($users_in_ms as $user) { + $this->multiselectMemberModel->addUser($ms_id, $user['id']); + } + + return $values; + } + + /** + * Create the new task and duplicate subtasks + * + * @access protected + * @param integer $task_id Task id + * @param array $values Form values + * @return boolean|integer + */ + protected function save($task_id, array $values) + { + $new_task_id = $this->taskCreationModel->create($values); + + if ($new_task_id !== false) { + $this->subtaskModel->duplicate($task_id, $new_task_id); + } + + return $new_task_id; + } +} diff --git a/plugins/Group_assign/Model/GroupColorExtension.php b/plugins/Group_assign/Model/GroupColorExtension.php new file mode 100644 index 00000000..e8e19850 --- /dev/null +++ b/plugins/Group_assign/Model/GroupColorExtension.php @@ -0,0 +1,28 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Model\GroupModel; + +class GroupColorExtension extends GroupModel +{ + + public function getGroupColor($str) { + $code = dechex(crc32($str)); + $code = substr($code, 0, 6); + return $code; + } + + public function getFontColor($hex) { + // returns brightness value from 0 to 255 + // strip off any leading # + $hex = str_replace('#', '', $hex); + + $c_r = hexdec(substr($hex, 0, 2)); + $c_g = hexdec(substr($hex, 2, 2)); + $c_b = hexdec(substr($hex, 4, 2)); + + $brightness = (($c_r * 299) + ($c_g * 587) + ($c_b * 114)) / 1000; + if ($brightness > 130) { return 'black'; } else { return 'white'; } + } +} diff --git a/plugins/Group_assign/Model/MultiselectMemberModel.php b/plugins/Group_assign/Model/MultiselectMemberModel.php new file mode 100644 index 00000000..ce0f3ace --- /dev/null +++ b/plugins/Group_assign/Model/MultiselectMemberModel.php @@ -0,0 +1,169 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Plugin\Group_assign\Model\MultiselectModel; +use Kanboard\Model\UserModel; +use Kanboard\Model\TaskModel; +use Kanboard\Core\Queue\QueueManager; +use Kanboard\Core\Base; + +/** + * Multiselect Member Model + * + * @package Kanboard\Plugin\Group_assign + * @author Craig Crosby + */ +class MultiselectMemberModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'multiselect_has_users'; + + /** + * Get query to fetch all users + * + * @access public + * @param integer $group_id + * @return \PicoDb\Table + */ + public function getQuery($group_id) + { + return $this->db->table(self::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq('group_id', $group_id); + } + + /** + * Get all users + * + * @access public + * @param integer $group_id + * @return array + */ + public function getMembers($group_id) + { + return $this->getQuery($group_id)->findAll(); + } + + /** + * Get all not members + * + * @access public + * @param integer $group_id + * @return array + */ + public function getNotMembers($group_id) + { + $subquery = $this->db->table(self::TABLE) + ->columns('user_id') + ->eq('group_id', $group_id); + + return $this->db->table(UserModel::TABLE) + ->notInSubquery('id', $subquery) + ->eq('is_active', 1) + ->findAll(); + } + + /** + * Add user to a group + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function addUser($group_id, $user_id) + { + return $this->db->table(self::TABLE)->insert(array( + 'group_id' => $group_id, + 'user_id' => $user_id, + )); + } + + /** + * Remove user from a group + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function removeUser($group_id, $user_id) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('user_id', $user_id) + ->remove(); + } + + /** + * Remove all users from a group + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function removeAllUsers($group_id) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->remove(); + } + + /** + * Check if a user is member + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function isMember($group_id, $user_id) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('user_id', $user_id) + ->exists(); + } + + /** + * Get all groups for a given user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getGroups($user_id) + { + return $this->db->table(self::TABLE) + ->columns(MultiselectModel::TABLE.'.id', MultiselectModel::TABLE.'.external_id') + ->join(MultiselectModel::TABLE, 'id', 'group_id') + ->eq(self::TABLE.'.user_id', $user_id) + ->asc(MultiselectModel::TABLE.'.id') + ->findAll(); + } + + /** + * Fire Assignee Change + * + * @access protected + * @param array $task + * @param array $changes + */ + public function assigneeChanged(array $task, array $changes) + { + $events = array(); + $events[] = TaskModel::EVENT_ASSIGNEE_CHANGE; + + if (! empty($events)) { + $this->queueManager->push($this->taskEventJob + ->withParams($task['id'], $events, $changes, array(), $task) + ); + } + } +} diff --git a/plugins/Group_assign/Model/MultiselectModel.php b/plugins/Group_assign/Model/MultiselectModel.php new file mode 100644 index 00000000..36f1a9d1 --- /dev/null +++ b/plugins/Group_assign/Model/MultiselectModel.php @@ -0,0 +1,143 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Plugin\Group_assign\Model\MultiselectMemberModel; +use Kanboard\Core\Base; + +/** + * Multiselect Model + * + * @package Kanboard\Plugin\Group_assign + * @author Craig Crosby + */ +class MultiselectModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'multiselect'; + + /** + * Get query to fetch all groups + * + * @access public + * @return \PicoDb\Table + */ + public function getQuery() + { + return $this->db->table(self::TABLE) + ->columns('id', 'external_id') + ->subquery('SELECT COUNT(*) FROM '.MultiselectMemberModel::TABLE.' WHERE group_id='.self::TABLE.'.id', 'nb_users'); + } + + /** + * Get a specific group by id + * + * @access public + * @param integer $group_id + * @return array + */ + public function getById($group_id) + { + return $this->db->table(self::TABLE)->eq('id', $group_id)->findOne(); + } + + /** + * Get a specific group by externalID + * + * @access public + * @param string $external_id + * @return array + */ + public function getByExternalId($external_id) + { + return $this->db->table(self::TABLE)->eq('external_id', $external_id)->findOne(); + } + + /** + * Get specific groups by externalIDs + * + * @access public + * @param string[] $external_ids + * @return array + */ + public function getByExternalIds(array $external_ids) + { + if (empty($external_ids)) { + return []; + } + + return $this->db->table(self::TABLE)->in('external_id', $external_ids)->findAll(); + } + + /** + * Get all groups + * + * @access public + * @return array + */ + public function getAll() + { + return $this->getQuery()->asc('id')->findAll(); + } + + /** + * Remove a group + * + * @access public + * @param integer $group_id + * @return boolean + */ + public function remove($group_id) + { + return $this->db->table(self::TABLE)->eq('id', $group_id)->remove(); + } + + /** + * Create a new group + * + * @access public + * @param string $external_id + * @return integer|boolean + */ + public function create($external_id = '') + { + return $this->db->table(self::TABLE)->persist(array( + 'external_id' => $external_id, + )); + } + + /** + * Update existing group + * + * @access public + * @param array $values + * @return boolean + */ + public function update(array $values) + { + return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); + } + + /** + * Get groupId from externalGroupId and create the group if not found + * + * @access public + * @param string $name + * @param string $external_id + * @return bool|integer + */ + public function getOrCreateExternalGroupId($name, $external_id) + { + $group_id = $this->db->table(self::TABLE)->eq('external_id', $external_id)->findOneColumn('id'); + + if (empty($group_id)) { + $group_id = $this->create($external_id); + } + + return $group_id; + } +} diff --git a/plugins/Group_assign/Model/NewMetaMagikSubquery.php b/plugins/Group_assign/Model/NewMetaMagikSubquery.php new file mode 100644 index 00000000..4870120e --- /dev/null +++ b/plugins/Group_assign/Model/NewMetaMagikSubquery.php @@ -0,0 +1,28 @@ +<?php
+
+namespace Kanboard\Plugin\Group_assign\Model;
+
+use Kanboard\Plugin\Group_assign\Model\NewTaskFinderModel;
+
+/**
+ * New Task Finder model
+ * Extends Group_assign Model
+ *
+ * @package Kanboard\Plugin\Group_assign\Model
+ */
+class NewMetaMagikSubQuery extends NewTaskFinderModel
+{
+ const METADATA_TABLE = 'task_has_metadata';
+ /**
+ * Extended query
+ *
+ * @access public
+ * @return \PicoDb\Table
+ */
+ public function getExtendedQuery()
+ {
+ // add subquery to original Model, changing only what we want
+ return parent::getExtendedQuery()
+ ->subquery('(SELECT COUNT(*) FROM '.self::METADATA_TABLE.' WHERE task_id=tasks.id)', 'nb_metadata');
+ }
+}
\ No newline at end of file diff --git a/plugins/Group_assign/Model/NewTaskFinderModel.php b/plugins/Group_assign/Model/NewTaskFinderModel.php new file mode 100644 index 00000000..b8b10916 --- /dev/null +++ b/plugins/Group_assign/Model/NewTaskFinderModel.php @@ -0,0 +1,491 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Model\ActionParameterModel; +use Kanboard\Model\AvatarFileModel; +use Kanboard\Model\BoardModel; +use Kanboard\Model\CategoryModel; +use Kanboard\Model\ColorModel; +use Kanboard\Model\ColumnModel; +use Kanboard\Model\ColumnMoveRestrictionModel; +use Kanboard\Model\CommentModel; +use Kanboard\Model\ConfigModel; +use Kanboard\Model\CurrencyModel; +use Kanboard\Model\CustomFilterModel; +use Kanboard\Model\FileModel; +use Kanboard\Model\GroupMemberModel; +use Kanboard\Model\GroupModel; +use Kanboard\Model\LanguageModel; +use Kanboard\Model\LastLoginModel; +use Kanboard\Model\LinkModel; +use Kanboard\Model\MetadataModel; +use Kanboard\Model\NotificationModel; +use Kanboard\Model\NotificationTypeModel; +use Kanboard\Model\PasswordResetModel; +use Kanboard\Model\ProjectActivityModel; +use Kanboard\Model\ProjectDailyColumnStatsModel; +use Kanboard\Model\ProjectDailyStatsModel; +use Kanboard\Model\ProjectDuplicationModel; +use Kanboard\Model\ProjectFileModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Model\ProjectMetadataModel; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\ProjectNotificationModel; +use Kanboard\Model\ProjectNotificationTypeModel; +use Kanboard\Model\ProjectPermissionModel; +use Kanboard\Model\ProjectRoleModel; +use Kanboard\Model\ProjectRoleRestrictionModel; +use Kanboard\Model\ProjectTaskDuplicationModel; +use Kanboard\Model\ProjectTaskPriorityModel; +use Kanboard\Model\ProjectUserRoleModel; +use Kanboard\Model\RememberMeSessionModel; +use Kanboard\Model\SettingModel; +use Kanboard\Model\SubtaskModel; +use Kanboard\Model\SubtaskPositionModel; +use Kanboard\Model\SubtaskStatusModel; +use Kanboard\Model\SubtaskTaskConversionModel; +use Kanboard\Model\SubtaskTimeTrackingModel; +use Kanboard\Model\SwimlaneModel; +use Kanboard\Model\TagDuplicationModel; +use Kanboard\Model\TagModel; +use Kanboard\Model\TaskAnalyticModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\TaskDuplicationModel; +use Kanboard\Model\TaskExternalLinkModel; +use Kanboard\Model\TaskFileModel; +use Kanboard\Model\TaskLinkModel; +use Kanboard\Model\TaskMetadataModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\TaskModificationModel; +use Kanboard\Model\TaskPositionModel; +use Kanboard\Model\TaskProjectDuplicationModel; +use Kanboard\Model\TaskProjectMoveModel; +use Kanboard\Model\TaskRecurrenceModel; +use Kanboard\Model\TaskStatusModel; +use Kanboard\Model\TaskTagModel; +use Kanboard\Model\TimezoneModel; +use Kanboard\Model\TransitionModel; +use Kanboard\Model\UserLockingModel; +use Kanboard\Model\UserMentionModel; +use Kanboard\Model\UserMetadataModel; +use Kanboard\Model\UserModel; +use Kanboard\Model\UserNotificationFilterModel; +use Kanboard\Model\UserNotificationModel; +use Kanboard\Model\UserNotificationTypeModel; +use Kanboard\Model\UserUnreadNotificationModel; +use Kanboard\Core\Base; + + +class NewTaskFinderModel extends Base +{ + /** + * Get query for project user overview + * + * @access public + * @param array $project_ids + * @param integer $is_active + * @return \PicoDb\Table + */ + public function getProjectUserOverviewQuery(array $project_ids, $is_active) + { + if (empty($project_ids)) { + $project_ids = array(-1); + } + + return $this->db + ->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.date_started', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.priority', + TaskModel::TABLE.'.time_spent', + TaskModel::TABLE.'.time_estimated', + ProjectModel::TABLE.'.name AS project_name', + ColumnModel::TABLE.'.title AS column_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->eq(TaskModel::TABLE.'.is_active', $is_active) + ->in(ProjectModel::TABLE.'.id', $project_ids) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE); + } + + /** + * Get query for assigned user tasks + * + * @access public + * @param integer $user_id User id + * @return \PicoDb\Table + */ + public function getUserQuery($user_id) + { + return $this->getExtendedQuery() + ->beginOr() + ->eq(TaskModel::TABLE.'.owner_id', $user_id) + ->addCondition(TaskModel::TABLE.".id IN (SELECT task_id FROM ".SubtaskModel::TABLE." WHERE ".SubtaskModel::TABLE.".user_id='$user_id')") + ->addCondition(TaskModel::TABLE.".owner_gp IN (SELECT group_id FROM ".GroupMemberModel::TABLE." WHERE ".GroupMemberModel::TABLE.".user_id='$user_id')") + ->addCondition(TaskModel::TABLE.".owner_ms IN (SELECT group_id FROM ".MultiselectMemberModel::TABLE." WHERE ".MultiselectMemberModel::TABLE.".user_id='$user_id')") + ->closeOr() + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE) + ->eq(ColumnModel::TABLE.'.hide_in_dashboard', 0); + } + + /** + * Extended query + * + * @access public + * @return \PicoDb\Table + */ + public function getExtendedQuery() + { + return $this->db + ->table(TaskModel::TABLE) + ->columns( + '(SELECT COUNT(*) FROM '.CommentModel::TABLE.' WHERE task_id=tasks.id) AS nb_comments', + '(SELECT COUNT(*) FROM '.TaskFileModel::TABLE.' WHERE task_id=tasks.id) AS nb_files', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id) AS nb_subtasks', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks', + '(SELECT COUNT(*) FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id) AS nb_links', + '(SELECT COUNT(*) FROM '.TaskExternalLinkModel::TABLE.' WHERE '.TaskExternalLinkModel::TABLE.'.task_id = tasks.id) AS nb_external_links', + '(SELECT DISTINCT 1 FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id AND '.TaskLinkModel::TABLE.'.link_id = 9) AS is_milestone', + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.reference', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.description', + TaskModel::TABLE.'.date_creation', + TaskModel::TABLE.'.date_modification', + TaskModel::TABLE.'.date_completed', + TaskModel::TABLE.'.date_started', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.column_id', + TaskModel::TABLE.'.swimlane_id', + TaskModel::TABLE.'.owner_id', + TaskModel::TABLE.'.creator_id', + TaskModel::TABLE.'.position', + TaskModel::TABLE.'.is_active', + TaskModel::TABLE.'.score', + TaskModel::TABLE.'.category_id', + TaskModel::TABLE.'.priority', + TaskModel::TABLE.'.date_moved', + TaskModel::TABLE.'.recurrence_status', + TaskModel::TABLE.'.recurrence_trigger', + TaskModel::TABLE.'.recurrence_factor', + TaskModel::TABLE.'.recurrence_timeframe', + TaskModel::TABLE.'.recurrence_basedate', + TaskModel::TABLE.'.recurrence_parent', + TaskModel::TABLE.'.recurrence_child', + TaskModel::TABLE.'.time_estimated', + TaskModel::TABLE.'.time_spent', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name', + UserModel::TABLE.'.email AS assignee_email', + UserModel::TABLE.'.avatar_path AS assignee_avatar_path', + CategoryModel::TABLE.'.name AS category_name', + CategoryModel::TABLE.'.description AS category_description', + CategoryModel::TABLE.'.color_id AS category_color_id', + ColumnModel::TABLE.'.title AS column_name', + ColumnModel::TABLE.'.position AS column_position', + SwimlaneModel::TABLE.'.name AS swimlane_name', + ProjectModel::TABLE.'.name AS project_name', + TaskModel::TABLE.'.owner_ms', + GroupModel::TABLE.'.name AS assigned_groupname' + ) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->join(CategoryModel::TABLE, 'id', 'category_id', TaskModel::TABLE) + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(SwimlaneModel::TABLE, 'id', 'swimlane_id', TaskModel::TABLE) + ->join(GroupModel::TABLE, 'id', 'owner_gp', TaskModel::TABLE) + ->join(MultiselectModel::TABLE, 'id', 'owner_ms', TaskModel::TABLE) + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param integer $status_id Status id + * @return array + */ + public function getAll($project_id, $status_id = TaskModel::STATUS_OPEN) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->eq(TaskModel::TABLE.'.is_active', $status_id) + ->asc(TaskModel::TABLE.'.id') + ->findAll(); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id + * @param array $status + * @return array + */ + public function getAllIds($project_id, array $status = array(TaskModel::STATUS_OPEN)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->in(TaskModel::TABLE.'.is_active', $status) + ->asc(TaskModel::TABLE.'.id') + ->findAllByColumn(TaskModel::TABLE.'.id'); + } + + /** + * Get overdue tasks query + * + * @access public + * @return \PicoDb\Table + */ + public function getOverdueTasksQuery() + { + return $this->db->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.creator_id', + TaskModel::TABLE.'.owner_id', + ProjectModel::TABLE.'.name AS project_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->eq(ProjectModel::TABLE.'.is_active', 1) + ->eq(TaskModel::TABLE.'.is_active', 1) + ->neq(TaskModel::TABLE.'.date_due', 0) + ->lte(TaskModel::TABLE.'.date_due', time()); + } + + /** + * Get a list of overdue tasks for all projects + * + * @access public + * @return array + */ + public function getOverdueTasks() + { + return $this->getOverdueTasksQuery()->findAll(); + } + + /** + * Get a list of overdue tasks by project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getOverdueTasksByProject($project_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.project_id', $project_id)->findAll(); + } + + /** + * Get a list of overdue tasks by user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getOverdueTasksByUser($user_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.owner_id', $user_id)->findAll(); + } + + /** + * Get project id for a given task + * + * @access public + * @param integer $task_id Task id + * @return integer + */ + public function getProjectId($task_id) + { + return (int) $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('project_id') ?: 0; + } + + /** + * Fetch a task by the id + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getById($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOne(); + } + + /** + * Fetch a task by the reference (external id) + * + * @access public + * @param integer $project_id Project id + * @param string $reference Task reference + * @return array + */ + public function getByReference($project_id, $reference) + { + return $this->db->table(TaskModel::TABLE)->eq('project_id', $project_id)->eq('reference', $reference)->findOne(); + } + + /** + * Get task details (fetch more information from other tables) + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getDetails($task_id) + { + return $this->db->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.*', + CategoryModel::TABLE.'.name AS category_name', + SwimlaneModel::TABLE.'.name AS swimlane_name', + ProjectModel::TABLE.'.name AS project_name', + ColumnModel::TABLE.'.title AS column_title', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name', + 'uc.username AS creator_username', + 'uc.name AS creator_name', + CategoryModel::TABLE.'.description AS category_description', + ColumnModel::TABLE.'.position AS column_position', + GroupModel::TABLE.'.name AS assigned_groupname', + ColumnModel::TABLE.'.position AS column_position' + ) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->join(CategoryModel::TABLE, 'id', 'category_id', TaskModel::TABLE) + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(SwimlaneModel::TABLE, 'id', 'swimlane_id', TaskModel::TABLE) + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE) + ->join(GroupModel::TABLE, 'id', 'owner_gp', TaskModel::TABLE) + ->join(MultiselectModel::TABLE, 'id', 'owner_ms', TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.id', $task_id) + ->findOne(); + } + + /** + * Get iCal query + * + * @access public + * @return \PicoDb\Table + */ + public function getICalQuery() + { + return $this->db->table(TaskModel::TABLE) + ->left(UserModel::TABLE, 'ua', 'id', TaskModel::TABLE, 'owner_id') + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->columns( + TaskModel::TABLE.'.*', + 'ua.email AS assignee_email', + 'ua.name AS assignee_name', + 'ua.username AS assignee_username', + 'uc.email AS creator_email', + 'uc.name AS creator_name', + 'uc.username AS creator_username' + ); + } + + /** + * Count all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param array $status List of status id + * @return integer + */ + public function countByProjectId($project_id, array $status = array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->in('is_active', $status) + ->count(); + } + + /** + * Count the number of tasks for a given column and status + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param array $status + * @return int + */ + public function countByColumnId($project_id, $column_id, array $status = array(TaskModel::STATUS_OPEN)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->in('is_active', $status) + ->count(); + } + + /** + * Count the number of tasks for a given column and swimlane + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param integer $swimlane_id Swimlane id + * @return integer + */ + public function countByColumnAndSwimlaneId($project_id, $column_id, $swimlane_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->eq('swimlane_id', $swimlane_id) + ->eq('is_active', 1) + ->count(); + } + + /** + * Return true if the task exists + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function exists($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->exists(); + } + + /** + * Get project token + * + * @access public + * @param integer $task_id + * @return string + */ + public function getProjectToken($task_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.id', $task_id) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->findOneColumn(ProjectModel::TABLE.'.token'); + } +} diff --git a/plugins/Group_assign/Model/NewUserNotificationFilterModel.php b/plugins/Group_assign/Model/NewUserNotificationFilterModel.php new file mode 100644 index 00000000..3fa67487 --- /dev/null +++ b/plugins/Group_assign/Model/NewUserNotificationFilterModel.php @@ -0,0 +1,218 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Model\UserModel; +use Kanboard\Model\GroupMemberModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectMemberModel; +use Kanboard\Core\Base; + +/** + * User Notification Filter + * + * @package Kanboard\Plugin\Group_assign + * @author Craig Crosby + */ +class NewUserNotificationFilterModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const PROJECT_TABLE = 'user_has_notifications'; + + /** + * User filters + * + * @var integer + */ + const FILTER_NONE = 1; + const FILTER_ASSIGNEE = 2; + const FILTER_CREATOR = 3; + const FILTER_BOTH = 4; + + /** + * Get the list of filters + * + * @access public + * @return array + */ + public function getFilters() + { + return array( + self::FILTER_NONE => t('All tasks'), + self::FILTER_ASSIGNEE => t('Only for tasks assigned to me'), + self::FILTER_CREATOR => t('Only for tasks created by me'), + self::FILTER_BOTH => t('Only for tasks created by me and tasks assigned to me'), + ); + } + + /** + * Get user selected filter + * + * @access public + * @param integer $user_id + * @return integer + */ + public function getSelectedFilter($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->findOneColumn('notifications_filter'); + } + + /** + * Save selected filter for a user + * + * @access public + * @param integer $user_id + * @param string $filter + * @return boolean + */ + public function saveFilter($user_id, $filter) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array( + 'notifications_filter' => $filter, + )); + } + + /** + * Get user selected projects + * + * @access public + * @param integer $user_id + * @return array + */ + public function getSelectedProjects($user_id) + { + return $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->findAllByColumn('project_id'); + } + + /** + * Save selected projects for a user + * + * @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) { + $results[] = $this->db->table(self::PROJECT_TABLE)->insert(array( + 'user_id' => $user_id, + 'project_id' => $project_id, + )); + } + + return !in_array(false, $results, true); + } + + /** + * Return true if the user should receive notification + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function shouldReceiveNotification(array $user, array $event_data) + { + $filters = array( + 'filterNone', + 'filterAssignee', + 'filterCreator', + 'filterBoth', + ); + + foreach ($filters as $filter) { + if ($this->$filter($user, $event_data)) { + return $this->filterProject($user, $event_data); + } + } + + return false; + } + + /** + * Return true if the user will receive all notifications + * + * @access public + * @param array $user + * @return boolean + */ + public function filterNone(array $user) + { + return $user['notifications_filter'] == self::FILTER_NONE; + } + + /** + * Return true if the user is the assignee and selected the filter "assignee" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterAssignee(array $user, array $event_data) + { + if (!isset($event_data['task']['owner_ms'])) $event_data['task']['owner_ms'] = 0; + if (!isset($event_data['task']['owner_gp'])) $event_data['task']['owner_gp'] = 0; + return $user['notifications_filter'] == self::FILTER_ASSIGNEE && + ($event_data['task']['owner_id'] == $user['id'] || + $this->multiselectMemberModel->isMember($event_data['task']['owner_ms'], $user['id']) || + $this->groupMemberModel->isMember($event_data['task']['owner_gp'], $user['id'])); + } + + /** + * Return true if the user is the creator and enabled the filter "creator" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterCreator(array $user, array $event_data) + { + return $user['notifications_filter'] == self::FILTER_CREATOR && $event_data['task']['creator_id'] == $user['id']; + } + + /** + * Return true if the user is the assignee or the creator and selected the filter "both" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterBoth(array $user, array $event_data) + { + if (!isset($event_data['task']['owner_ms'])) $event_data['task']['owner_ms'] = 0; + if (!isset($event_data['task']['owner_gp'])) $event_data['task']['owner_gp'] = 0; + return $user['notifications_filter'] == self::FILTER_BOTH && + ($event_data['task']['creator_id'] == $user['id'] || $event_data['task']['owner_id'] == $user['id'] || + $this->multiselectMemberModel->isMember($event_data['task']['owner_ms'], $user['id']) || + $this->groupMemberModel->isMember($event_data['task']['owner_gp'], $user['id'])); + } + + /** + * Return true if the user want to receive notification for the selected project + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterProject(array $user, array $event_data) + { + $projects = $this->getSelectedProjects($user['id']); + + if (! empty($projects)) { + return in_array($event_data['task']['project_id'], $projects); + } + + return true; + } +} diff --git a/plugins/Group_assign/Model/OldMetaMagikSubquery.php b/plugins/Group_assign/Model/OldMetaMagikSubquery.php new file mode 100644 index 00000000..787b7e4a --- /dev/null +++ b/plugins/Group_assign/Model/OldMetaMagikSubquery.php @@ -0,0 +1,28 @@ +<?php
+
+namespace Kanboard\Plugin\Group_assign\Model;
+
+use Kanboard\Plugin\Group_assign\Model\OldTaskFinderModel;
+
+/**
+ * New Task Finder model
+ * Extends Group_assign Model
+ *
+ * @package Kanboard\Plugin\Group_assign\Model
+ */
+class OldMetaMagikSubQuery extends OldTaskFinderModel
+{
+ const METADATA_TABLE = 'task_has_metadata';
+ /**
+ * Extended query
+ *
+ * @access public
+ * @return \PicoDb\Table
+ */
+ public function getExtendedQuery()
+ {
+ // add subquery to original Model, changing only what we want
+ return parent::getExtendedQuery()
+ ->subquery('(SELECT COUNT(*) FROM '.self::METADATA_TABLE.' WHERE task_id=tasks.id)', 'nb_metadata');
+ }
+}
\ No newline at end of file diff --git a/plugins/Group_assign/Model/OldTaskFinderModel.php b/plugins/Group_assign/Model/OldTaskFinderModel.php new file mode 100644 index 00000000..70d06126 --- /dev/null +++ b/plugins/Group_assign/Model/OldTaskFinderModel.php @@ -0,0 +1,489 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Model\ActionParameterModel; +use Kanboard\Model\AvatarFileModel; +use Kanboard\Model\BoardModel; +use Kanboard\Model\CategoryModel; +use Kanboard\Model\ColorModel; +use Kanboard\Model\ColumnModel; +use Kanboard\Model\ColumnMoveRestrictionModel; +use Kanboard\Model\CommentModel; +use Kanboard\Model\ConfigModel; +use Kanboard\Model\CurrencyModel; +use Kanboard\Model\CustomFilterModel; +use Kanboard\Model\FileModel; +use Kanboard\Model\GroupMemberModel; +use Kanboard\Model\GroupModel; +use Kanboard\Model\LanguageModel; +use Kanboard\Model\LastLoginModel; +use Kanboard\Model\LinkModel; +use Kanboard\Model\MetadataModel; +use Kanboard\Model\NotificationModel; +use Kanboard\Model\NotificationTypeModel; +use Kanboard\Model\PasswordResetModel; +use Kanboard\Model\ProjectActivityModel; +use Kanboard\Model\ProjectDailyColumnStatsModel; +use Kanboard\Model\ProjectDailyStatsModel; +use Kanboard\Model\ProjectDuplicationModel; +use Kanboard\Model\ProjectFileModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Model\ProjectMetadataModel; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\ProjectNotificationModel; +use Kanboard\Model\ProjectNotificationTypeModel; +use Kanboard\Model\ProjectPermissionModel; +use Kanboard\Model\ProjectRoleModel; +use Kanboard\Model\ProjectRoleRestrictionModel; +use Kanboard\Model\ProjectTaskDuplicationModel; +use Kanboard\Model\ProjectTaskPriorityModel; +use Kanboard\Model\ProjectUserRoleModel; +use Kanboard\Model\RememberMeSessionModel; +use Kanboard\Model\SettingModel; +use Kanboard\Model\SubtaskModel; +use Kanboard\Model\SubtaskPositionModel; +use Kanboard\Model\SubtaskStatusModel; +use Kanboard\Model\SubtaskTaskConversionModel; +use Kanboard\Model\SubtaskTimeTrackingModel; +use Kanboard\Model\SwimlaneModel; +use Kanboard\Model\TagDuplicationModel; +use Kanboard\Model\TagModel; +use Kanboard\Model\TaskAnalyticModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\TaskDuplicationModel; +use Kanboard\Model\TaskExternalLinkModel; +use Kanboard\Model\TaskFileModel; +use Kanboard\Model\TaskLinkModel; +use Kanboard\Model\TaskMetadataModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\TaskModificationModel; +use Kanboard\Model\TaskPositionModel; +use Kanboard\Model\TaskProjectDuplicationModel; +use Kanboard\Model\TaskProjectMoveModel; +use Kanboard\Model\TaskRecurrenceModel; +use Kanboard\Model\TaskStatusModel; +use Kanboard\Model\TaskTagModel; +use Kanboard\Model\TimezoneModel; +use Kanboard\Model\TransitionModel; +use Kanboard\Model\UserLockingModel; +use Kanboard\Model\UserMentionModel; +use Kanboard\Model\UserMetadataModel; +use Kanboard\Model\UserModel; +use Kanboard\Model\UserNotificationFilterModel; +use Kanboard\Model\UserNotificationModel; +use Kanboard\Model\UserNotificationTypeModel; +use Kanboard\Model\UserUnreadNotificationModel; +use Kanboard\Core\Base; + + +class OldTaskFinderModel extends Base +{ + /** + * Get query for project user overview + * + * @access public + * @param array $project_ids + * @param integer $is_active + * @return \PicoDb\Table + */ + public function getProjectUserOverviewQuery(array $project_ids, $is_active) + { + if (empty($project_ids)) { + $project_ids = array(-1); + } + + return $this->db + ->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.date_started', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.priority', + TaskModel::TABLE.'.time_spent', + TaskModel::TABLE.'.time_estimated', + ProjectModel::TABLE.'.name AS project_name', + ColumnModel::TABLE.'.title AS column_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->eq(TaskModel::TABLE.'.is_active', $is_active) + ->in(ProjectModel::TABLE.'.id', $project_ids) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE); + } + + /** + * Get query for assigned user tasks + * + * @access public + * @param integer $user_id User id + * @return \PicoDb\Table + */ + public function getUserQuery($user_id) + { + return $this->getExtendedQuery() + ->beginOr() + ->eq(TaskModel::TABLE.'.owner_id', $user_id) + ->addCondition(TaskModel::TABLE.".id IN (SELECT task_id FROM ".SubtaskModel::TABLE." WHERE ".SubtaskModel::TABLE.".user_id='$user_id')") + ->addCondition(TaskModel::TABLE.".owner_gp IN (SELECT group_id FROM ".GroupMemberModel::TABLE." WHERE ".GroupMemberModel::TABLE.".user_id='$user_id')") + ->addCondition(TaskModel::TABLE.".owner_ms IN (SELECT group_id FROM ".MultiselectMemberModel::TABLE." WHERE ".MultiselectMemberModel::TABLE.".user_id='$user_id')") + ->closeOr() + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE) + ->eq(ColumnModel::TABLE.'.hide_in_dashboard', 0); + } + + /** + * Extended query + * + * @access public + * @return \PicoDb\Table + */ + public function getExtendedQuery() + { + return $this->db + ->table(TaskModel::TABLE) + ->columns( + '(SELECT COUNT(*) FROM '.CommentModel::TABLE.' WHERE task_id=tasks.id) AS nb_comments', + '(SELECT COUNT(*) FROM '.TaskFileModel::TABLE.' WHERE task_id=tasks.id) AS nb_files', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id) AS nb_subtasks', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks', + '(SELECT COUNT(*) FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id) AS nb_links', + '(SELECT COUNT(*) FROM '.TaskExternalLinkModel::TABLE.' WHERE '.TaskExternalLinkModel::TABLE.'.task_id = tasks.id) AS nb_external_links', + '(SELECT DISTINCT 1 FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id AND '.TaskLinkModel::TABLE.'.link_id = 9) AS is_milestone', + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.reference', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.description', + TaskModel::TABLE.'.date_creation', + TaskModel::TABLE.'.date_modification', + TaskModel::TABLE.'.date_completed', + TaskModel::TABLE.'.date_started', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.column_id', + TaskModel::TABLE.'.swimlane_id', + TaskModel::TABLE.'.owner_id', + TaskModel::TABLE.'.creator_id', + TaskModel::TABLE.'.position', + TaskModel::TABLE.'.is_active', + TaskModel::TABLE.'.score', + TaskModel::TABLE.'.category_id', + TaskModel::TABLE.'.priority', + TaskModel::TABLE.'.date_moved', + TaskModel::TABLE.'.recurrence_status', + TaskModel::TABLE.'.recurrence_trigger', + TaskModel::TABLE.'.recurrence_factor', + TaskModel::TABLE.'.recurrence_timeframe', + TaskModel::TABLE.'.recurrence_basedate', + TaskModel::TABLE.'.recurrence_parent', + TaskModel::TABLE.'.recurrence_child', + TaskModel::TABLE.'.time_estimated', + TaskModel::TABLE.'.time_spent', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name', + UserModel::TABLE.'.email AS assignee_email', + UserModel::TABLE.'.avatar_path AS assignee_avatar_path', + CategoryModel::TABLE.'.name AS category_name', + CategoryModel::TABLE.'.description AS category_description', + ColumnModel::TABLE.'.title AS column_name', + ColumnModel::TABLE.'.position AS column_position', + SwimlaneModel::TABLE.'.name AS swimlane_name', + ProjectModel::TABLE.'.name AS project_name', + TaskModel::TABLE.'.owner_ms', + GroupModel::TABLE.'.name AS assigned_groupname' + ) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->join(CategoryModel::TABLE, 'id', 'category_id', TaskModel::TABLE) + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(SwimlaneModel::TABLE, 'id', 'swimlane_id', TaskModel::TABLE) + ->join(GroupModel::TABLE, 'id', 'owner_gp', TaskModel::TABLE) + ->join(MultiselectModel::TABLE, 'id', 'owner_ms', TaskModel::TABLE) + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param integer $status_id Status id + * @return array + */ + public function getAll($project_id, $status_id = TaskModel::STATUS_OPEN) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->eq(TaskModel::TABLE.'.is_active', $status_id) + ->asc(TaskModel::TABLE.'.id') + ->findAll(); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id + * @param array $status + * @return array + */ + public function getAllIds($project_id, array $status = array(TaskModel::STATUS_OPEN)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->in(TaskModel::TABLE.'.is_active', $status) + ->asc(TaskModel::TABLE.'.id') + ->findAllByColumn(TaskModel::TABLE.'.id'); + } + + /** + * Get overdue tasks query + * + * @access public + * @return \PicoDb\Table + */ + public function getOverdueTasksQuery() + { + return $this->db->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.creator_id', + TaskModel::TABLE.'.owner_id', + ProjectModel::TABLE.'.name AS project_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->eq(ProjectModel::TABLE.'.is_active', 1) + ->eq(TaskModel::TABLE.'.is_active', 1) + ->neq(TaskModel::TABLE.'.date_due', 0) + ->lte(TaskModel::TABLE.'.date_due', time()); + } + + /** + * Get a list of overdue tasks for all projects + * + * @access public + * @return array + */ + public function getOverdueTasks() + { + return $this->getOverdueTasksQuery()->findAll(); + } + + /** + * Get a list of overdue tasks by project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getOverdueTasksByProject($project_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.project_id', $project_id)->findAll(); + } + + /** + * Get a list of overdue tasks by user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getOverdueTasksByUser($user_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.owner_id', $user_id)->findAll(); + } + + /** + * Get project id for a given task + * + * @access public + * @param integer $task_id Task id + * @return integer + */ + public function getProjectId($task_id) + { + return (int) $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('project_id') ?: 0; + } + + /** + * Fetch a task by the id + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getById($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOne(); + } + + /** + * Fetch a task by the reference (external id) + * + * @access public + * @param integer $project_id Project id + * @param string $reference Task reference + * @return array + */ + public function getByReference($project_id, $reference) + { + return $this->db->table(TaskModel::TABLE)->eq('project_id', $project_id)->eq('reference', $reference)->findOne(); + } + + /** + * Get task details (fetch more information from other tables) + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getDetails($task_id) + { + return $this->db->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.*', + CategoryModel::TABLE.'.name AS category_name', + SwimlaneModel::TABLE.'.name AS swimlane_name', + ProjectModel::TABLE.'.name AS project_name', + ColumnModel::TABLE.'.title AS column_title', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name', + 'uc.username AS creator_username', + 'uc.name AS creator_name', + CategoryModel::TABLE.'.description AS category_description', + ColumnModel::TABLE.'.position AS column_position', + GroupModel::TABLE.'.name AS assigned_groupname' + ) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->join(CategoryModel::TABLE, 'id', 'category_id', TaskModel::TABLE) + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(SwimlaneModel::TABLE, 'id', 'swimlane_id', TaskModel::TABLE) + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE) + ->join(GroupModel::TABLE, 'id', 'owner_gp', TaskModel::TABLE) + ->join(MultiselectModel::TABLE, 'id', 'owner_ms', TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.id', $task_id) + ->findOne(); + } + + /** + * Get iCal query + * + * @access public + * @return \PicoDb\Table + */ + public function getICalQuery() + { + return $this->db->table(TaskModel::TABLE) + ->left(UserModel::TABLE, 'ua', 'id', TaskModel::TABLE, 'owner_id') + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->columns( + TaskModel::TABLE.'.*', + 'ua.email AS assignee_email', + 'ua.name AS assignee_name', + 'ua.username AS assignee_username', + 'uc.email AS creator_email', + 'uc.name AS creator_name', + 'uc.username AS creator_username' + ); + } + + /** + * Count all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param array $status List of status id + * @return integer + */ + public function countByProjectId($project_id, array $status = array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->in('is_active', $status) + ->count(); + } + + /** + * Count the number of tasks for a given column and status + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param array $status + * @return int + */ + public function countByColumnId($project_id, $column_id, array $status = array(TaskModel::STATUS_OPEN)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->in('is_active', $status) + ->count(); + } + + /** + * Count the number of tasks for a given column and swimlane + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param integer $swimlane_id Swimlane id + * @return integer + */ + public function countByColumnAndSwimlaneId($project_id, $column_id, $swimlane_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->eq('swimlane_id', $swimlane_id) + ->eq('is_active', 1) + ->count(); + } + + /** + * Return true if the task exists + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function exists($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->exists(); + } + + /** + * Get project token + * + * @access public + * @param integer $task_id + * @return string + */ + public function getProjectToken($task_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.id', $task_id) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->findOneColumn(ProjectModel::TABLE.'.token'); + } +} diff --git a/plugins/Group_assign/Model/TaskProjectDuplicationModel.php b/plugins/Group_assign/Model/TaskProjectDuplicationModel.php new file mode 100644 index 00000000..835f2063 --- /dev/null +++ b/plugins/Group_assign/Model/TaskProjectDuplicationModel.php @@ -0,0 +1,94 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Model\TaskDuplicationModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectMemberModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectModel; +use Kanboard\Model\ProjectPermissionModel; +use Kanboard\Model\TaskLinkModel; + +/** + * Task Project Duplication + * + * @package Kanboard\Plugins\Group_assign + * @author Craig Crosby + */ +class TaskProjectDuplicationModel extends TaskDuplicationModel +{ + /** + * Duplicate a task to another project + * + * @access public + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return boolean|integer + */ + public function duplicateToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null, $owner_gp = 0, $owner_ms = 0) + { + $values = $this->prepare($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); + + $this->checkDestinationProjectValues($values); + $new_task_id = $this->save($task_id, $values); + if ($new_task_id !== false) { + // Check if the group is allowed for the destination project + $group_id = $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('owner_gp'); + if ($group_id > 0) { + $group_in_project = $this->db + ->table(ProjectGroupRoleModel::TABLE) + ->eq('project_id', $values['project_id']) + ->eq('group_id', $group_id) + ->exists(); + if ($group_in_project) { $this->db->table(TaskModel::TABLE)->eq('id', $new_task_id)->update(['owner_gp' => $group_id]); } + } + + // Check if the other assignees are allowed for the destination project + $ms_id = $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('owner_ms'); + if ($ms_id > 0) { + $users_in_ms = $this->multiselectMemberModel->getMembers($ms_id); + $new_ms_id = $this->multiselectModel->create(); + $this->db->table(TaskModel::TABLE)->eq('id', $new_task_id)->update(['owner_ms' => $new_ms_id]); + foreach ($users_in_ms as $user) { + if ($this->projectPermissionModel->isAssignable($values['project_id'], $user['id'])) { + $this->multiselectMemberModel->addUser($new_ms_id, $user['id']); + } + } + } + + $this->tagDuplicationModel->duplicateTaskTagsToAnotherProject($task_id, $new_task_id, $project_id); + $this->taskLinkModel->create($new_task_id, $task_id, 4); + } + + return $new_task_id; + } + + /** + * Prepare values before duplication + * + * @access protected + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return array + */ + protected function prepare($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id) + { + $values = $this->copyFields($task_id); + $values['project_id'] = $project_id; + $values['column_id'] = $column_id !== null ? $column_id : $values['column_id']; + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $values['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $values['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $values['owner_id']; + + return $values; + } +} diff --git a/plugins/Group_assign/Model/TaskProjectMoveModel.php b/plugins/Group_assign/Model/TaskProjectMoveModel.php new file mode 100644 index 00000000..554d3330 --- /dev/null +++ b/plugins/Group_assign/Model/TaskProjectMoveModel.php @@ -0,0 +1,96 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use Kanboard\Model\TaskDuplicationModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectMemberModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectModel; +use Kanboard\Model\ProjectPermissionModel; +use Kanboard\Model\TaskLinkModel; + +/** + * Task Project Move + * + * @package Kanboard\Plugins\Group_assign + * @author Craig Crosby + */ +class TaskProjectMoveModel extends TaskDuplicationModel +{ + /** + * Move a task to another project + * + * @access public + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return boolean + */ + public function moveToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) + { + $task = $this->taskFinderModel->getById($task_id); + $values = $this->prepare($project_id, $swimlane_id, $column_id, $category_id, $owner_id, $task); + + $this->checkDestinationProjectValues($values); + $this->tagDuplicationModel->syncTaskTagsToAnotherProject($task_id, $project_id); + + // Check if the group is allowed for the destination project and unassign if not + $group_id = $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('owner_gp'); + if ($group_id > 0) { + $group_in_project = $this->db + ->table(ProjectGroupRoleModel::TABLE) + ->eq('project_id', $project_id) + ->eq('group_id', $group_id) + ->exists(); + if (!$group_in_project) { $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(['owner_gp' => 0]); } + } + + // Check if the other assignees are allowed for the destination project and remove from ms group if not + $ms_id = $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('owner_ms'); + if ($ms_id > 0) { + $users_in_ms = $this->multiselectMemberModel->getMembers($ms_id); + foreach ($users_in_ms as $user) { + if (! $this->projectPermissionModel->isAssignable($project_id, $user['id'])) { + $this->multiselectMemberModel->removeUser($ms_id, $user['id']); + } + } + } + + + if ($this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update($values)) { + $this->queueManager->push($this->taskEventJob->withParams($task_id, array(TaskModel::EVENT_MOVE_PROJECT), $values)); + } + + return true; + } + + /** + * Prepare new task values + * + * @access protected + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @param array $task + * @return array + */ + protected function prepare($project_id, $swimlane_id, $column_id, $category_id, $owner_id, array $task) + { + $values = array(); + $values['is_active'] = 1; + $values['project_id'] = $project_id; + $values['column_id'] = $column_id !== null ? $column_id : $task['column_id']; + $values['position'] = $this->taskFinderModel->countByColumnId($project_id, $values['column_id']) + 1; + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $task['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $task['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $task['owner_id']; + $values['priority'] = $task['priority']; + return $values; + } +} diff --git a/plugins/Group_assign/Model/TaskRecurrenceModel.php b/plugins/Group_assign/Model/TaskRecurrenceModel.php new file mode 100644 index 00000000..999751e1 --- /dev/null +++ b/plugins/Group_assign/Model/TaskRecurrenceModel.php @@ -0,0 +1,154 @@ +<?php + +namespace Kanboard\Plugin\Group_assign\Model; + +use DateInterval; +use DateTime; +use Kanboard\Model\TaskDuplicationModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectMemberModel; +use Kanboard\Plugin\Group_assign\Model\MultiselectModel; +use Kanboard\Model\ProjectPermissionModel; +use Kanboard\Model\TaskLinkModel; + +/** + * Task Recurrence + * + * @package Kanboard\Plugin\Group_assign + * @author Craig Crosby + */ +class TaskRecurrenceModel extends GroupAssignTaskDuplicationModel +{ + /** + * Return the list user selectable recurrence status + * + * @access public + * @return array + */ + public function getRecurrenceStatusList() + { + return array( + TaskModel::RECURRING_STATUS_NONE => t('No'), + TaskModel::RECURRING_STATUS_PENDING => t('Yes'), + ); + } + + /** + * Return the list recurrence triggers + * + * @access public + * @return array + */ + public function getRecurrenceTriggerList() + { + return array( + TaskModel::RECURRING_TRIGGER_FIRST_COLUMN => t('When task is moved from first column'), + TaskModel::RECURRING_TRIGGER_LAST_COLUMN => t('When task is moved to last column'), + TaskModel::RECURRING_TRIGGER_CLOSE => t('When task is closed'), + ); + } + + /** + * Return the list options to calculate recurrence due date + * + * @access public + * @return array + */ + public function getRecurrenceBasedateList() + { + return array( + TaskModel::RECURRING_BASEDATE_DUEDATE => t('Existing due date'), + TaskModel::RECURRING_BASEDATE_TRIGGERDATE => t('Action date'), + ); + } + + /** + * Return the list recurrence timeframes + * + * @access public + * @return array + */ + public function getRecurrenceTimeframeList() + { + return array( + TaskModel::RECURRING_TIMEFRAME_DAYS => t('Day(s)'), + TaskModel::RECURRING_TIMEFRAME_MONTHS => t('Month(s)'), + TaskModel::RECURRING_TIMEFRAME_YEARS => t('Year(s)'), + ); + } + + /** + * Duplicate recurring task + * + * @access public + * @param integer $task_id Task id + * @return boolean|integer Recurrence task id + */ + public function duplicateRecurringTask($task_id) + { + $values = $this->copyFields($task_id); + + if ($values['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) { + $values['recurrence_parent'] = $task_id; + $values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']); + $this->calculateRecurringTaskDueDate($values); + + $recurring_task_id = $this->save($task_id, $values); + + if ($recurring_task_id !== false) { + $this->tagDuplicationModel->duplicateTaskTags($task_id, $recurring_task_id); + + $parent_update = $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->update(array( + 'recurrence_status' => TaskModel::RECURRING_STATUS_PROCESSED, + 'recurrence_child' => $recurring_task_id, + )); + + if ($parent_update) { + return $recurring_task_id; + } + } + } + + return false; + } + + /** + * Calculate new due date for new recurrence task + * + * @access public + * @param array $values Task fields + */ + public function calculateRecurringTaskDueDate(array &$values) + { + if (! empty($values['date_due']) && $values['recurrence_factor'] != 0) { + if ($values['recurrence_basedate'] == TaskModel::RECURRING_BASEDATE_TRIGGERDATE) { + $values['date_due'] = time(); + } + + $factor = abs($values['recurrence_factor']); + $subtract = $values['recurrence_factor'] < 0; + + switch ($values['recurrence_timeframe']) { + case TaskModel::RECURRING_TIMEFRAME_MONTHS: + $interval = 'P' . $factor . 'M'; + break; + case TaskModel::RECURRING_TIMEFRAME_YEARS: + $interval = 'P' . $factor . 'Y'; + break; + default: + $interval = 'P' . $factor . 'D'; + } + + $date_due = new DateTime(); + $date_due->setTimestamp($values['date_due']); + + $subtract ? $date_due->sub(new DateInterval($interval)) : $date_due->add(new DateInterval($interval)); + + $values['date_due'] = $date_due->getTimestamp(); + } + } +} |