summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile18
-rw-r--r--README.markdown6
-rw-r--r--app/Controller/App.php18
-rw-r--r--app/Controller/Project.php4
-rw-r--r--app/Controller/User.php2
-rw-r--r--app/Locales/de_DE/translations.php6
-rw-r--r--app/Locales/es_ES/translations.php6
-rw-r--r--app/Locales/fi_FI/translations.php6
-rw-r--r--app/Locales/fr_FR/translations.php6
-rw-r--r--app/Locales/it_IT/translations.php6
-rw-r--r--app/Locales/pl_PL/translations.php6
-rw-r--r--app/Locales/pt_BR/translations.php6
-rw-r--r--app/Locales/ru_RU/translations.php120
-rw-r--r--app/Locales/sv_SE/translations.php6
-rw-r--r--app/Locales/zh_CN/translations.php6
-rw-r--r--app/Model/ProjectActivity.php21
-rw-r--r--app/Model/TaskFinder.php27
-rw-r--r--app/Templates/app_index.php45
-rw-r--r--app/Templates/layout.php3
-rw-r--r--app/Templates/project_activity.php21
-rw-r--r--app/Templates/project_events.php21
-rw-r--r--app/Templates/task_table.php2
-rw-r--r--app/helpers.php4
-rw-r--r--assets/css/app.css16
-rw-r--r--assets/js/app.js36
-rw-r--r--docs/contributing.markdown69
-rw-r--r--docs/docker.markdown48
-rw-r--r--docs/kanban-vs-todo-and-scrum.markdown37
-rw-r--r--docs/what-is-kanban.markdown32
-rw-r--r--tests/units/ProjectActivityTest.php6
30 files changed, 506 insertions, 104 deletions
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..29d0e9c8
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,18 @@
+FROM ubuntu:14.04
+MAINTAINER Frederic Guillot <fred@kanboard.net>
+
+RUN apt-get update && apt-get install -y apache2 php5 php5-sqlite git && apt-get clean
+RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
+RUN cd /var/www && git clone https://github.com/fguillot/kanboard.git
+RUN rm -rf /var/www/html && mv /var/www/kanboard /var/www/html
+RUN chown -R www-data:www-data /var/www/html/data
+
+EXPOSE 80
+
+ENV APACHE_RUN_USER www-data
+ENV APACHE_RUN_GROUP www-data
+ENV APACHE_LOG_DIR /var/log/apache2
+ENV APACHE_LOCK_DIR /var/lock/apache2
+ENV APACHE_PID_FILE /var/run/apache2.pid
+
+CMD /usr/sbin/apache2ctl -D FOREGROUND
diff --git a/README.markdown b/README.markdown
index 41cc3821..16c32a2a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -50,6 +50,8 @@ Documentation
#### Introduction
+- [What is Kanban?](docs/what-is-kanban.markdown)
+- [Kanban vs Todo Lists and Scrum](docs/kanban-vs-todo-and-scrum.markdown)
- [Usage examples](docs/usage-examples.markdown)
#### Working with projects
@@ -103,10 +105,12 @@ Documentation
- [Command line interface](docs/cli.markdown)
- [Json-RPC API](docs/api-json-rpc.markdown)
- [Webhooks](docs/webhooks.markdown)
-- [How to use Kanboard with Vagrant](docs/vagrant.markdown)
+- [Run Kanboard with Vagrant](docs/vagrant.markdown)
+- [Run Kanboard with Docker](docs/docker.markdown)
### Contributors
+- [Contributor guide](docs/contributing.markdown)
- [Translations](docs/translations.markdown)
- [Coding standards](docs/coding-standards.markdown)
- [Running tests](docs/tests.markdown)
diff --git a/app/Controller/App.php b/app/Controller/App.php
index b7f79b1d..feec4221 100644
--- a/app/Controller/App.php
+++ b/app/Controller/App.php
@@ -13,17 +13,21 @@ use Model\Project as ProjectModel;
class App extends Base
{
/**
- * Redirect to the project creation page or the board controller
+ * Dashboard for the current user
*
* @access public
*/
public function index()
{
- if ($this->project->countByStatus(ProjectModel::ACTIVE)) {
- $this->response->redirect('?controller=board');
- }
- else {
- $this->redirectNoProject();
- }
+ $user_id = $this->acl->getUserId();
+ $projects = $this->projectPermission->getAllowedProjects($user_id);
+
+ $this->response->html($this->template->layout('app_index', array(
+ 'board_selector' => $projects,
+ 'events' => $this->projectActivity->getProjects(array_keys($projects), 10),
+ 'tasks' => $this->taskFinder->getAllTasksByUser($user_id),
+ 'menu' => 'dashboard',
+ 'title' => t('Dashboard'),
+ )));
}
}
diff --git a/app/Controller/Project.php b/app/Controller/Project.php
index 1fac3ffb..503eb3a5 100644
--- a/app/Controller/Project.php
+++ b/app/Controller/Project.php
@@ -379,7 +379,7 @@ class Project extends Base
$project = $this->getProject();
$this->response->html($this->template->layout('project_activity', array(
- 'events' => $this->projectActivity->getAll($project['id']),
+ 'events' => $this->projectActivity->getProject($project['id']),
'menu' => 'projects',
'project' => $project,
'title' => t('%s\'s activity', $project['name'])
@@ -427,6 +427,7 @@ class Project extends Base
'project_id' => $project['id'],
),
'project' => $project,
+ 'menu' => 'projects',
'columns' => $this->board->getColumnsList($project['id']),
'categories' => $this->category->getList($project['id'], false),
'title' => $project['name'].($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
@@ -461,6 +462,7 @@ class Project extends Base
'limit' => $limit,
),
'project' => $project,
+ 'menu' => 'projects',
'columns' => $this->board->getColumnsList($project['id']),
'categories' => $this->category->getList($project['id'], false),
'tasks' => $tasks,
diff --git a/app/Controller/User.php b/app/Controller/User.php
index bbed9f6f..834b2379 100644
--- a/app/Controller/User.php
+++ b/app/Controller/User.php
@@ -59,7 +59,7 @@ class User extends Base
$this->response->redirect('?'.$redirect_query);
}
else {
- $this->response->redirect('?controller=board');
+ $this->response->redirect('?controller=app');
}
}
diff --git a/app/Locales/de_DE/translations.php b/app/Locales/de_DE/translations.php
index 4b901148..e8f4f53f 100644
--- a/app/Locales/de_DE/translations.php
+++ b/app/Locales/de_DE/translations.php
@@ -449,6 +449,7 @@ return array(
// 'Email:' => '',
// 'Default project:' => '',
// 'Notifications:' => '',
+ // 'Notifications' => '',
// 'Group:' => '',
// 'Regular user' => '',
// 'Account type:' => '',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Locales/es_ES/translations.php b/app/Locales/es_ES/translations.php
index 0cdd5f32..ffaeaf2d 100644
--- a/app/Locales/es_ES/translations.php
+++ b/app/Locales/es_ES/translations.php
@@ -449,6 +449,7 @@ return array(
'Email:' => 'Correo electrónico:',
'Default project:' => 'Proyecto por defecto:',
'Notifications:' => 'Notificaciones:',
+ // 'Notifications' => '',
'Group:' => 'Grupo:',
'Regular user' => 'Usuario regular:',
'Account type:' => 'Tipo de Cuenta:',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Locales/fi_FI/translations.php b/app/Locales/fi_FI/translations.php
index 5d05e92d..1c1b3439 100644
--- a/app/Locales/fi_FI/translations.php
+++ b/app/Locales/fi_FI/translations.php
@@ -449,6 +449,7 @@ return array(
// 'Email:' => '',
// 'Default project:' => '',
// 'Notifications:' => '',
+ // 'Notifications' => '',
// 'Group:' => '',
// 'Regular user' => '',
// 'Account type:' => '',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Locales/fr_FR/translations.php b/app/Locales/fr_FR/translations.php
index f4bffc3b..5809ef69 100644
--- a/app/Locales/fr_FR/translations.php
+++ b/app/Locales/fr_FR/translations.php
@@ -449,6 +449,7 @@ return array(
'Email:' => 'Email :',
'Default project:' => 'Projet par défaut :',
'Notifications:' => 'Notifications :',
+ 'Notifications' => 'Notifications',
'Group:' => 'Groupe :',
'Regular user' => 'Utilisateur normal',
'Account type:' => 'Type de compte :',
@@ -543,4 +544,9 @@ return array(
'Started on %B %e, %Y' => 'Commençé le %d/%m/%Y',
'Start date' => 'Date de début',
'Time estimated' => 'Temps estimé',
+ 'There is nothing assigned to you.' => 'Aucune tâche assignée pour vous.',
+ 'My tasks' => 'Mes tâches',
+ 'Activity stream' => 'Flux d\'activité',
+ 'Dashboard' => 'Tableau de bord',
+ 'Confirmation' => 'Confirmation',
);
diff --git a/app/Locales/it_IT/translations.php b/app/Locales/it_IT/translations.php
index 5854d033..d933d5c7 100644
--- a/app/Locales/it_IT/translations.php
+++ b/app/Locales/it_IT/translations.php
@@ -449,6 +449,7 @@ return array(
// 'Email:' => '',
// 'Default project:' => '',
// 'Notifications:' => '',
+ // 'Notifications' => '',
// 'Group:' => '',
// 'Regular user' => '',
// 'Account type:' => '',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Locales/pl_PL/translations.php b/app/Locales/pl_PL/translations.php
index fe76b34c..6f6732d6 100644
--- a/app/Locales/pl_PL/translations.php
+++ b/app/Locales/pl_PL/translations.php
@@ -449,6 +449,7 @@ return array(
// 'Email:' => '',
// 'Default project:' => '',
// 'Notifications:' => '',
+ // 'Notifications' => '',
// 'Group:' => '',
// 'Regular user' => '',
// 'Account type:' => '',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Locales/pt_BR/translations.php b/app/Locales/pt_BR/translations.php
index 59fcc983..0a5b2e28 100644
--- a/app/Locales/pt_BR/translations.php
+++ b/app/Locales/pt_BR/translations.php
@@ -449,6 +449,7 @@ return array(
// 'Email:' => '',
// 'Default project:' => '',
// 'Notifications:' => '',
+ // 'Notifications' => '',
// 'Group:' => '',
// 'Regular user' => '',
// 'Account type:' => '',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Locales/ru_RU/translations.php b/app/Locales/ru_RU/translations.php
index 1f5bfea2..dc9dc9ba 100644
--- a/app/Locales/ru_RU/translations.php
+++ b/app/Locales/ru_RU/translations.php
@@ -164,7 +164,7 @@ return array(
'Ready' => 'Готовые',
'Backlog' => 'Ожидающие',
'Work in progress' => 'В процессе',
- 'Done' => 'Завершенные',
+ 'Done' => 'Выполнена',
'Application version:' => 'Версия приложения :',
'Completed on %B %e, %Y at %k:%M %p' => 'Завершен %d/%m/%Y в %H:%M',
'%B %e, %Y at %k:%M %p' => '%d/%m/%Y в %H:%M',
@@ -192,11 +192,11 @@ return array(
'Edit users access' => 'Изменить доступ пользователей',
'Allow this user' => 'Разрешить этого пользователя',
'Only those users have access to this project:' => 'Только эти пользователи имеют доступ к проекту :',
- 'Don\'t forget that administrators have access to everything.' => 'Помните, администратор имеет доступ всюду.',
+ 'Don\'t forget that administrators have access to everything.' => 'Помните, администратор имеет доступ ко всему.',
'revoke' => 'отозвать',
'List of authorized users' => 'Список авторизованных пользователей',
'User' => 'Пользователь',
- // 'Nobody have access to this project.' => '',
+ 'Nobody have access to this project.' => 'Ни у кого нет доступа к этому проекту',
'You are not allowed to access to this project.' => 'Вам запрешен доступ к этому проекту.',
'Comments' => 'Комментарии',
'Post comment' => 'Оставить комментарий',
@@ -208,7 +208,7 @@ return array(
'Unable to create your comment.' => 'Невозможно создать комментарий.',
'The description is required' => 'Требуется описание',
'Edit this task' => 'Изменить задачу',
- 'Due Date' => 'Срок',
+ 'Due Date' => 'Сделать до',
'Invalid date' => 'Неверная дата',
'Must be done before %B %e, %Y' => 'Должно быть сделано до %d/%m/%Y',
'%B %e, %Y' => '%d/%m/%Y',
@@ -249,7 +249,7 @@ return array(
'Move Down' => 'Сдвинуть вниз',
'Duplicate to another project' => 'Клонировать в другой проект',
'Duplicate' => 'Клонировать',
- 'link' => 'связь',
+ 'link' => 'ссылка',
'Update this comment' => 'Обновить комментарий',
'Comment updated successfully.' => 'Комментарий обновлен.',
'Unable to update your comment.' => 'Не удалось обновить ваш комментарий.',
@@ -349,13 +349,13 @@ return array(
'estimated' => 'расчетное',
'Sub-Tasks' => 'Подзадачи',
'Add a sub-task' => 'Добавить подзадачу',
- 'Original estimate' => 'Начальная оценка',
+ 'Original estimate' => 'Первичная оценка',
'Create another sub-task' => 'Создать другую подзадачу',
'Time spent' => 'Времени затрачено',
'Edit a sub-task' => 'Изменить подзадачу',
'Remove a sub-task' => 'Удалить подзадачу',
'The time must be a numeric value' => 'Время должно быть числом!',
- 'Todo' => 'TODO',
+ 'Todo' => 'К исполнению',
'In progress' => 'В процессе',
'Sub-task removed successfully.' => 'Подзадача удалена.',
'Unable to remove this sub-task.' => 'Не удалось удалить подзадачу.',
@@ -449,10 +449,11 @@ return array(
'Email:' => 'Email:',
'Default project:' => 'Проект по умолчанию:',
'Notifications:' => 'Уведомления:',
+ 'Notifications' => 'Уведомления',
'Group:' => 'Группа:',
'Regular user' => 'Обычный пользователь',
'Account type:' => 'Тип профиля:',
- 'Edit profile' => 'Редактировать профиль:',
+ 'Edit profile' => 'Редактировать профиль',
'Change password' => 'Сменить пароль',
'Password modification' => 'Изменение пароля',
'External authentications' => 'Внешняя аутентификация',
@@ -494,53 +495,58 @@ return array(
'Activity' => 'Активность',
'Default values are "%s"' => 'Колонки по умолчанию: "%s"',
'Default columns for new projects (Comma-separated)' => 'Колонки по умолчанию для новых проектов (разделять запятой)',
- // 'Task assignee change' => '',
- // '%s change the assignee of the task #%d to %s' => '',
- // '%s change the assignee of the task <a href="?controller=task&amp;action=show&amp;task_id=%d">#%d</a> to %s' => '',
- // '[%s][Column Change] %s (#%d)' => '',
- // '[%s][Position Change] %s (#%d)' => '',
- // '[%s][Assignee Change] %s (#%d)' => '',
- // 'New password for the user "%s"' => '',
- // 'Choose an event' => '',
- // 'Github commit received' => '',
- // 'Github issue opened' => '',
- // 'Github issue closed' => '',
- // 'Github issue reopened' => '',
- // 'Github issue assignee change' => '',
- // 'Github issue label change' => '',
- // 'Create a task from an external provider' => '',
- // 'Change the assignee based on an external username' => '',
- // 'Change the category based on an external label' => '',
- // 'Reference' => '',
- // 'Reference: %s' => '',
- // 'Label' => '',
- // 'Database' => '',
- // 'About' => '',
- // 'Database driver:' => '',
- // 'Board settings' => '',
- // 'URL and token' => '',
- // 'Webhook settings' => '',
- // 'URL for task creation:' => '',
- // 'Reset token' => '',
- // 'API endpoint:' => '',
- // 'Refresh interval for private board' => '',
- // 'Refresh interval for public board' => '',
- // 'Task highlight period' => '',
- // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '',
- // 'Frequency in second (60 seconds by default)' => '',
- // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '',
- // 'Application URL' => '',
- // 'Example: http://example.kanboard.net/ (used by email notifications)' => '',
- // 'Token regenerated.' => '',
- // 'Date format' => '',
- // 'ISO format is always accepted, example: "%s" and "%s"' => '',
- // 'New private project' => '',
- // 'This project is private' => '',
- // 'Type here to create a new sub-task' => '',
- // 'Add' => '',
- // 'Estimated time: %s hours' => '',
- // 'Time spent: %s hours' => '',
- // 'Started on %B %e, %Y' => '',
- // 'Start date' => '',
- // 'Time estimated' => '',
+ 'Task assignee change' => 'Изменен назначенный',
+ '%s change the assignee of the task #%d to %s' => '%s сменил назначенного для задачи #%d на %s',
+ '%s change the assignee of the task <a href="?controller=task&amp;action=show&amp;task_id=%d">#%d</a> to %s' => '%s сменил назначенного для задачи <a href="?controller=task&amp;action=show&amp;task_id=%d">#%d</a> на %s',
+ '[%s][Column Change] %s (#%d)' => '[%s][Изменение колонки] %s (#%d)',
+ '[%s][Position Change] %s (#%d)' => '[%s][Изменение позиции] %s (#%d)',
+ '[%s][Assignee Change] %s (#%d)' => '[%s][Изменение назначеного] %s (#%d)',
+ 'New password for the user "%s"' => 'Новый пароль для пользователя %s"',
+ 'Choose an event' => 'Выберите событие',
+ 'Github commit received' => 'Github: коммит получен',
+ 'Github issue opened' => 'Github: новая проблема',
+ 'Github issue closed' => 'Github: проблема закрыта',
+ 'Github issue reopened' => 'Github: проблема переоткрыта',
+ 'Github issue assignee change' => 'Github: сменить ответственного за проблему',
+ 'Github issue label change' => 'Github: ярлык проблемы изменен',
+ 'Create a task from an external provider' => 'Создать задачу из внешнего источника',
+ 'Change the assignee based on an external username' => 'Изменить назначенного основываясь на внешнем имени пользователя',
+ 'Change the category based on an external label' => 'Изменить категорию основываясь на внешнем ярлыке',
+ 'Reference' => 'Ссылка',
+ 'Reference: %s' => 'Ссылка: %s',
+ 'Label' => 'Ярлык',
+ 'Database' => 'База данных',
+ 'About' => 'Информация',
+ 'Database driver:' => 'Драйвер базы данных',
+ 'Board settings' => 'Настройки доски',
+ 'URL and token' => 'URL и токен',
+ 'Webhook settings' => 'Параметры Webhook',
+ 'URL for task creation:' => 'URL для создания задачи:',
+ 'Reset token' => 'Перезагрузить токен',
+ 'API endpoint:' => 'API endpoint:',
+ 'Refresh interval for private board' => 'Период обновления для частных досок',
+ 'Refresh interval for public board' => 'Период обновления для публичных досок',
+ 'Task highlight period' => 'Время подсвечивания задачи',
+ 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Период (в секундах) в течении которого задача считается недавно измененной (0 для выключения, 2 дня по умолчанию)',
+ 'Frequency in second (60 seconds by default)' => 'Частота в секундах (60 секунд по умолчанию)',
+ 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Частота в секундах (0 для выключения, 10 секунд по умолчанию)',
+ 'Application URL' => 'URL приложения',
+ 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Пример: http://example.kanboard.net (используется в email уведомлениях)',
+ 'Token regenerated.' => 'Токен пересоздан',
+ 'Date format' => 'Формат даты',
+ 'ISO format is always accepted, example: "%s" and "%s"' => 'Время должно быть в ISO-формате, например: "%s" или "%s"',
+ 'New private project' => 'Новый проект с ограниченным доступом',
+ 'This project is private' => 'Это проект с ограниченным доступом',
+ 'Type here to create a new sub-task' => 'Печатайте сюда чтобы создать подзадачу',
+ 'Add' => 'Добавить',
+ 'Estimated time: %s hours' => 'Планируемое время: %s часов',
+ 'Time spent: %s hours' => 'Потрачено времени: %s часов',
+ 'Started on %B %e, %Y' => 'Начато %B %e, %Y',
+ 'Start date' => 'Дата начала',
+ 'Time estimated' => 'Планируемое время',
+ 'There is nothing assigned to you.' => 'Вам ничего не назначено',
+ 'My tasks' => 'Мои задачи',
+ 'Activity stream' => 'Текущая активность',
+ 'Dashboard' => 'Инфопанель',
+ 'Confirmation' => 'Подтверждение пароля',
);
diff --git a/app/Locales/sv_SE/translations.php b/app/Locales/sv_SE/translations.php
index 20b685ef..d0f179f8 100644
--- a/app/Locales/sv_SE/translations.php
+++ b/app/Locales/sv_SE/translations.php
@@ -449,6 +449,7 @@ return array(
// 'Email:' => '',
// 'Default project:' => '',
// 'Notifications:' => '',
+ // 'Notifications' => '',
// 'Group:' => '',
// 'Regular user' => '',
// 'Account type:' => '',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Locales/zh_CN/translations.php b/app/Locales/zh_CN/translations.php
index 92f46ef9..4b2d57ed 100644
--- a/app/Locales/zh_CN/translations.php
+++ b/app/Locales/zh_CN/translations.php
@@ -449,6 +449,7 @@ return array(
// 'Email:' => '',
// 'Default project:' => '',
// 'Notifications:' => '',
+ // 'Notifications' => '',
// 'Group:' => '',
// 'Regular user' => '',
// 'Account type:' => '',
@@ -543,4 +544,9 @@ return array(
// 'Started on %B %e, %Y' => '',
// 'Start date' => '',
// 'Time estimated' => '',
+ // 'There is nothing assigned to you.' => '',
+ // 'My tasks' => '',
+ // 'Activity stream' => '',
+ // 'Dashboard' => '',
+ // 'Confirmation' => '',
);
diff --git a/app/Model/ProjectActivity.php b/app/Model/ProjectActivity.php
index d2457609..6d6ef454 100644
--- a/app/Model/ProjectActivity.php
+++ b/app/Model/ProjectActivity.php
@@ -61,15 +61,32 @@ class ProjectActivity extends Base
* @param integer $limit Maximum events number
* @return array
*/
- public function getAll($project_id, $limit = 50)
+ public function getProject($project_id, $limit = 50)
{
+ return $this->getProjects(array($project_id), $limit);
+ }
+
+ /**
+ * Get all events for the given projects list
+ *
+ * @access public
+ * @param integer $project_id Project id
+ * @param integer $limit Maximum events number
+ * @return array
+ */
+ public function getProjects(array $projects, $limit = 50)
+ {
+ if (empty($projects)) {
+ return array();
+ }
+
$events = $this->db->table(self::TABLE)
->columns(
self::TABLE.'.*',
User::TABLE.'.username AS author_username',
User::TABLE.'.name AS author_name'
)
- ->eq('project_id', $project_id)
+ ->in('project_id', $projects)
->join(User::TABLE, 'id', 'creator_id')
->desc('id')
->limit($limit)
diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php
index 5bf8c139..56795152 100644
--- a/app/Model/TaskFinder.php
+++ b/app/Model/TaskFinder.php
@@ -112,6 +112,33 @@ class TaskFinder extends Base
}
/**
+ * Get all open tasks for a given user
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return array
+ */
+ public function getAllTasksByUser($user_id)
+ {
+ return $this->db
+ ->table(Task::TABLE)
+ ->columns(
+ 'tasks.id',
+ 'tasks.title',
+ 'tasks.date_due',
+ 'tasks.date_creation',
+ 'tasks.project_id',
+ 'tasks.color_id',
+ 'projects.name AS project_name'
+ )
+ ->join(Project::TABLE, 'id', 'project_id')
+ ->eq('tasks.owner_id', $user_id)
+ ->eq('tasks.is_active', Task::STATUS_OPEN)
+ ->asc('tasks.id')
+ ->findAll();
+ }
+
+ /**
* Get all tasks for a given project and status
*
* @access public
diff --git a/app/Templates/app_index.php b/app/Templates/app_index.php
new file mode 100644
index 00000000..91eecce4
--- /dev/null
+++ b/app/Templates/app_index.php
@@ -0,0 +1,45 @@
+<section id="main">
+ <div class="page-header">
+ <h2><?= t('Dashboard') ?></h2>
+ </div>
+ <section id="dashboard">
+ <div class="dashboard-left-column">
+ <h2><?= t('My tasks') ?></h2>
+ <?php if (empty($tasks)): ?>
+ <p class="alert"><?= t('There is nothing assigned to you.') ?></p>
+ <?php else: ?>
+ <table>
+ <tr>
+ <th>&nbsp;</th>
+ <th width="15%"><?= t('Project') ?></th>
+ <th width="40%"><?= t('Title') ?></th>
+ <th><?= t('Due date') ?></th>
+ <th><?= t('Date created') ?></th>
+ </tr>
+ <?php foreach ($tasks as $task): ?>
+ <tr>
+ <td class="task-table task-<?= $task['color_id'] ?>">
+ <?= Helper\a('#'.$task['id'], 'task', 'show', array('task_id' => $task['id'])) ?>
+ </td>
+ <td>
+ <?= Helper\a(Helper\escape($task['project_name']), 'board', 'show', array('project_id' => $task['project_id'])) ?>
+ </td>
+ <td>
+ <?= Helper\a(Helper\escape($task['title']), 'task', 'show', array('task_id' => $task['id'])) ?>
+ </td>
+ <td>
+ <?= dt('%B %e, %Y', $task['date_due']) ?>
+ </td>
+ <td>
+ <?= dt('%B %e, %Y', $task['date_creation']) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+ <?php endif ?>
+ </div>
+ <div class="dashboard-right-column">
+ <h2><?= t('Activity stream') ?></h2>
+ <?= Helper\template('project_events', array('events' => $events)) ?>
+ </section>
+</section> \ No newline at end of file
diff --git a/app/Templates/layout.php b/app/Templates/layout.php
index 434c5aca..a86d613b 100644
--- a/app/Templates/layout.php
+++ b/app/Templates/layout.php
@@ -50,6 +50,9 @@
</select>
</li>
<?php endif ?>
+ <li <?= isset($menu) && $menu === 'dashboard' ? 'class="active"' : '' ?>>
+ <a href="?controller=app"><?= t('Dashboard') ?></a>
+ </li>
<li <?= isset($menu) && $menu === 'boards' ? 'class="active"' : '' ?>>
<a href="?controller=board"><?= t('Boards') ?></a>
</li>
diff --git a/app/Templates/project_activity.php b/app/Templates/project_activity.php
index 50743d68..d07ba86a 100644
--- a/app/Templates/project_activity.php
+++ b/app/Templates/project_activity.php
@@ -9,29 +9,10 @@
</ul>
</div>
<section>
- <?php if (empty($events)): ?>
- <p class="alert"><?= t('No activity.') ?></p>
- <?php else: ?>
-
<?php if ($project['is_public']): ?>
<p class="pull-right"><i class="fa fa-rss-square"></i> <?= Helper\a(t('RSS feed'), 'project', 'feed', array('token' => $project['token'])) ?></p>
<?php endif ?>
- <?php foreach ($events as $event): ?>
- <div class="activity-event">
- <p class="activity-datetime">
- <?php if (Helper\contains($event['event_name'], 'task')): ?>
- <i class="fa fa-newspaper-o"></i>
- <?php elseif (Helper\contains($event['event_name'], 'subtask')): ?>
- <i class="fa fa-tasks"></i>
- <?php elseif (Helper\contains($event['event_name'], 'comment')): ?>
- <i class="fa fa-comments-o"></i>
- <?php endif ?>
- &nbsp;<?= dt('%B %e, %Y at %k:%M %p', $event['date_creation']) ?>
- </p>
- <div class="activity-content"><?= $event['event_content'] ?></div>
- </div>
- <?php endforeach ?>
- <?php endif ?>
+ <?= Helper\template('project_events', array('events' => $events)) ?>
</section>
</section> \ No newline at end of file
diff --git a/app/Templates/project_events.php b/app/Templates/project_events.php
new file mode 100644
index 00000000..1b606414
--- /dev/null
+++ b/app/Templates/project_events.php
@@ -0,0 +1,21 @@
+<?php if (empty($events)): ?>
+ <p class="alert"><?= t('No activity.') ?></p>
+<?php else: ?>
+
+ <?php foreach ($events as $event): ?>
+ <div class="activity-event">
+ <p class="activity-datetime">
+ <?php if (Helper\contains($event['event_name'], 'subtask')): ?>
+ <i class="fa fa-tasks"></i>
+ <?php elseif (Helper\contains($event['event_name'], 'task')): ?>
+ <i class="fa fa-newspaper-o"></i>
+ <?php elseif (Helper\contains($event['event_name'], 'comment')): ?>
+ <i class="fa fa-comments-o"></i>
+ <?php endif ?>
+ &nbsp;<?= dt('%B %e, %Y at %k:%M %p', $event['date_creation']) ?>
+ </p>
+ <div class="activity-content"><?= $event['event_content'] ?></div>
+ </div>
+ <?php endforeach ?>
+
+<?php endif ?> \ No newline at end of file
diff --git a/app/Templates/task_table.php b/app/Templates/task_table.php
index b6fdb2b9..fa04fa55 100644
--- a/app/Templates/task_table.php
+++ b/app/Templates/task_table.php
@@ -13,7 +13,7 @@
<?php foreach ($tasks as $task): ?>
<tr>
<td class="task-table task-<?= $task['color_id'] ?>">
- <a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['id']) ?></a>
+ <a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>">#<?= Helper\escape($task['id']) ?></a>
</td>
<td>
<?= Helper\in_list($task['column_id'], $columns) ?>
diff --git a/app/helpers.php b/app/helpers.php
index c9f2f8ab..9e0c2698 100644
--- a/app/helpers.php
+++ b/app/helpers.php
@@ -566,9 +566,9 @@ function a($label, $controller, $action, array $params = array(), $csrf = false,
}
/**
- * URL
+ * URL query string
*
- * a('link', 'task', 'show', array('task_id' => $task_id))
+ * u('task', 'show', array('task_id' => $task_id))
*
* @param string $controller Controller name
* @param string $action Action name
diff --git a/assets/css/app.css b/assets/css/app.css
index 0bb25740..a4d71cb4 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -1119,6 +1119,22 @@ tr td.task-orange,
color: #555;
}
+/* dashboard */
+#dashboard table {
+ font-size: 0.95em;
+}
+
+.dashboard-left-column {
+ width: 55%;
+ float: left;
+}
+
+.dashboard-right-column {
+ margin-left: 5%;
+ width: 40%;
+ float: left;
+}
+
/* confirmation box */
.confirm {
max-width: 700px;
diff --git a/assets/js/app.js b/assets/js/app.js
index 395dfb6e..7dcf04eb 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -67,6 +67,15 @@ var Kanboard = (function() {
dateFormat: 'yy-mm-dd',
constrainInput: false
});
+
+ // Project select box
+ $("#board-selector").chosen({
+ width: 180
+ });
+
+ $("#board-selector").change(function() {
+ window.location = "?controller=board&action=show&project_id=" + $(this).val();
+ });
}
};
@@ -229,17 +238,11 @@ Kanboard.Board = (function() {
return {
Init: function() {
- board_load_events();
- filter_load_events();
- // Project select box
- $("#board-selector").chosen({
- width: 180
- });
+ Kanboard.Before();
- $("#board-selector").change(function() {
- window.location = "?controller=board&action=show&project_id=" + $(this).val();
- });
+ board_load_events();
+ filter_load_events();
}
};
@@ -274,6 +277,18 @@ Kanboard.Project = (function() {
})();
+// Dashboard related functions
+Kanboard.Dashboard = (function() {
+
+ return {
+ Init: function() {
+ Kanboard.Before();
+ }
+ };
+
+})();
+
+
// Initialization
$(function() {
if ($("#board").length) {
@@ -282,6 +297,9 @@ $(function() {
else if ($("#task-section").length) {
Kanboard.Task.Init();
}
+ else if ($("#dashboard").length) {
+ Kanboard.Dashboard.Init();
+ }
else if ($("#project-section").length) {
Kanboard.Project.Init();
}
diff --git a/docs/contributing.markdown b/docs/contributing.markdown
new file mode 100644
index 00000000..a6b9029c
--- /dev/null
+++ b/docs/contributing.markdown
@@ -0,0 +1,69 @@
+Contributor Guidelines
+======================
+
+How can I help?
+---------------
+
+Kanboard is not perfect but there is many ways to help:
+
+- Give feedback
+- Report bugs
+- Add or update translations
+- Improve the documentation
+- Writing code
+- Tell your friends that Kanboard is awesome :)
+
+Before doing any large undertaking, open a new issue and explain your proposal.
+
+I want to give feedback
+-----------------------
+
+- You think something should be improved (user interface, feature request)
+- Check if your idea is not already proposed
+- Open a new issue
+- Describe your idea
+- You can also up vote with +1 on existing proposals
+
+I want to report a bug
+----------------------
+
+- Check if the issue is not already reported
+- Open a new ticket
+- Explain what is broken
+- Describe how to reproduce the bug
+- Describe your environment (Kanboard version, OS, web server, PHP version, database version, hosting type)
+
+I want to translate Kanboard
+----------------------------
+
+Kanboard is translated in many languages.
+However, translations are not complete, take look at the [translation guide to contribute](http://kanboard.net/documentation/translations).
+
+I want to improve the documentation
+-----------------------------------
+
+- You think something is not clear, there is grammatical errors, typo errors, anything.
+- The documentation is written in Markdown and stored in the folder `docs`.
+- Edit the file and send a pull-request.
+- The documentation on the official website is synchronized with the repository.
+
+I want to contribute to the code
+--------------------------------
+
+Pull-requests are always welcome, however to be accepted you have to follow those directives:
+
+- **Before doing any large change or design proposal, open a new ticket to start a discussion.**
+- If you want to add a new feature, respect the philosophy behind Kanboard. **We focus on simplicity**, we don't want to have a bloated software.
+- The same apply for the user interface, **simplicity and efficiency**.
+- Send only one pull-request per feature or bug fix, your patch will be merged into one single commit in the master branch.
+- Make sure the [unit tests pass](http://kanboard.net/documentation/tests).
+- Respect the [coding standards](http://kanboard.net/documentation/coding-standards).
+- Write maintainable code, avoid code duplication, use PHP good practices.
+
+In any case, if you are not sure about something open a new ticket.
+
+Tell your friends that Kanboard is awesome :)
+---------------------------------------------
+
+If you use Kanboard, spread the word around you.
+Tell them that free and open source software are cool :)
diff --git a/docs/docker.markdown b/docs/docker.markdown
new file mode 100644
index 00000000..92fcf2c3
--- /dev/null
+++ b/docs/docker.markdown
@@ -0,0 +1,48 @@
+How to test Kanboard with Docker?
+=================================
+
+Kanboard can run with [Docker](https://www.docker.com).
+You can use the public image or build your own image from the `Dockerfile`.
+
+Actually, the Docker image is based on the master branch (development version).
+
+Build your own Docker image
+---------------------------
+
+From your kanboard directory run the following command:
+
+```bash
+docker build -t youruser\kanboard:master .
+```
+
+To run your image in background on the port 80:
+
+```bash
+docker run -d --name kanboard -p 80:80 -t youruser/kanboard:master
+```
+
+Run the public Kanboard image
+-----------------------------
+
+This image is stored on the [Docker Hub](https://hub.docker.com).
+
+Fetch the image on your machine:
+
+```bash
+docker pull kanboard/kanboard:master
+```
+
+Run the image:
+
+```bash
+docker run -d --name kanboard -p 80:80 -t kanboard/kanboard:master
+```
+
+Store your data on a volume
+---------------------------
+
+You can also save your data outside of the container, on the local machine:
+
+```bash
+docker run -d --name kanboard -v /your/local/data/folder:/var/www/html/data -p 80:80 -t kanboard/kanboard:master
+```
diff --git a/docs/kanban-vs-todo-and-scrum.markdown b/docs/kanban-vs-todo-and-scrum.markdown
new file mode 100644
index 00000000..3d53023a
--- /dev/null
+++ b/docs/kanban-vs-todo-and-scrum.markdown
@@ -0,0 +1,37 @@
+Kanban vs Todo lists and Scrum
+==============================
+
+Kanban vs Todo lists
+--------------------
+
+### Todo lists:
+
+- Single phase (just a list of items)
+- Multitasking possible (not efficient)
+
+### Kanban:
+
+- Multiple phases, each column represent a step
+- Bring focus and avoid multitasking because you can set a work in progress limit per column
+
+Kanban vs Scrum
+---------------
+
+### Scrum:
+
+- Sprints are time-boxed, usually 2 or 4 weeks
+- Do not allow changes during the iteration
+- Estimation is required
+- Uses velocity as default metric
+- Scrum board is cleared between each sprint
+- Scrum has predefined roles like scrum master, product owner and the team
+- A lot of meetings: planning, backlog grooming, daily stand-up, retrospective
+
+### Kanban:
+
+- Continuous flow
+- Changes can be made at anytime
+- Estimation is optional
+- Use lead and cycle time to measure performance
+- Kanban board is persistent
+- Kanban doesn't impose strict constraints or meetings, process is more flexible
diff --git a/docs/what-is-kanban.markdown b/docs/what-is-kanban.markdown
new file mode 100644
index 00000000..e3c3cf53
--- /dev/null
+++ b/docs/what-is-kanban.markdown
@@ -0,0 +1,32 @@
+What is Kanban?
+===============
+
+Kanban is a methodology originally developed by Toyota to be more efficient.
+
+There is only two constraints imposed by Kanban:
+
+- Visualize your workflow
+- Limit your work in progress
+
+Visualize your workflow
+-----------------------
+
+- Your work is visualized on a board, you have a clear overview of your project
+- Each column represent a step in your workflow
+
+Bring focus and avoid multitasking
+----------------------------------
+
+- Each phase can have a work in progress limit
+- Limits are great to identify bottlenecks
+- Limits avoid working on too many tasks in the same time
+
+Measure performance and improvement
+-----------------------------------
+
+Kanban uses lead and cycle times to measure performance:
+
+- **Lead time**: Time between the task is created and completed
+- **Cycle time**: Time between the task is started and completed
+
+By example, you may have a lead time of 100 days and only have to work 1 hour to complete the task.
diff --git a/tests/units/ProjectActivityTest.php b/tests/units/ProjectActivityTest.php
index 2565b6e7..7e7841dd 100644
--- a/tests/units/ProjectActivityTest.php
+++ b/tests/units/ProjectActivityTest.php
@@ -24,7 +24,7 @@ class ProjectActivityTest extends Base
$this->assertTrue($e->createEvent(1, 2, 1, Task::EVENT_UPDATE, array('task' => $tf->getById(2))));
$this->assertFalse($e->createEvent(1, 1, 0, Task::EVENT_OPEN, array('task' => $tf->getbyId(1))));
- $events = $e->getAll(1);
+ $events = $e->getProject(1);
$this->assertNotEmpty($events);
$this->assertTrue(is_array($events));
@@ -50,7 +50,7 @@ class ProjectActivityTest extends Base
$this->assertTrue($e->createEvent(1, 1, 1, Task::EVENT_UPDATE, array('task' => $tf->getbyId(1))));
}
- $events = $e->getAll(1);
+ $events = $e->getProject(1);
$this->assertNotEmpty($events);
$this->assertTrue(is_array($events));
@@ -80,7 +80,7 @@ class ProjectActivityTest extends Base
$this->assertEquals($nb_events, $this->registry->shared('db')->table('project_activities')->count());
$e->cleanup($max);
- $events = $e->getAll(1);
+ $events = $e->getProject(1);
$this->assertNotEmpty($events);
$this->assertTrue(is_array($events));