summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Controller/Base.php2
-rw-r--r--app/Model/Action.php4
-rw-r--r--app/Model/Base.php2
-rw-r--r--app/Model/Category.php2
-rw-r--r--app/Model/Currency.php2
-rw-r--r--app/Model/File.php2
-rw-r--r--app/Model/Link.php4
-rw-r--r--app/Model/Project.php6
-rw-r--r--app/Model/ProjectDuplication.php2
-rw-r--r--app/Model/ProjectIntegration.php4
-rw-r--r--app/Model/ProjectPermission.php6
-rw-r--r--app/Model/Subtask.php2
-rw-r--r--app/Model/TaskFinder.php2
-rw-r--r--app/Model/TaskLink.php2
-rw-r--r--app/Model/User.php2
-rw-r--r--app/ServiceProvider/DatabaseProvider.php2
-rw-r--r--composer.lock8
-rw-r--r--docs/closing-tasks.markdown14
-rw-r--r--docs/index.markdown4
-rw-r--r--docs/notifications.markdown30
-rw-r--r--docs/project-configuration.markdown8
-rw-r--r--docs/rss.markdown23
-rw-r--r--docs/subtasks.markdown47
-rw-r--r--docs/time-tracking.markdown25
-rw-r--r--tests/units/Base.php2
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'));