summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Controller/Board.php1
-rw-r--r--app/Controller/Customfilter.php142
-rw-r--r--app/Core/Base.php1
-rw-r--r--app/Model/Acl.php1
-rw-r--r--app/Model/CustomFilter.php163
-rw-r--r--app/Schema/Mysql.php19
-rw-r--r--app/Schema/Postgres.php16
-rw-r--r--app/Schema/Sqlite.php16
-rw-r--r--app/ServiceProvider/ClassProvider.php1
-rw-r--r--app/Template/board/view_private.php1
-rw-r--r--app/Template/custom_filter/add.php22
-rw-r--r--app/Template/custom_filter/edit.php30
-rw-r--r--app/Template/custom_filter/index.php40
-rw-r--r--app/Template/project/dropdown.php4
-rw-r--r--app/Template/project/filters.php11
-rw-r--r--app/Template/project/sidebar.php3
16 files changed, 468 insertions, 3 deletions
diff --git a/app/Controller/Board.php b/app/Controller/Board.php
index 5851bd26..840db05b 100644
--- a/app/Controller/Board.php
+++ b/app/Controller/Board.php
@@ -52,6 +52,7 @@ class Board extends Base
$this->response->html($this->template->layout('board/view_private', array(
'categories_list' => $this->category->getList($params['project']['id'], false),
'users_list' => $this->projectPermission->getMemberList($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']),
'description' => $params['project']['description'],
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
diff --git a/app/Controller/Customfilter.php b/app/Controller/Customfilter.php
new file mode 100644
index 00000000..c403cb4b
--- /dev/null
+++ b/app/Controller/Customfilter.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Controller;
+
+/**
+ * Custom Filter management
+ *
+ * @package controller
+ * @author Timo Litzbarski
+ */
+class Customfilter extends Base
+{
+ /**
+ * Display list of filters
+ *
+ * @access public
+ */
+ public function index(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->projectLayout('custom_filter/index', array(
+ 'values' => $values + array('project_id' => $project['id']),
+ 'errors' => $errors,
+ 'project' => $project,
+ 'custom_filters' => $this->customFilter->getAll($project['id'], $this->userSession->getId()),
+ 'title' => t('Custom filters'),
+ )));
+ }
+
+ /**
+ * Save a new custom filter
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+
+ $values = $this->request->getValues();
+ $values['user_id'] = $this->userSession->getId();
+
+ list($valid, $errors) = $this->customFilter->validateCreation($values);
+
+ if ($valid) {
+ if ($this->customFilter->create($values)) {
+ $this->session->flash(t('Your custom filter have been created successfully.'));
+ $this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
+ }
+ else {
+ $this->session->flashError(t('Unable to create your custom filter.'));
+ }
+ }
+
+ $this->index($values, $errors);
+ }
+
+ /**
+ * Remove a custom filter
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $project = $this->getProject();
+ $filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
+
+ $this->checkPermission($project, $filter);
+
+ if ($this->customFilter->remove($filter['id'])) {
+ $this->session->flash(t('Custom filter removed successfully.'));
+ } else {
+ $this->session->flashError(t('Unable to remove this custom filter.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Edit a custom filter (display the form)
+ *
+ * @access public
+ */
+ public function edit(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
+
+ $this->checkPermission($project, $filter);
+
+ $this->response->html($this->projectLayout('custom_filter/edit', array(
+ 'values' => empty($values) ? $filter : $values,
+ 'errors' => $errors,
+ 'project' => $project,
+ 'filter' => $filter,
+ 'title' => t('Edit custom filter')
+ )));
+ }
+
+ /**
+ * Edit a custom filter (validate the form and update the database)
+ *
+ * @access public
+ */
+ public function update()
+ {
+ $project = $this->getProject();
+ $filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
+
+ $this->checkPermission($project, $filter);
+
+ $values = $this->request->getValues();
+
+ if (! isset($values['is_shared'])) {
+ $values += array('is_shared' => 0);
+ }
+
+ list($valid, $errors) = $this->customFilter->validateModification($values);
+
+ if ($valid) {
+ if ($this->customFilter->update($values)) {
+ $this->session->flash(t('Your custom filter have been updated successfully.'));
+ $this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
+ }
+ else {
+ $this->session->flashError(t('Unable to update custom filter.'));
+ }
+ }
+
+ $this->edit($values, $errors);
+ }
+
+ private function checkPermission(array $project, array $filter)
+ {
+ $user_id = $this->userSession->getId();
+
+ if ($filter['user_id'] != $user_id && (! $this->projectPermission->isManager($project['id'], $user_id) || ! $this->userSession->isAdmin())) {
+ $this->forbidden();
+ }
+ }
+}
diff --git a/app/Core/Base.php b/app/Core/Base.php
index b919d551..71d54413 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -47,6 +47,7 @@ use Pimple\Container;
* @property \Model\Comment $comment
* @property \Model\Config $config
* @property \Model\Currency $currency
+ * @property \Model\CustomFilter $customFilter
* @property \Model\DateParser $dateParser
* @property \Model\File $file
* @property \Model\LastLogin $lastLogin
diff --git a/app/Model/Acl.php b/app/Model/Acl.php
index 9a227cf5..675ca36e 100644
--- a/app/Model/Acl.php
+++ b/app/Model/Acl.php
@@ -47,6 +47,7 @@ class Acl extends Base
'taskstatus' => '*',
'tasklink' => '*',
'timer' => '*',
+ 'customfilter' => '*',
'calendar' => array('show', 'project'),
);
diff --git a/app/Model/CustomFilter.php b/app/Model/CustomFilter.php
new file mode 100644
index 00000000..2c485247
--- /dev/null
+++ b/app/Model/CustomFilter.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace Model;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Custom Filter model
+ *
+ * @package model
+ * @author Timo Litzbarski
+ */
+class CustomFilter extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'custom_filters';
+
+ /**
+ * Return the list of all allowed custom filters for a user and project
+ *
+ * @access public
+ * @param integer $project_id Project id
+ * @param integer $user_id User id
+ * @return array
+ */
+ public function getAll($project_id, $user_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ User::TABLE.'.name as owner_name',
+ User::TABLE.'.username as owner_username',
+ self::TABLE.'.id',
+ self::TABLE.'.user_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.filter',
+ self::TABLE.'.name',
+ self::TABLE.'.is_shared'
+ )
+ ->asc(self::TABLE.'.name')
+ ->join(User::TABLE, 'id', 'user_id')
+ ->beginOr()
+ ->eq('is_shared', 1)
+ ->eq('user_id', $user_id)
+ ->closeOr()
+ ->eq('project_id', $project_id)
+ ->findAll();
+ }
+
+ /**
+ * Get custom filter by id
+ *
+ * @access private
+ * @param integer $filter_id
+ * @return array
+ */
+ public function getById($filter_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $filter_id)->findOne();
+ }
+
+ /**
+ * Create a custom filter
+ *
+ * @access public
+ * @param array $values Form values
+ * @return bool|integer
+ */
+ public function create(array $values)
+ {
+ return $this->persist(self::TABLE, $values);
+ }
+
+ /**
+ * Update a custom filter
+ *
+ * @access public
+ * @param array $values Form values
+ * @return bool
+ */
+ public function update(array $values)
+ {
+ return $this->db->table(self::TABLE)
+ ->eq('id', $values['id'])
+ ->update($values);
+ }
+
+ /**
+ * Remove a custom filter
+ *
+ * @access public
+ * @param integer $filter_id
+ * @return bool
+ */
+ public function remove($filter_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $filter_id)->remove();
+ }
+
+ /**
+ * Common validation rules
+ *
+ * @access private
+ * @return array
+ */
+ private function commonValidationRules()
+ {
+ return array(
+ new Validators\Required('project_id', t('Field required')),
+ new Validators\Required('user_id', t('Field required')),
+ new Validators\Required('name', t('Field required')),
+ new Validators\Required('filter', t('Field required')),
+ new Validators\Integer('user_id', t('This value must be an integer')),
+ new Validators\Integer('project_id', t('This value must be an integer')),
+ new Validators\MaxLength('name', t('The maximum length is %d characters', 100), 100),
+ new Validators\MaxLength('filter', t('The maximum length is %d characters', 100), 100)
+ );
+ }
+
+ /**
+ * Validate filter creation
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateCreation(array $values)
+ {
+ $v = new Validator($values, $this->commonValidationRules());
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+
+ /**
+ * Validate filter modification
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateModification(array $values)
+ {
+ $rules = array(
+ new Validators\Required('id', t('Field required')),
+ new Validators\Integer('id', t('This value must be an integer')),
+ );
+
+ $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php
index c146eb0c..a1cef344 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -6,7 +6,24 @@ use PDO;
use Core\Security;
use Model\Link;
-const VERSION = 87;
+const VERSION = 88;
+
+function version_88($pdo)
+{
+ $pdo->exec("
+ CREATE TABLE custom_filters (
+ id INT NOT NULL AUTO_INCREMENT,
+ filter VARCHAR(100) NOT NULL,
+ project_id INT NOT NULL,
+ user_id INT NOT NULL,
+ name VARCHAR(100) NOT NULL,
+ is_shared TINYINT(1) DEFAULT 0,
+ PRIMARY KEY(id),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+}
function version_87($pdo)
{
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
index 695d8f1f..f7263e75 100644
--- a/app/Schema/Postgres.php
+++ b/app/Schema/Postgres.php
@@ -6,7 +6,21 @@ use PDO;
use Core\Security;
use Model\Link;
-const VERSION = 67;
+const VERSION = 68;
+
+function version_68($pdo)
+{
+ $pdo->exec("
+ CREATE TABLE custom_filters (
+ id SERIAL PRIMARY KEY,
+ filter VARCHAR(100) NOT NULL,
+ project_id INTEGER NOT NULL,
+ user_id INTEGER NOT NULL,
+ name VARCHAR(100) NOT NULL,
+ is_shared BOOLEAN DEFAULT '0'
+ )
+ ");
+}
function version_67($pdo)
{
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index 26ad96c7..064d0f3c 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -6,7 +6,21 @@ use Core\Security;
use PDO;
use Model\Link;
-const VERSION = 83;
+const VERSION = 84;
+
+function version_84($pdo)
+{
+ $pdo->exec("
+ CREATE TABLE custom_filters (
+ id INTEGER PRIMARY KEY,
+ filter TEXT NOT NULL,
+ project_id INTEGER NOT NULL,
+ user_id INTEGER NOT NULL,
+ name TEXT NOT NULL,
+ is_shared INTEGER DEFAULT 0
+ )
+ ");
+}
function version_83($pdo)
{
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index a89b78bb..b671dace 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -27,6 +27,7 @@ class ClassProvider implements ServiceProviderInterface
'Comment',
'Config',
'Currency',
+ 'CustomFilter',
'DateParser',
'File',
'LastLogin',
diff --git a/app/Template/board/view_private.php b/app/Template/board/view_private.php
index d4c2c651..63d261f6 100644
--- a/app/Template/board/view_private.php
+++ b/app/Template/board/view_private.php
@@ -5,6 +5,7 @@
'filters' => $filters,
'categories_list' => $categories_list,
'users_list' => $users_list,
+ 'custom_filters_list' => $custom_filters_list,
'is_board' => true,
)) ?>
diff --git a/app/Template/custom_filter/add.php b/app/Template/custom_filter/add.php
new file mode 100644
index 00000000..d4e102b3
--- /dev/null
+++ b/app/Template/custom_filter/add.php
@@ -0,0 +1,22 @@
+<div class="page-header">
+ <h2><?= t('Add a new filter') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('customfilter', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array('required', 'maxlength="100"')) ?>
+
+ <?= $this->form->label(t('Filter'), 'filter') ?>
+ <?= $this->form->text('filter', $values, $errors, array('required', 'maxlength="100"')) ?>
+
+ <?php if ($this->user->isProjectManagementAllowed($project['id'])): ?>
+ <?= $this->form->checkbox('is_shared', t('Share with all project members'), 1) ?>
+ <?php endif ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue">
+ </div>
+</form> \ No newline at end of file
diff --git a/app/Template/custom_filter/edit.php b/app/Template/custom_filter/edit.php
new file mode 100644
index 00000000..7525574c
--- /dev/null
+++ b/app/Template/custom_filter/edit.php
@@ -0,0 +1,30 @@
+<div class="page-header">
+ <h2><?= t('Edit custom filter') ?></h2>
+</div>
+
+<form method="post" action="<?= $this->url->href('customfilter', 'update', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?>" autocomplete="off">
+
+ <?= $this->form->csrf() ?>
+
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->hidden('user_id', $values) ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?>
+
+ <?= $this->form->label(t('Filter'), 'filter') ?>
+ <?= $this->form->text('filter', $values, $errors, array('required', 'maxlength="100"')) ?>
+
+ <?php if ($this->user->isProjectManagementAllowed($project['id'])): ?>
+ <?= $this->form->checkbox('is_shared', t('Share with all project members'), 1, $values['is_shared'] == 1) ?>
+ <?php else: ?>
+ <?= $this->form->hidden('is_shared', $values) ?>
+ <?php endif ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue">
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'customfilter', 'index', array('project_id' => $project['id'])) ?>
+ </div>
+</form> \ No newline at end of file
diff --git a/app/Template/custom_filter/index.php b/app/Template/custom_filter/index.php
new file mode 100644
index 00000000..a53d0d0a
--- /dev/null
+++ b/app/Template/custom_filter/index.php
@@ -0,0 +1,40 @@
+<?php if (! empty($custom_filters)): ?>
+<div class="page-header">
+ <h2><?= t('Custom filters') ?></h2>
+</div>
+<div>
+ <table>
+ <tr>
+ <th><?= t('Name') ?></th>
+ <th><?= t('Filter') ?></th>
+ <th><?= t('Shared') ?></th>
+ <th><?= t('Owner') ?></th>
+ <th><?= t('Actions') ?></th>
+ </tr>
+ <?php foreach ($custom_filters as $filter): ?>
+ <tr>
+ <td><?= $this->e($filter['name']) ?></td>
+ <td><?= $this->e($filter['filter']) ?></td>
+ <td>
+ <?php if ($filter['is_shared'] == 1): ?>
+ <?= t('Yes') ?>
+ <?php else: ?>
+ <?= t('No') ?>
+ <?php endif ?>
+ </td>
+ <td><?= $this->e($filter['owner_name'] ?: $filter['owner_username']) ?></td>
+ <td>
+ <?php if ($filter['user_id'] == $this->user->getId() || $this->user->isProjectManagementAllowed($project['id'])): ?>
+ <ul>
+ <li><?= $this->url->link(t('Remove'), 'customfilter', 'remove', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id']), true) ?></li>
+ <li><?= $this->url->link(t('Edit'), 'customfilter', 'edit', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?></li>
+ </ul>
+ <?php endif ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+</div>
+<?php endif ?>
+
+<?= $this->render('custom_filter/add', array('project' => $project, 'values' => $values, 'errors' => $errors)) ?> \ No newline at end of file
diff --git a/app/Template/project/dropdown.php b/app/Template/project/dropdown.php
index 96b6a43a..1eb87b0e 100644
--- a/app/Template/project/dropdown.php
+++ b/app/Template/project/dropdown.php
@@ -2,6 +2,10 @@
<i class="fa fa-dashboard fa-fw"></i>&nbsp;
<?= $this->url->link(t('Activity'), 'activity', 'project', array('project_id' => $project['id'])) ?>
</li>
+<li>
+ <i class="fa fa-filter fa-fw"></i>&nbsp;
+ <?= $this->url->link(t('Custom filters'), 'customfilter', 'index', array('project_id' => $project['id'])) ?>
+</li>
<?php if ($project['is_public']): ?>
<li>
diff --git a/app/Template/project/filters.php b/app/Template/project/filters.php
index 5b9ac472..c8f09fee 100644
--- a/app/Template/project/filters.php
+++ b/app/Template/project/filters.php
@@ -86,5 +86,16 @@
</ul>
</div>
<?php endif ?>
+
+ <?php if (isset($custom_filters_list) && ! empty($custom_filters_list)): ?>
+ <div class="dropdown filters">
+ <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('My filters') ?></a>
+ <ul>
+ <?php foreach ($custom_filters_list as $filter): ?>
+ <li><a href="#" class="filter-helper" data-filter='<?= $this->e($filter['filter']) ?>'><?= $this->e($filter['name']) ?></a></li>
+ <?php endforeach ?>
+ </ul>
+ </div>
+ <?php endif ?>
</div>
</div> \ No newline at end of file
diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php
index 482a95d2..d8b35e3b 100644
--- a/app/Template/project/sidebar.php
+++ b/app/Template/project/sidebar.php
@@ -4,6 +4,9 @@
<li <?= $this->app->getRouterAction() === 'show' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Summary'), 'project', 'show', array('project_id' => $project['id'])) ?>
</li>
+ <li <?= $this->app->getRouterController() === 'customfilter' && $this->app->getRouterAction() === 'index' ? 'class="active"' : '' ?>>
+ <?= $this->url->link(t('Custom filters'), 'customfilter', 'index', array('project_id' => $project['id'])) ?>
+ </li>
<?php if ($this->user->isProjectManagementAllowed($project['id'])): ?>
<li <?= $this->app->getRouterController() === 'project' && $this->app->getRouterAction() === 'share' ? 'class="active"' : '' ?>>