diff options
-rw-r--r-- | app/Controller/Base.php | 2 | ||||
-rw-r--r-- | app/Model/Action.php | 4 | ||||
-rw-r--r-- | app/Model/Base.php | 2 | ||||
-rw-r--r-- | app/Model/Category.php | 2 | ||||
-rw-r--r-- | app/Model/Currency.php | 2 | ||||
-rw-r--r-- | app/Model/File.php | 2 | ||||
-rw-r--r-- | app/Model/Link.php | 4 | ||||
-rw-r--r-- | app/Model/Project.php | 6 | ||||
-rw-r--r-- | app/Model/ProjectDuplication.php | 2 | ||||
-rw-r--r-- | app/Model/ProjectIntegration.php | 4 | ||||
-rw-r--r-- | app/Model/ProjectPermission.php | 6 | ||||
-rw-r--r-- | app/Model/Subtask.php | 2 | ||||
-rw-r--r-- | app/Model/TaskFinder.php | 2 | ||||
-rw-r--r-- | app/Model/TaskLink.php | 2 | ||||
-rw-r--r-- | app/Model/User.php | 2 | ||||
-rw-r--r-- | app/ServiceProvider/DatabaseProvider.php | 2 | ||||
-rw-r--r-- | composer.lock | 8 | ||||
-rw-r--r-- | docs/closing-tasks.markdown | 14 | ||||
-rw-r--r-- | docs/index.markdown | 4 | ||||
-rw-r--r-- | docs/notifications.markdown | 30 | ||||
-rw-r--r-- | docs/project-configuration.markdown | 8 | ||||
-rw-r--r-- | docs/rss.markdown | 23 | ||||
-rw-r--r-- | docs/subtasks.markdown | 47 | ||||
-rw-r--r-- | docs/time-tracking.markdown | 25 | ||||
-rw-r--r-- | tests/units/Base.php | 2 |
25 files changed, 161 insertions, 46 deletions
diff --git a/app/Controller/Base.php b/app/Controller/Base.php index 19bb9ac9..cab70c6b 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -65,7 +65,7 @@ abstract class Base extends \Core\Base $this->container['logger']->debug($message); } - $this->container['logger']->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nb_queries)); + $this->container['logger']->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries)); $this->container['logger']->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT'])); $this->container['logger']->debug('END_REQUEST='.$_SERVER['REQUEST_URI']); } diff --git a/app/Model/Action.php b/app/Model/Action.php index 4ede5661..d0607794 100644 --- a/app/Model/Action.php +++ b/app/Model/Action.php @@ -241,7 +241,7 @@ class Action extends Base return false; } - $action_id = $this->db->getConnection()->getLastId(); + $action_id = $this->db->getLastId(); foreach ($values['params'] as $param_name => $param_value) { @@ -329,7 +329,7 @@ class Action extends Base continue; } - $action_id = $this->db->getConnection()->getLastId(); + $action_id = $this->db->getLastId(); if (! $this->duplicateParameters($dst_project_id, $action_id, $action['params'])) { $this->container['logger']->debug('Action::duplicate => unable to copy parameters for '.$action['action_name']); diff --git a/app/Model/Base.php b/app/Model/Base.php index 51ae782d..973462cc 100644 --- a/app/Model/Base.php +++ b/app/Model/Base.php @@ -48,7 +48,7 @@ abstract class Base extends \Core\Base return false; } - return (int) $db->getConnection()->getLastId(); + return (int) $db->getLastId(); }); } diff --git a/app/Model/Category.php b/app/Model/Category.php index 9f93e7be..dcc0f88e 100644 --- a/app/Model/Category.php +++ b/app/Model/Category.php @@ -30,7 +30,7 @@ class Category extends Base */ public function exists($category_id, $project_id) { - return $this->db->table(self::TABLE)->eq('id', $category_id)->eq('project_id', $project_id)->count() > 0; + return $this->db->table(self::TABLE)->eq('id', $category_id)->eq('project_id', $project_id)->exists(); } /** diff --git a/app/Model/Currency.php b/app/Model/Currency.php index 6ae842e7..d04df763 100644 --- a/app/Model/Currency.php +++ b/app/Model/Currency.php @@ -64,7 +64,7 @@ class Currency extends Base */ public function create($currency, $rate) { - if ($this->db->table(self::TABLE)->eq('currency', $currency)->count() === 1) { + if ($this->db->table(self::TABLE)->eq('currency', $currency)->exists()) { return $this->update($currency, $rate); } diff --git a/app/Model/File.php b/app/Model/File.php index 38b34cd3..3a44fee1 100644 --- a/app/Model/File.php +++ b/app/Model/File.php @@ -105,7 +105,7 @@ class File extends Base new FileEvent(array('task_id' => $task_id, 'name' => $name)) ); - return (int) $this->db->getConnection()->getLastId(); + return (int) $this->db->getLastId(); } return false; diff --git a/app/Model/Link.php b/app/Model/Link.php index e0e5184e..467710bd 100644 --- a/app/Model/Link.php +++ b/app/Model/Link.php @@ -123,7 +123,7 @@ class Link extends Base return false; } - $label_id = $this->db->getConnection()->getLastId(); + $label_id = $this->db->getLastId(); if (! empty($opposite_label)) { @@ -138,7 +138,7 @@ class Link extends Base ->table(self::TABLE) ->eq('id', $label_id) ->update(array( - 'opposite_id' => $this->db->getConnection()->getLastId() + 'opposite_id' => $this->db->getLastId() )); } diff --git a/app/Model/Project.php b/app/Model/Project.php index ef7340ee..3c864e5d 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -111,7 +111,7 @@ class Project extends Base */ public function isPrivate($project_id) { - return (bool) $this->db->table(self::TABLE)->eq('id', $project_id)->eq('is_private', 1)->count(); + return $this->db->table(self::TABLE)->eq('id', $project_id)->eq('is_private', 1)->exists(); } /** @@ -305,7 +305,7 @@ class Project extends Base return false; } - $project_id = $this->db->getConnection()->getLastId(); + $project_id = $this->db->getLastId(); if (! $this->board->create($project_id, $this->board->getUserColumns())) { $this->db->cancelTransaction(); @@ -391,7 +391,7 @@ class Project extends Base */ public function exists($project_id) { - return $this->db->table(self::TABLE)->eq('id', $project_id)->count() === 1; + return $this->db->table(self::TABLE)->eq('id', $project_id)->exists(); } /** diff --git a/app/Model/ProjectDuplication.php b/app/Model/ProjectDuplication.php index 7e3407be..6e054a4b 100644 --- a/app/Model/ProjectDuplication.php +++ b/app/Model/ProjectDuplication.php @@ -53,7 +53,7 @@ class ProjectDuplication extends Base return 0; } - return $this->db->getConnection()->getLastId(); + return $this->db->getLastId(); } /** diff --git a/app/Model/ProjectIntegration.php b/app/Model/ProjectIntegration.php index 98ff8d4c..bcbfeae5 100644 --- a/app/Model/ProjectIntegration.php +++ b/app/Model/ProjectIntegration.php @@ -39,7 +39,7 @@ class ProjectIntegration extends Base */ public function saveParameters($project_id, array $values) { - if ($this->db->table(self::TABLE)->eq('project_id', $project_id)->count() === 1) { + if ($this->db->table(self::TABLE)->eq('project_id', $project_id)->exists()) { return $this->db->table(self::TABLE)->eq('project_id', $project_id)->update($values); } @@ -61,6 +61,6 @@ class ProjectIntegration extends Base ->table(self::TABLE) ->eq('project_id', $project_id) ->eq($option, $value) - ->count() === 1; + ->exists(); } } diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php index b0a09df4..bc752dda 100644 --- a/app/Model/ProjectPermission.php +++ b/app/Model/ProjectPermission.php @@ -219,7 +219,7 @@ class ProjectPermission extends Base ->table(self::TABLE) ->eq('project_id', $project_id) ->eq('user_id', $user_id) - ->count() === 1; + ->exists(); } /** @@ -237,7 +237,7 @@ class ProjectPermission extends Base ->eq('project_id', $project_id) ->eq('user_id', $user_id) ->eq('is_owner', 1) - ->count() === 1; + ->exists(); } /** @@ -266,7 +266,7 @@ class ProjectPermission extends Base ->table(Project::TABLE) ->eq('id', $project_id) ->eq('is_everybody_allowed', 1) - ->count() === 1; + ->exists(); } /** diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php index b9cc6ded..d8a44aff 100644 --- a/app/Model/Subtask.php +++ b/app/Model/Subtask.php @@ -390,7 +390,7 @@ class Subtask extends Base $this->db->table(self::TABLE) ->eq('status', self::STATUS_INPROGRESS) ->eq('user_id', $user_id) - ->count() === 1; + ->exists(); } /** diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index ead60b4b..9c46f4a9 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -349,6 +349,6 @@ class TaskFinder extends Base */ public function exists($task_id) { - return $this->db->table(Task::TABLE)->eq('id', $task_id)->count() === 1; + return $this->db->table(Task::TABLE)->eq('id', $task_id)->exists(); } } diff --git a/app/Model/TaskLink.php b/app/Model/TaskLink.php index e079a664..a89e6ca3 100644 --- a/app/Model/TaskLink.php +++ b/app/Model/TaskLink.php @@ -133,7 +133,7 @@ class TaskLink extends Base 'link_id' => $link_id, )); - $task_link_id = $this->db->getConnection()->getLastId(); + $task_link_id = $this->db->getLastId(); // Create the opposite task link $this->db->table(self::TABLE)->insert(array( diff --git a/app/Model/User.php b/app/Model/User.php index 726978e5..63b49367 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -38,7 +38,7 @@ class User extends Base */ public function exists($user_id) { - return $this->db->table(self::TABLE)->eq('id', $user_id)->count() === 1; + return $this->db->table(self::TABLE)->eq('id', $user_id)->exists(); } /** diff --git a/app/ServiceProvider/DatabaseProvider.php b/app/ServiceProvider/DatabaseProvider.php index e6a75a4e..8300fe7d 100644 --- a/app/ServiceProvider/DatabaseProvider.php +++ b/app/ServiceProvider/DatabaseProvider.php @@ -12,7 +12,7 @@ class DatabaseProvider implements ServiceProviderInterface { $container['db'] = $this->getInstance(); $container['db']->stopwatch = DEBUG; - $container['db']->log_queries = DEBUG; + $container['db']->logQueries = DEBUG; } /** diff --git a/composer.lock b/composer.lock index 30831d9f..77b15f62 100644 --- a/composer.lock +++ b/composer.lock @@ -301,12 +301,12 @@ "source": { "type": "git", "url": "https://github.com/fguillot/picoDb.git", - "reference": "dd08649713c9d8330b3c5fa23220a11cb5da3e79" + "reference": "6d9e2314a7aa2893a49c7da7b66f7352bd6ea296" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fguillot/picoDb/zipball/dd08649713c9d8330b3c5fa23220a11cb5da3e79", - "reference": "dd08649713c9d8330b3c5fa23220a11cb5da3e79", + "url": "https://api.github.com/repos/fguillot/picoDb/zipball/6d9e2314a7aa2893a49c7da7b66f7352bd6ea296", + "reference": "6d9e2314a7aa2893a49c7da7b66f7352bd6ea296", "shasum": "" }, "require": { @@ -330,7 +330,7 @@ ], "description": "Minimalist database query builder", "homepage": "https://github.com/fguillot/picoDb", - "time": "2015-06-24 21:58:15" + "time": "2015-06-27 16:13:40" }, { "name": "fguillot/simple-validator", diff --git a/docs/closing-tasks.markdown b/docs/closing-tasks.markdown new file mode 100644 index 00000000..7462b683 --- /dev/null +++ b/docs/closing-tasks.markdown @@ -0,0 +1,14 @@ +Closing tasks +============= + +When a task is closed, they are hidden from the board. However, you can always access to the list of closed tasks from the board menu **Board > Actions > Completed tasks**. + +There are two different way to close a task, from the task dropdown menu on the board: + +![Close a task from dropdown menu](http://kanboard.net/screenshots/documentation/menu-close-task.png) + +Or from the task sidebar menu in the task detail view: + +![Close a task](http://kanboard.net/screenshots/documentation/closing-tasks.png) + +Note: When you close a task, all subtasks not completed will be changed to the status "Done".
\ No newline at end of file diff --git a/docs/index.markdown b/docs/index.markdown index 75df4694..6eb6e6ef 100644 --- a/docs/index.markdown +++ b/docs/index.markdown @@ -25,16 +25,19 @@ Using Kanboard ### Working with tasks - [Creating tasks](creating-tasks.markdown) +- [Closing tasks](closing-tasks.markdown) - [Adding screenshots](screenshots.markdown) - [Task links](task-links.markdown) - [Transitions](transitions.markdown) - [Time tracking](time-tracking.markdown) - [Recurring tasks](recurring-tasks.markdown) - [Create tasks by email](create-tasks-by-email.markdown) +- [Subtasks](subtasks.markdown) ### Working with users - [User management](user-management.markdown) +- [Notifications](notifications.markdown) - [Hourly rate](hourly-rate.markdown) - [Timetable](timetable.markdown) - [Two factor authentication](2fa.markdown) @@ -62,6 +65,7 @@ Using Kanboard - [Slack](slack.markdown) - [Postmark](postmark.markdown) - [iCalendar subscriptions](ical.markdown) +- [RSS/Atom subscriptions](rss.markdown) - [Json-RPC API](api-json-rpc.markdown) - [Webhooks](webhooks.markdown) diff --git a/docs/notifications.markdown b/docs/notifications.markdown new file mode 100644 index 00000000..f42c66cb --- /dev/null +++ b/docs/notifications.markdown @@ -0,0 +1,30 @@ +Notifications +============= + +Kanboard is able to send notifications through several channels: + +- Email +- Jabber/XMPP +- Hipchat +- Slack + +Actually, Jabber/Hipchat/Slack notifications are sent to a room or group channel because they are configured at the project level. +However, email notifications are sent to an individual. + +User notifications +------------------ + +Each user must enable the notifications in their profile: **User Profile > Email notifications**. It's disabled by default. + +You need of course a valid email address in you profile and the application must be configured to send emails. + +![Notifications](http://kanboard.net/screenshots/documentation/notifications.png) + +For each project your are member, you can choose to receive notifications for: + +- All tasks +- Only for tasks assigned to you +- Only for tasks created by you +- Only for tasks created by you and assigned to you + +You can also select only some projects, by default it's all projects where you are member. diff --git a/docs/project-configuration.markdown b/docs/project-configuration.markdown index 5cfa4974..d234eca7 100644 --- a/docs/project-configuration.markdown +++ b/docs/project-configuration.markdown @@ -29,11 +29,3 @@ If another subtask have the status "in progress", the user will see this dialog ![Subtask user restriction](http://kanboard.net/screenshots/documentation/subtask-user-restriction.png) -### Enable time tracking for subtasks - -When this option is enabled, each time the status of a subtask is changed, the start time and the end time are recorded in the database for the assignee. - -- When the status changes to "in progress" then the start time is saved -- When the status changes to "done" then the end time is saved - -The time spent for the subtask and the task is also updated. diff --git a/docs/rss.markdown b/docs/rss.markdown new file mode 100644 index 00000000..57c81020 --- /dev/null +++ b/docs/rss.markdown @@ -0,0 +1,23 @@ +RSS/Atom subscriptions +====================== + +Kanboard supports RSS feeds for projects and users. + +- Project feeds contains only the activity the project +- User feeds contains the activity stream of all projects the user is member + +Those subscriptions are only activated when the public access is enabled in the user profile or in the project settings. + +Enable/disable project RSS feeds +-------------------------------- + +Go to **Project settings > Public access**. + +![Disable public access](http://kanboard.net/screenshots/documentation/project-disable-sharing.png) + +Enable/disable user RSS feeds +-------------------------------- + +Go to **User profile > Public access**. + +The RSS link is protected by a random token, only people who knows the url can access to the feed.
\ No newline at end of file diff --git a/docs/subtasks.markdown b/docs/subtasks.markdown new file mode 100644 index 00000000..6ea67284 --- /dev/null +++ b/docs/subtasks.markdown @@ -0,0 +1,47 @@ +Subtasks +======== + +Subtasks are useful to split the work of a task. + +Each subtask: + +- Can be assigned to a project member +- Have 3 different statuses: **Todo**, **In progress**, **Done** +- Have time tracking information: **time spent** and **time estimated** +- Be ordered by position + +Creating subtasks +----------------- + +From the task view, on left sidebar click on **Add a subtask**: + +![Add a subtask](http://kanboard.net/screenshots/documentation/add-subtask.png) + +You can also add quickly a subtask by entering only the title: + +![Add a subtask from the task view](http://kanboard.net/screenshots/documentation/add-subtask-shortcut.png) + +Change subtask status +--------------------- + +When you click on the subtask title the status change: + +![Subtask in progress](http://kanboard.net/screenshots/documentation/subtask-status-inprogress.png) + +The icon before the title is updated according to the status. + +![Subtask done](http://kanboard.net/screenshots/documentation/subtask-status-done.png) + +Note: When the task is closed, all subtasks are changed to the status **Done**. + +Subtask timer +------------- + +Each time a subtask is in progress, the timer is also started. The timer can be started and stopped at any time. + +The timer records the time spent on the subtask automatically. You can also change manually the value of the time spent field when you edit a subtask. + +The time calculated is rounded to the nearest quarter. This information is recorded in a separate table. + +The task time spent is updated automatically according to the sum of all subtasks time spent. + diff --git a/docs/time-tracking.markdown b/docs/time-tracking.markdown index 575a0094..8c65b16a 100644 --- a/docs/time-tracking.markdown +++ b/docs/time-tracking.markdown @@ -21,18 +21,23 @@ Subtask time tracking ![Subtask time tracking](http://kanboard.net/screenshots/documentation/subtask-time-tracking.png) Subtasks also have the fields "time spent" and "time estimated". -However, when you set a value for those fields, **the task time tracking values becomes the sum of all subtask values**. -User time tracking ------------------- +When you change the value of these fields, **the task time tracking values are updated automatically and becomes the sum of all subtask values**. + +Kanboard records the time between each subtask status change in a separate table. + +- Changing subtask status from **todo** to **in pogress** logs the start time +- Changing subtask status from **in progress** to **done** logs the end time but also update the time spent of the subtask and the task + +The breakdown of all records is visible in the task view page: -In the board settings, you can enable subtasks time tracking for users. +![Task timesheet](http://kanboard.net/screenshots/documentation/task-timesheet.png) -Each time a subtask status change, the start time and the end time are saved in a seperate table automatically. -The time spent is automatically calculated for tasks and subtasks when the subtask is completed. +For each subtask, the timer can be stopped/started at any time: -- Changing subtask status from "todo" to "in pogress" logs the start time -- Changing subtask status from "in progress" to "done" logs the end time but also update the time spent of the subtask and the task +![Subtask timer](http://kanboard.net/screenshots/documentation/subtask-timer.png) -The breakdown by user is also visible in the tasks details: -I
\ No newline at end of file +- The timer doesn't depends of the subtask status +- Each time you start the timer a new record is created in the time tracking table +- Each time you stop the clock the end date is recorded in the time tracking table +- The calculated time spent is rounded to the nearest quarter diff --git a/tests/units/Base.php b/tests/units/Base.php index 1bbd62f8..7ad4e626 100644 --- a/tests/units/Base.php +++ b/tests/units/Base.php @@ -80,7 +80,7 @@ abstract class Base extends PHPUnit_Framework_TestCase new Stopwatch ); - $this->container['db']->log_queries = true; + $this->container['db']->logQueries = true; $this->container['logger'] = new Logger; $this->container['logger']->setLogger(new File('/dev/null')); |