summaryrefslogtreecommitdiff
path: root/app/Controller
diff options
context:
space:
mode:
Diffstat (limited to 'app/Controller')
-rw-r--r--app/Controller/Activity.php5
-rw-r--r--app/Controller/Analytic.php8
-rw-r--r--app/Controller/App.php2
-rw-r--r--app/Controller/AvatarFile.php92
-rw-r--r--app/Controller/Base.php56
-rw-r--r--app/Controller/Board.php21
-rw-r--r--app/Controller/Calendar.php46
-rw-r--r--app/Controller/Doc.php87
-rw-r--r--app/Controller/Feed.php4
-rw-r--r--app/Controller/FileViewer.php26
-rw-r--r--app/Controller/Gantt.php34
-rw-r--r--app/Controller/GroupHelper.php8
-rw-r--r--app/Controller/Ical.php59
-rw-r--r--app/Controller/Listing.php21
-rw-r--r--app/Controller/Oauth.php106
-rw-r--r--app/Controller/ProjectOverview.php21
-rw-r--r--app/Controller/ProjectPermission.php4
-rw-r--r--app/Controller/Search.php32
-rw-r--r--app/Controller/Task.php8
-rw-r--r--app/Controller/TaskHelper.php38
-rw-r--r--app/Controller/UserHelper.php13
21 files changed, 418 insertions, 273 deletions
diff --git a/app/Controller/Activity.php b/app/Controller/Activity.php
index db520ebe..47a66e0a 100644
--- a/app/Controller/Activity.php
+++ b/app/Controller/Activity.php
@@ -20,7 +20,7 @@ class Activity extends Base
$project = $this->getProject();
$this->response->html($this->helper->layout->app('activity/project', array(
- 'events' => $this->projectActivity->getProject($project['id']),
+ 'events' => $this->helper->projectActivity->getProjectEvents($project['id']),
'project' => $project,
'title' => t('%s\'s activity', $project['name'])
)));
@@ -38,7 +38,8 @@ class Activity extends Base
$this->response->html($this->helper->layout->task('activity/task', array(
'title' => $task['title'],
'task' => $task,
- 'events' => $this->projectActivity->getTask($task['id']),
+ 'project' => $this->project->getById($task['project_id']),
+ 'events' => $this->helper->projectActivity->getTaskEvents($task['id']),
)));
}
}
diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php
index 6ce062c4..35bc3048 100644
--- a/app/Controller/Analytic.php
+++ b/app/Controller/Analytic.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\Task as TaskModel;
/**
@@ -44,14 +45,15 @@ class Analytic extends Base
public function compareHours()
{
$project = $this->getProject();
- $params = $this->getProjectFilters('analytic', 'compareHours');
- $query = $this->taskFilter->create()->filterByProject($params['project']['id'])->getQuery();
$paginator = $this->paginator
->setUrl('analytic', 'compareHours', array('project_id' => $project['id']))
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
- ->setQuery($query)
+ ->setQuery($this->taskQuery
+ ->withFilter(new TaskProjectFilter($project['id']))
+ ->getQuery()
+ )
->calculate();
$this->response->html($this->helper->layout->analytic('analytic/compare_hours', array(
diff --git a/app/Controller/App.php b/app/Controller/App.php
index df1d3c90..01f733ff 100644
--- a/app/Controller/App.php
+++ b/app/Controller/App.php
@@ -157,7 +157,7 @@ class App extends Base
$this->response->html($this->helper->layout->dashboard('app/activity', array(
'title' => t('My activity stream'),
- 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id']), 100),
+ 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermission->getActiveProjectIds($user['id']), 100),
'user' => $user,
)));
}
diff --git a/app/Controller/AvatarFile.php b/app/Controller/AvatarFile.php
new file mode 100644
index 00000000..a47cca66
--- /dev/null
+++ b/app/Controller/AvatarFile.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\ObjectStorage\ObjectStorageException;
+use Kanboard\Core\Thumbnail;
+
+/**
+ * Avatar File Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class AvatarFile extends Base
+{
+ /**
+ * Display avatar page
+ */
+ public function show()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->helper->layout->user('avatar_file/show', array(
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Upload Avatar
+ */
+ public function upload()
+ {
+ $user = $this->getUser();
+
+ if (! $this->avatarFile->uploadFile($user['id'], $this->request->getFileInfo('avatar'))) {
+ $this->flash->failure(t('Unable to upload the file.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('AvatarFile', 'show', array('user_id' => $user['id'])));
+ }
+
+ /**
+ * Remove Avatar image
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $user = $this->getUser();
+ $this->avatarFile->remove($user['id']);
+ $this->response->redirect($this->helper->url->to('AvatarFile', 'show', array('user_id' => $user['id'])));
+ }
+
+ /**
+ * Show Avatar image (public)
+ */
+ public function image()
+ {
+ $user_id = $this->request->getIntegerParam('user_id');
+ $size = $this->request->getStringParam('size', 48);
+ $filename = $this->avatarFile->getFilename($user_id);
+ $etag = md5($filename.$size);
+
+ $this->response->cache(365 * 86400, $etag);
+ $this->response->contentType('image/jpeg');
+
+ if ($this->request->getHeader('If-None-Match') !== '"'.$etag.'"') {
+ $this->render($filename, $size);
+ } else {
+ $this->response->status(304);
+ }
+ }
+
+ /**
+ * Render thumbnail from object storage
+ *
+ * @access private
+ * @param string $filename
+ * @param integer $size
+ */
+ private function render($filename, $size)
+ {
+ try {
+ $blob = $this->objectStorage->get($filename);
+
+ Thumbnail::createFromString($blob)
+ ->resize($size, $size)
+ ->toOutput();
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
+ }
+}
diff --git a/app/Controller/Base.php b/app/Controller/Base.php
index 2453be18..beb56909 100644
--- a/app/Controller/Base.php
+++ b/app/Controller/Base.php
@@ -287,60 +287,4 @@ abstract class Base extends \Kanboard\Core\Base
return $subtask;
}
-
- /**
- * Common method to get project filters
- *
- * @access protected
- * @param string $controller
- * @param string $action
- * @return array
- */
- protected function getProjectFilters($controller, $action)
- {
- $project = $this->getProject();
- $search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id']));
- $board_selector = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
- unset($board_selector[$project['id']]);
-
- $filters = array(
- 'controller' => $controller,
- 'action' => $action,
- 'project_id' => $project['id'],
- 'search' => urldecode($search),
- );
-
- $this->userSession->setFilters($project['id'], $filters['search']);
-
- return array(
- 'project' => $project,
- 'board_selector' => $board_selector,
- 'filters' => $filters,
- 'title' => $project['name'],
- 'description' => $this->getProjectDescription($project),
- );
- }
-
- /**
- * Get project description
- *
- * @access protected
- * @param array &$project
- * @return string
- */
- protected function getProjectDescription(array &$project)
- {
- if ($project['owner_id'] > 0) {
- $description = t('Project owner: ').'**'.$this->helper->text->e($project['owner_name'] ?: $project['owner_username']).'**'.PHP_EOL.PHP_EOL;
-
- if (! empty($project['description'])) {
- $description .= '***'.PHP_EOL.PHP_EOL;
- $description .= $project['description'];
- }
- } else {
- $description = $project['description'];
- }
-
- return $description;
- }
}
diff --git a/app/Controller/Board.php b/app/Controller/Board.php
index 199f1703..67e99b81 100644
--- a/app/Controller/Board.php
+++ b/app/Controller/Board.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Formatter\BoardFormatter;
+
/**
* Board controller
*
@@ -47,16 +49,19 @@ class Board extends Base
*/
public function show()
{
- $params = $this->getProjectFilters('board', 'show');
+ $project = $this->getProject();
+ $search = $this->helper->projectHeader->getSearchQuery($project);
$this->response->html($this->helper->layout->app('board/view_private', array(
- 'categories_list' => $this->category->getList($params['project']['id'], false),
- 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
- 'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
- 'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']),
+ 'project' => $project,
+ 'title' => $project['name'],
+ 'description' => $this->helper->projectHeader->getDescription($project),
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
'board_highlight_period' => $this->config->get('board_highlight_period'),
- ) + $params));
+ 'swimlanes' => $this->taskLexer
+ ->build($search)
+ ->format(BoardFormatter::getInstance($this->container)->setProjectId($project['id']))
+ )));
}
/**
@@ -177,9 +182,11 @@ class Board extends Base
{
return $this->template->render('board/table_container', array(
'project' => $this->project->getById($project_id),
- 'swimlanes' => $this->taskFilter->search($this->userSession->getFilters($project_id))->getBoard($project_id),
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
'board_highlight_period' => $this->config->get('board_highlight_period'),
+ 'swimlanes' => $this->taskLexer
+ ->build($this->userSession->getFilters($project_id))
+ ->format(BoardFormatter::getInstance($this->container)->setProjectId($project_id))
));
}
}
diff --git a/app/Controller/Calendar.php b/app/Controller/Calendar.php
index a0a25e41..2517286d 100644
--- a/app/Controller/Calendar.php
+++ b/app/Controller/Calendar.php
@@ -2,6 +2,9 @@
namespace Kanboard\Controller;
+use Kanboard\Filter\TaskAssigneeFilter;
+use Kanboard\Filter\TaskProjectFilter;
+use Kanboard\Filter\TaskStatusFilter;
use Kanboard\Model\Task as TaskModel;
/**
@@ -20,9 +23,14 @@ class Calendar extends Base
*/
public function show()
{
+ $project = $this->getProject();
+
$this->response->html($this->helper->layout->app('calendar/show', array(
+ 'project' => $project,
+ 'title' => $project['name'],
+ 'description' => $this->helper->projectHeader->getDescription($project),
'check_interval' => $this->config->get('board_private_refresh_interval'),
- ) + $this->getProjectFilters('calendar', 'show')));
+ )));
}
/**
@@ -35,21 +43,11 @@ class Calendar extends Base
$project_id = $this->request->getIntegerParam('project_id');
$start = $this->request->getStringParam('start');
$end = $this->request->getStringParam('end');
+ $search = $this->userSession->getFilters($project_id);
+ $queryBuilder = $this->taskLexer->build($search)->withFilter(new TaskProjectFilter($project_id));
- // Common filter
- $filter = $this->taskFilterCalendarFormatter
- ->search($this->userSession->getFilters($project_id))
- ->filterByProject($project_id);
-
- // Tasks
- if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') {
- $events = $filter->copy()->filterByCreationDateRange($start, $end)->setColumns('date_creation', 'date_completed')->format();
- } else {
- $events = $filter->copy()->filterByStartDateRange($start, $end)->setColumns('date_started', 'date_completed')->format();
- }
-
- // Tasks with due date
- $events = array_merge($events, $filter->copy()->filterByDueDateRange($start, $end)->setColumns('date_due')->setFullDay()->format());
+ $events = $this->helper->calendar->getTaskDateDueEvents(clone($queryBuilder), $start, $end);
+ $events = array_merge($events, $this->helper->calendar->getTaskEvents(clone($queryBuilder), $start, $end));
$events = $this->hook->merge('controller:calendar:project:events', $events, array(
'project_id' => $project_id,
@@ -70,21 +68,15 @@ class Calendar extends Base
$user_id = $this->request->getIntegerParam('user_id');
$start = $this->request->getStringParam('start');
$end = $this->request->getStringParam('end');
- $filter = $this->taskFilterCalendarFormatter->create()->filterByOwner($user_id)->filterByStatus(TaskModel::STATUS_OPEN);
-
- // Task with due date
- $events = $filter->copy()->filterByDueDateRange($start, $end)->setColumns('date_due')->setFullDay()->format();
+ $queryBuilder = $this->taskQuery
+ ->withFilter(new TaskAssigneeFilter($user_id))
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN));
- // Tasks
- if ($this->config->get('calendar_user_tasks', 'date_started') === 'date_creation') {
- $events = array_merge($events, $filter->copy()->filterByCreationDateRange($start, $end)->setColumns('date_creation', 'date_completed')->format());
- } else {
- $events = array_merge($events, $filter->copy()->filterByStartDateRange($start, $end)->setColumns('date_started', 'date_completed')->format());
- }
+ $events = $this->helper->calendar->getTaskDateDueEvents(clone($queryBuilder), $start, $end);
+ $events = array_merge($events, $this->helper->calendar->getTaskEvents(clone($queryBuilder), $start, $end));
- // Subtasks time tracking
if ($this->config->get('calendar_user_subtasks_time_tracking') == 1) {
- $events = array_merge($events, $this->subtaskTimeTracking->getUserCalendarEvents($user_id, $start, $end));
+ $events = array_merge($events, $this->helper->calendar->getSubtaskTimeTrackingEvents($user_id, $start, $end));
}
$events = $this->hook->merge('controller:calendar:user:events', $events, array(
diff --git a/app/Controller/Doc.php b/app/Controller/Doc.php
index f85326ac..00b9e585 100644
--- a/app/Controller/Doc.php
+++ b/app/Controller/Doc.php
@@ -5,54 +5,32 @@ namespace Kanboard\Controller;
use Parsedown;
/**
- * Documentation controller
+ * Documentation Viewer
*
* @package controller
* @author Frederic Guillot
*/
class Doc extends Base
{
- private function readFile($filename)
- {
- $url = $this->helper->url;
- $data = file_get_contents($filename);
- list($title, ) = explode("\n", $data, 2);
-
- $replaceUrl = function (array $matches) use ($url) {
- return '('.$url->to('doc', 'show', array('file' => str_replace('.markdown', '', $matches[1]))).')';
- };
-
- $content = preg_replace_callback('/\((.*.markdown)\)/', $replaceUrl, $data);
-
- return array(
- 'content' => Parsedown::instance()->text($content),
- 'title' => $title !== 'Documentation' ? t('Documentation: %s', $title) : $title,
- );
- }
-
public function show()
{
$page = $this->request->getStringParam('file', 'index');
- if (! preg_match('/^[a-z0-9\-]+/', $page)) {
+ if (!preg_match('/^[a-z0-9\-]+/', $page)) {
$page = 'index';
}
- $filenames = array(__DIR__.'/../../doc/'.$page.'.markdown');
- $filename = __DIR__.'/../../doc/index.markdown';
-
if ($this->config->getCurrentLanguage() === 'fr_FR') {
- array_unshift($filenames, __DIR__.'/../../doc/fr/'.$page.'.markdown');
+ $filename = __DIR__.'/../../doc/fr/' . $page . '.markdown';
+ } else {
+ $filename = __DIR__ . '/../../doc/' . $page . '.markdown';
}
- foreach ($filenames as $file) {
- if (file_exists($file)) {
- $filename = $file;
- break;
- }
+ if (!file_exists($filename)) {
+ $filename = __DIR__.'/../../doc/index.markdown';
}
- $this->response->html($this->helper->layout->app('doc/show', $this->readFile($filename)));
+ $this->response->html($this->helper->layout->app('doc/show', $this->render($filename)));
}
/**
@@ -62,4 +40,53 @@ class Doc extends Base
{
$this->response->html($this->template->render('config/keyboard_shortcuts'));
}
+
+ /**
+ * Prepare Markdown file
+ *
+ * @access private
+ * @param string $filename
+ * @return array
+ */
+ private function render($filename)
+ {
+ $data = file_get_contents($filename);
+ $content = preg_replace_callback('/\((.*.markdown)\)/', array($this, 'replaceMarkdownUrl'), $data);
+ $content = preg_replace_callback('/\((screenshots.*\.png)\)/', array($this, 'replaceImageUrl'), $content);
+
+ list($title, ) = explode("\n", $data, 2);
+
+ return array(
+ 'content' => Parsedown::instance()->text($content),
+ 'title' => $title !== 'Documentation' ? t('Documentation: %s', $title) : $title,
+ );
+ }
+
+ /**
+ * Regex callback to replace Markdown links
+ *
+ * @access public
+ * @param array $matches
+ * @return string
+ */
+ public function replaceMarkdownUrl(array $matches)
+ {
+ return '('.$this->helper->url->to('doc', 'show', array('file' => str_replace('.markdown', '', $matches[1]))).')';
+ }
+
+ /**
+ * Regex callback to replace image links
+ *
+ * @access public
+ * @param array $matches
+ * @return string
+ */
+ public function replaceImageUrl(array $matches)
+ {
+ if ($this->config->getCurrentLanguage() === 'fr_FR') {
+ return '('.$this->helper->url->base().'doc/fr/'.$matches[1].')';
+ }
+
+ return '('.$this->helper->url->base().'doc/'.$matches[1].')';
+ }
}
diff --git a/app/Controller/Feed.php b/app/Controller/Feed.php
index 8457c383..f8b3d320 100644
--- a/app/Controller/Feed.php
+++ b/app/Controller/Feed.php
@@ -26,7 +26,7 @@ class Feed extends Base
}
$this->response->xml($this->template->render('feed/user', array(
- 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id'])),
+ 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermission->getActiveProjectIds($user['id'])),
'user' => $user,
)));
}
@@ -47,7 +47,7 @@ class Feed extends Base
}
$this->response->xml($this->template->render('feed/project', array(
- 'events' => $this->projectActivity->getProject($project['id']),
+ 'events' => $this->helper->projectActivity->getProjectEvents($project['id']),
'project' => $project,
)));
}
diff --git a/app/Controller/FileViewer.php b/app/Controller/FileViewer.php
index bc91c3d8..3be4ea14 100644
--- a/app/Controller/FileViewer.php
+++ b/app/Controller/FileViewer.php
@@ -66,9 +66,16 @@ class FileViewer extends Base
*/
public function image()
{
+ $file = $this->getFile();
+ $etag = md5($file['path']);
+ $this->response->contentType($this->helper->file->getImageMimeType($file['name']));
+ $this->response->cache(5 * 86400, $etag);
+
+ if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
+ return $this->response->status(304);
+ }
+
try {
- $file = $this->getFile();
- $this->response->contentType($this->helper->file->getImageMimeType($file['name']));
$this->objectStorage->output($file['path']);
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
@@ -82,12 +89,21 @@ class FileViewer extends Base
*/
public function thumbnail()
{
+ $file = $this->getFile();
+ $model = $file['model'];
+ $filename = $this->$model->getThumbnailPath($file['path']);
+ $etag = md5($filename);
+
+ $this->response->cache(5 * 86400, $etag);
$this->response->contentType('image/jpeg');
+ if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
+ return $this->response->status(304);
+ }
+
try {
- $file = $this->getFile();
- $model = $file['model'];
- $this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
+
+ $this->objectStorage->output($filename);
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
diff --git a/app/Controller/Gantt.php b/app/Controller/Gantt.php
index 9ffa277f..5e9ad55e 100644
--- a/app/Controller/Gantt.php
+++ b/app/Controller/Gantt.php
@@ -2,7 +2,14 @@
namespace Kanboard\Controller;
+use Kanboard\Filter\ProjectIdsFilter;
+use Kanboard\Filter\ProjectStatusFilter;
+use Kanboard\Filter\ProjectTypeFilter;
+use Kanboard\Filter\TaskProjectFilter;
+use Kanboard\Formatter\ProjectGanttFormatter;
+use Kanboard\Formatter\TaskGanttFormatter;
use Kanboard\Model\Task as TaskModel;
+use Kanboard\Model\Project as ProjectModel;
/**
* Gantt controller
@@ -17,14 +24,16 @@ class Gantt extends Base
*/
public function projects()
{
- if ($this->userSession->isAdmin()) {
- $project_ids = $this->project->getAllIds();
- } else {
- $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
- }
+ $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
+ $filter = $this->projectQuery
+ ->withFilter(new ProjectTypeFilter(ProjectModel::TYPE_TEAM))
+ ->withFilter(new ProjectStatusFilter(ProjectModel::ACTIVE))
+ ->withFilter(new ProjectIdsFilter($project_ids));
+
+ $filter->getQuery()->asc(ProjectModel::TABLE.'.start_date');
$this->response->html($this->helper->layout->app('gantt/projects', array(
- 'projects' => $this->projectGanttFormatter->filter($project_ids)->format(),
+ 'projects' => $filter->format(new ProjectGanttFormatter($this->container)),
'title' => t('Gantt chart for all projects'),
)));
}
@@ -54,9 +63,10 @@ class Gantt extends Base
*/
public function project()
{
- $params = $this->getProjectFilters('gantt', 'project');
- $filter = $this->taskFilterGanttFormatter->search($params['filters']['search'])->filterByProject($params['project']['id']);
+ $project = $this->getProject();
+ $search = $this->helper->projectHeader->getSearchQuery($project);
$sorting = $this->request->getStringParam('sorting', 'board');
+ $filter = $this->taskLexer->build($search)->withFilter(new TaskProjectFilter($project['id']));
if ($sorting === 'date') {
$filter->getQuery()->asc(TaskModel::TABLE.'.date_started')->asc(TaskModel::TABLE.'.date_creation');
@@ -64,10 +74,12 @@ class Gantt extends Base
$filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position');
}
- $this->response->html($this->helper->layout->app('gantt/project', $params + array(
- 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
+ $this->response->html($this->helper->layout->app('gantt/project', array(
+ 'project' => $project,
+ 'title' => $project['name'],
+ 'description' => $this->helper->projectHeader->getDescription($project),
'sorting' => $sorting,
- 'tasks' => $filter->format(),
+ 'tasks' => $filter->format(new TaskGanttFormatter($this->container)),
)));
}
diff --git a/app/Controller/GroupHelper.php b/app/Controller/GroupHelper.php
index 34f522a6..429614c2 100644
--- a/app/Controller/GroupHelper.php
+++ b/app/Controller/GroupHelper.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Formatter\GroupAutoCompleteFormatter;
+
/**
* Group Helper
*
@@ -11,14 +13,14 @@ namespace Kanboard\Controller;
class GroupHelper extends Base
{
/**
- * Group autocompletion (Ajax)
+ * Group auto-completion (Ajax)
*
* @access public
*/
public function autocomplete()
{
$search = $this->request->getStringParam('term');
- $groups = $this->groupManager->find($search);
- $this->response->json($this->groupAutoCompleteFormatter->setGroups($groups)->format());
+ $formatter = new GroupAutoCompleteFormatter($this->groupManager->find($search));
+ $this->response->json($formatter->format());
}
}
diff --git a/app/Controller/Ical.php b/app/Controller/Ical.php
index f1ea6d8f..8fe97b46 100644
--- a/app/Controller/Ical.php
+++ b/app/Controller/Ical.php
@@ -2,7 +2,11 @@
namespace Kanboard\Controller;
-use Kanboard\Model\TaskFilter;
+use Kanboard\Core\Filter\QueryBuilder;
+use Kanboard\Filter\TaskAssigneeFilter;
+use Kanboard\Filter\TaskProjectFilter;
+use Kanboard\Filter\TaskStatusFilter;
+use Kanboard\Formatter\TaskICalFormatter;
use Kanboard\Model\Task as TaskModel;
use Eluceo\iCal\Component\Calendar as iCalendar;
@@ -30,10 +34,11 @@ class Ical extends Base
}
// Common filter
- $filter = $this->taskFilterICalendarFormatter
- ->create()
- ->filterByStatus(TaskModel::STATUS_OPEN)
- ->filterByOwner($user['id']);
+ $queryBuilder = new QueryBuilder();
+ $queryBuilder
+ ->withQuery($this->taskFinder->getICalQuery())
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskAssigneeFilter($user['id']));
// Calendar properties
$calendar = new iCalendar('Kanboard');
@@ -41,7 +46,7 @@ class Ical extends Base
$calendar->setDescription($user['name'] ?: $user['username']);
$calendar->setPublishedTTL('PT1H');
- $this->renderCalendar($filter, $calendar);
+ $this->renderCalendar($queryBuilder, $calendar);
}
/**
@@ -60,10 +65,11 @@ class Ical extends Base
}
// Common filter
- $filter = $this->taskFilterICalendarFormatter
- ->create()
- ->filterByStatus(TaskModel::STATUS_OPEN)
- ->filterByProject($project['id']);
+ $queryBuilder = new QueryBuilder();
+ $queryBuilder
+ ->withQuery($this->taskFinder->getICalQuery())
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskProjectFilter($project['id']));
// Calendar properties
$calendar = new iCalendar('Kanboard');
@@ -71,7 +77,7 @@ class Ical extends Base
$calendar->setDescription($project['name']);
$calendar->setPublishedTTL('PT1H');
- $this->renderCalendar($filter, $calendar);
+ $this->renderCalendar($queryBuilder, $calendar);
}
/**
@@ -79,37 +85,14 @@ class Ical extends Base
*
* @access private
*/
- private function renderCalendar(TaskFilter $filter, iCalendar $calendar)
+ private function renderCalendar(QueryBuilder $queryBuilder, iCalendar $calendar)
{
$start = $this->request->getStringParam('start', strtotime('-2 month'));
$end = $this->request->getStringParam('end', strtotime('+6 months'));
- // Tasks
- if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') {
- $filter
- ->copy()
- ->filterByCreationDateRange($start, $end)
- ->setColumns('date_creation', 'date_completed')
- ->setCalendar($calendar)
- ->addDateTimeEvents();
- } else {
- $filter
- ->copy()
- ->filterByStartDateRange($start, $end)
- ->setColumns('date_started', 'date_completed')
- ->setCalendar($calendar)
- ->addDateTimeEvents($calendar);
- }
-
- // Tasks with due date
- $filter
- ->copy()
- ->filterByDueDateRange($start, $end)
- ->setColumns('date_due')
- ->setCalendar($calendar)
- ->addFullDayEvents($calendar);
+ $this->helper->ical->addTaskDateDueEvents($queryBuilder, $calendar, $start, $end);
- $this->response->contentType('text/calendar; charset=utf-8');
- echo $filter->setCalendar($calendar)->format();
+ $formatter = new TaskICalFormatter($this->container);
+ $this->response->ical($formatter->setCalendar($calendar)->format());
}
}
diff --git a/app/Controller/Listing.php b/app/Controller/Listing.php
index c784dd50..2024ff03 100644
--- a/app/Controller/Listing.php
+++ b/app/Controller/Listing.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\Task as TaskModel;
/**
@@ -19,22 +20,26 @@ class Listing extends Base
*/
public function show()
{
- $params = $this->getProjectFilters('listing', 'show');
- $query = $this->taskFilter->search($params['filters']['search'])->filterByProject($params['project']['id'])->getQuery();
+ $project = $this->getProject();
+ $search = $this->helper->projectHeader->getSearchQuery($project);
$paginator = $this->paginator
- ->setUrl('listing', 'show', array('project_id' => $params['project']['id']))
+ ->setUrl('listing', 'show', array('project_id' => $project['id']))
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
->setDirection('DESC')
- ->setQuery($query)
+ ->setQuery($this->taskLexer
+ ->build($search)
+ ->withFilter(new TaskProjectFilter($project['id']))
+ ->getQuery()
+ )
->calculate();
- $this->response->html($this->helper->layout->app('listing/show', $params + array(
+ $this->response->html($this->helper->layout->app('listing/show', array(
+ 'project' => $project,
+ 'title' => $project['name'],
+ 'description' => $this->helper->projectHeader->getDescription($project),
'paginator' => $paginator,
- 'categories_list' => $this->category->getList($params['project']['id'], false),
- 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
- 'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
)));
}
}
diff --git a/app/Controller/Oauth.php b/app/Controller/Oauth.php
index 452faecd..12b91144 100644
--- a/app/Controller/Oauth.php
+++ b/app/Controller/Oauth.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Security\OAuthAuthenticationProviderInterface;
+
/**
* OAuth controller
*
@@ -11,25 +13,6 @@ namespace Kanboard\Controller;
class Oauth extends Base
{
/**
- * Unlink external account
- *
- * @access public
- */
- public function unlink()
- {
- $backend = $this->request->getStringParam('backend');
- $this->checkCSRFParam();
-
- if ($this->authenticationManager->getProvider($backend)->unlink($this->userSession->getId())) {
- $this->flash->success(t('Your external account is not linked anymore to your profile.'));
- } else {
- $this->flash->failure(t('Unable to unlink your external account.'));
- }
-
- $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
- }
-
- /**
* Redirect to the provider if no code received
*
* @access private
@@ -38,9 +21,10 @@ class Oauth extends Base
protected function step1($provider)
{
$code = $this->request->getStringParam('code');
+ $state = $this->request->getStringParam('state');
if (! empty($code)) {
- $this->step2($provider, $code);
+ $this->step2($provider, $code, $state);
} else {
$this->response->redirect($this->authenticationManager->getProvider($provider)->getService()->getAuthorizationUrl());
}
@@ -50,34 +34,44 @@ class Oauth extends Base
* Link or authenticate the user
*
* @access protected
- * @param string $provider
+ * @param string $providerName
* @param string $code
+ * @param string $state
*/
- protected function step2($provider, $code)
+ protected function step2($providerName, $code, $state)
{
- $this->authenticationManager->getProvider($provider)->setCode($code);
+ $provider = $this->authenticationManager->getProvider($providerName);
+ $provider->setCode($code);
+ $hasValidState = $provider->getService()->isValidateState($state);
if ($this->userSession->isLogged()) {
- $this->link($provider);
+ if ($hasValidState) {
+ $this->link($provider);
+ } else {
+ $this->flash->failure(t('The OAuth2 state parameter is invalid'));
+ $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
+ }
+ } else {
+ if ($hasValidState) {
+ $this->authenticate($providerName);
+ } else {
+ $this->authenticationFailure(t('The OAuth2 state parameter is invalid'));
+ }
}
-
- $this->authenticate($provider);
}
/**
* Link the account
*
* @access protected
- * @param string $provider
+ * @param OAuthAuthenticationProviderInterface $provider
*/
- protected function link($provider)
+ protected function link(OAuthAuthenticationProviderInterface $provider)
{
- $authProvider = $this->authenticationManager->getProvider($provider);
-
- if (! $authProvider->authenticate()) {
+ if (! $provider->authenticate()) {
$this->flash->failure(t('External authentication failed'));
} else {
- $this->userProfile->assign($this->userSession->getId(), $authProvider->getUser());
+ $this->userProfile->assign($this->userSession->getId(), $provider->getUser());
$this->flash->success(t('Your external account is linked to your profile successfully.'));
}
@@ -85,22 +79,52 @@ class Oauth extends Base
}
/**
+ * Unlink external account
+ *
+ * @access public
+ */
+ public function unlink()
+ {
+ $backend = $this->request->getStringParam('backend');
+ $this->checkCSRFParam();
+
+ if ($this->authenticationManager->getProvider($backend)->unlink($this->userSession->getId())) {
+ $this->flash->success(t('Your external account is not linked anymore to your profile.'));
+ } else {
+ $this->flash->failure(t('Unable to unlink your external account.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
+ }
+
+ /**
* Authenticate the account
*
* @access protected
- * @param string $provider
+ * @param string $providerName
*/
- protected function authenticate($provider)
+ protected function authenticate($providerName)
{
- if ($this->authenticationManager->oauthAuthentication($provider)) {
+ if ($this->authenticationManager->oauthAuthentication($providerName)) {
$this->response->redirect($this->helper->url->to('app', 'index'));
} else {
- $this->response->html($this->helper->layout->app('auth/index', array(
- 'errors' => array('login' => t('External authentication failed')),
- 'values' => array(),
- 'no_layout' => true,
- 'title' => t('Login')
- )));
+ $this->authenticationFailure(t('External authentication failed'));
}
}
+
+ /**
+ * Show login failure page
+ *
+ * @access protected
+ * @param string $message
+ */
+ protected function authenticationFailure($message)
+ {
+ $this->response->html($this->helper->layout->app('auth/index', array(
+ 'errors' => array('login' => $message),
+ 'values' => array(),
+ 'no_layout' => true,
+ 'title' => t('Login')
+ )));
+ }
}
diff --git a/app/Controller/ProjectOverview.php b/app/Controller/ProjectOverview.php
index b0687ed3..b2bb33d6 100644
--- a/app/Controller/ProjectOverview.php
+++ b/app/Controller/ProjectOverview.php
@@ -15,15 +15,18 @@ class ProjectOverview extends Base
*/
public function show()
{
- $params = $this->getProjectFilters('ProjectOverview', 'show');
- $params['users'] = $this->projectUserRole->getAllUsersGroupedByRole($params['project']['id']);
- $params['roles'] = $this->role->getProjectRoles();
- $params['events'] = $this->projectActivity->getProject($params['project']['id'], 10);
- $params['images'] = $this->projectFile->getAllImages($params['project']['id']);
- $params['files'] = $this->projectFile->getAllDocuments($params['project']['id']);
+ $project = $this->getProject();
+ $this->project->getColumnStats($project);
- $this->project->getColumnStats($params['project']);
-
- $this->response->html($this->helper->layout->app('project_overview/show', $params));
+ $this->response->html($this->helper->layout->app('project_overview/show', array(
+ 'project' => $project,
+ 'title' => $project['name'],
+ 'description' => $this->helper->projectHeader->getDescription($project),
+ 'users' => $this->projectUserRole->getAllUsersGroupedByRole($project['id']),
+ 'roles' => $this->role->getProjectRoles(),
+ 'events' => $this->helper->projectActivity->getProjectEvents($project['id'], 10),
+ 'images' => $this->projectFile->getAllImages($project['id']),
+ 'files' => $this->projectFile->getAllDocuments($project['id']),
+ )));
}
}
diff --git a/app/Controller/ProjectPermission.php b/app/Controller/ProjectPermission.php
index 800da02f..e203c0db 100644
--- a/app/Controller/ProjectPermission.php
+++ b/app/Controller/ProjectPermission.php
@@ -83,7 +83,9 @@ class ProjectPermission extends Base
$project = $this->getProject();
$values = $this->request->getValues();
- if ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) {
+ if (empty($values['user_id'])) {
+ $this->flash->failure(t('User not found.'));
+ } elseif ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) {
$this->flash->success(t('Project updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this project.'));
diff --git a/app/Controller/Search.php b/app/Controller/Search.php
index 9b9b9e65..a42e9d3d 100644
--- a/app/Controller/Search.php
+++ b/app/Controller/Search.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Filter\TaskProjectsFilter;
+
/**
* Search controller
*
@@ -23,14 +25,12 @@ class Search extends Base
->setDirection('DESC');
if ($search !== '' && ! empty($projects)) {
- $query = $this
- ->taskFilter
- ->search($search)
- ->filterByProjects(array_keys($projects))
- ->getQuery();
-
$paginator
- ->setQuery($query)
+ ->setQuery($this->taskLexer
+ ->build($search)
+ ->withFilter(new TaskProjectsFilter(array_keys($projects)))
+ ->getQuery()
+ )
->calculate();
$nb_tasks = $paginator->getTotal();
@@ -46,4 +46,22 @@ class Search extends Base
'title' => t('Search tasks').($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
)));
}
+
+ public function activity()
+ {
+ $search = urldecode($this->request->getStringParam('search'));
+ $events = $this->helper->projectActivity->searchEvents($search);
+ $nb_events = count($events);
+
+ $this->response->html($this->helper->layout->app('search/activity', array(
+ 'values' => array(
+ 'search' => $search,
+ 'controller' => 'search',
+ 'action' => 'activity',
+ ),
+ 'title' => t('Search in activity stream').($nb_events > 0 ? ' ('.$nb_events.')' : ''),
+ 'nb_events' => $nb_events,
+ 'events' => $events,
+ )));
+ }
}
diff --git a/app/Controller/Task.php b/app/Controller/Task.php
index dc10604e..902a32d6 100644
--- a/app/Controller/Task.php
+++ b/app/Controller/Task.php
@@ -71,17 +71,16 @@ class Task extends Base
$values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', DateParser::DATE_TIME_FORMAT));
$this->response->html($this->helper->layout->task('task/show', array(
+ 'task' => $task,
'project' => $this->project->getById($task['project_id']),
+ 'values' => $values,
'files' => $this->taskFile->getAllDocuments($task['id']),
'images' => $this->taskFile->getAllImages($task['id']),
'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()),
'subtasks' => $subtasks,
'internal_links' => $this->taskLink->getAllGroupedByLabel($task['id']),
'external_links' => $this->taskExternalLink->getAll($task['id']),
- 'task' => $task,
- 'values' => $values,
'link_label_list' => $this->link->getList(0, false),
- 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id'], true, false, false),
)));
}
@@ -96,6 +95,7 @@ class Task extends Base
$this->response->html($this->helper->layout->task('task/analytics', array(
'task' => $task,
+ 'project' => $this->project->getById($task['project_id']),
'lead_time' => $this->taskAnalytic->getLeadTime($task),
'cycle_time' => $this->taskAnalytic->getCycleTime($task),
'time_spent_columns' => $this->taskAnalytic->getTimeSpentByColumn($task),
@@ -121,6 +121,7 @@ class Task extends Base
$this->response->html($this->helper->layout->task('task/time_tracking_details', array(
'task' => $task,
+ 'project' => $this->project->getById($task['project_id']),
'subtask_paginator' => $subtask_paginator,
)));
}
@@ -136,6 +137,7 @@ class Task extends Base
$this->response->html($this->helper->layout->task('task/transitions', array(
'task' => $task,
+ 'project' => $this->project->getById($task['project_id']),
'transitions' => $this->transition->getAllByTask($task['id']),
)));
}
diff --git a/app/Controller/TaskHelper.php b/app/Controller/TaskHelper.php
index 7e340a6a..6835ab2b 100644
--- a/app/Controller/TaskHelper.php
+++ b/app/Controller/TaskHelper.php
@@ -2,6 +2,12 @@
namespace Kanboard\Controller;
+use Kanboard\Filter\TaskIdExclusionFilter;
+use Kanboard\Filter\TaskIdFilter;
+use Kanboard\Filter\TaskProjectsFilter;
+use Kanboard\Filter\TaskTitleFilter;
+use Kanboard\Formatter\TaskAutoCompleteFormatter;
+
/**
* Task Ajax Helper
*
@@ -11,31 +17,33 @@ namespace Kanboard\Controller;
class TaskHelper extends Base
{
/**
- * Task autocompletion (Ajax)
+ * Task auto-completion (Ajax)
*
* @access public
*/
public function autocomplete()
{
$search = $this->request->getStringParam('term');
- $projects = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
+ $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
+ $exclude_task_id = $this->request->getIntegerParam('exclude_task_id');
- if (empty($projects)) {
+ if (empty($project_ids)) {
$this->response->json(array());
- }
+ } else {
- $filter = $this->taskFilterAutoCompleteFormatter
- ->create()
- ->filterByProjects($projects)
- ->excludeTasks(array($this->request->getIntegerParam('exclude_task_id')));
+ $filter = $this->taskQuery->withFilter(new TaskProjectsFilter($project_ids));
- // Search by task id or by title
- if (ctype_digit($search)) {
- $filter->filterById($search);
- } else {
- $filter->filterByTitle($search);
- }
+ if (! empty($exclude_task_id)) {
+ $filter->withFilter(new TaskIdExclusionFilter(array($exclude_task_id)));
+ }
+
+ if (ctype_digit($search)) {
+ $filter->withFilter(new TaskIdFilter($search));
+ } else {
+ $filter->withFilter(new TaskTitleFilter($search));
+ }
- $this->response->json($filter->format());
+ $this->response->json($filter->format(new TaskAutoCompleteFormatter($this->container)));
+ }
}
}
diff --git a/app/Controller/UserHelper.php b/app/Controller/UserHelper.php
index 041ed2c8..47bbe554 100644
--- a/app/Controller/UserHelper.php
+++ b/app/Controller/UserHelper.php
@@ -2,6 +2,10 @@
namespace Kanboard\Controller;
+use Kanboard\Filter\UserNameFilter;
+use Kanboard\Formatter\UserAutoCompleteFormatter;
+use Kanboard\Model\User as UserModel;
+
/**
* User Helper
*
@@ -11,19 +15,20 @@ namespace Kanboard\Controller;
class UserHelper extends Base
{
/**
- * User autocompletion (Ajax)
+ * User auto-completion (Ajax)
*
* @access public
*/
public function autocomplete()
{
$search = $this->request->getStringParam('term');
- $users = $this->userFilterAutoCompleteFormatter->create($search)->filterByUsernameOrByName()->format();
- $this->response->json($users);
+ $filter = $this->userQuery->withFilter(new UserNameFilter($search));
+ $filter->getQuery()->asc(UserModel::TABLE.'.name')->asc(UserModel::TABLE.'.username');
+ $this->response->json($filter->format(new UserAutoCompleteFormatter($this->container)));
}
/**
- * User mention autocompletion (Ajax)
+ * User mention auto-completion (Ajax)
*
* @access public
*/