This commit is contained in:
Teamjungla{CODE} 2016-08-20 13:47:12 -05:00
commit fe8e9cdcfe
708 changed files with 30686 additions and 13659 deletions

View File

@ -1 +0,0 @@
1 0 * * * cd /var/www/kanboard && ./kanboard cronjob >/dev/null 2>&1

View File

@ -1,72 +0,0 @@
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;
access_log off;
error_log /dev/stderr;
server {
listen 80;
server_name localhost;
index index.php;
root /var/www/kanboard;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
location /data {
return 404;
}
location ~* ^.+\.(log|sqlite)$ {
return 404;
}
location ~ /\.ht {
return 404;
}
location ~* ^.+\.(ico|jpg|gif|png|css|js|svg|eot|ttf|woff|woff2|otf)$ {
expires 7d;
etag on;
}
client_max_body_size 32M;
gzip on;
gzip_comp_level 3;
gzip_disable "msie6";
gzip_vary on;
gzip_types
text/javascript
application/javascript
application/json
text/xml
application/xml
application/rss+xml
text/css
text/plain;
}
}

View File

@ -1,16 +0,0 @@
expose_php = Off
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /dev/stderr
date.timezone = UTC
allow_url_fopen = On
post_max_size = 30M
upload_max_filesize = 30M
opcache.max_accelerated_files = 7963
opcache.validate_timestamps = Off
opcache.save_comments = 0
opcache.load_comments = 0
opcache.fast_shutdown = 1
opcache.enable_file_override = On
always_populate_raw_post_data = -1

View File

@ -1,22 +0,0 @@
[global]
error_log = /dev/stderr
log_level = error
daemonize = no
[www]
env[DATABASE_URL] = $DATABASE_URL
env[DEBUG] = $DEBUG
env[LOG_DRIVER] = stderr
catch_workers_output = yes
user = nginx
group = nginx
listen.owner = nginx
listen.group = nginx
listen = /var/run/php-fpm.sock
pm = dynamic
pm.max_children = 20
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 2048

View File

@ -1,2 +0,0 @@
#!/bin/sh
/bin/true

View File

@ -1,2 +0,0 @@
#!/bin/execlineb -P
nginx -g "daemon off;"

View File

@ -1,2 +0,0 @@
#!/bin/execlineb -P
php-fpm -F

21
.dockerignore Normal file
View File

@ -0,0 +1,21 @@
.git
.git*
.dockerignore
.vagrant
.idea
data/*
Makefile
.*.yml
*.yml
*.js
*.md
*.sh
app.json
bower.json
nitrous.json
package.json
Vagrantfile
web.config
Makefile
bower_components
node_modules

2
.gitignore vendored
View File

@ -19,6 +19,6 @@ data/files
data/cache
/vendor
*.bak
!.docker/kanboard/config.php
!docker/kanboard/config.php
node_modules
bower_components

View File

@ -1,11 +0,0 @@
filter:
paths:
- app/*
excluded_paths:
- app/Schema/*
- app/Template/*
- app/Locale/*
- app/Library/*
- app/constants.php
- app/common.php
- app/check_setup.php

1
CONTRIBUTING Normal file
View File

@ -0,0 +1 @@
Read https://kanboard.net/documentation/contributing

View File

@ -11,11 +11,14 @@ Contributors:
- [Ally Raza](https://github.com/alirz23)
- [Angystardust](https://github.com/angystardust)
- [Anjar Febrianto](https://github.com/Lasut)
- [Anton](https://github.com/tester22)
- [Ashbike](https://github.com/ashbike)
- [Ashish Kulkarni](https://github.com/ashkulz)
- [Biniou180](https://github.com/Biniou180)
- [Bitcoin 333](https://github.com/bitcoin333)
- [Busfreak](https://github.com/Busfreak)
- [Christian González](https://github.com/nerdoc)
- [Christopher Geelen](https://github.com/cdgeelen)
- [Chorgroup](https://github.com/chorgroup)
- Claudio Lobo
- [Cluxter](https://github.com/cluxter)
@ -44,15 +47,19 @@ Contributors:
- [Gavlepeter](https://github.com/gavlepeter)
- [Gerardo Zamudio](https://github.com/gerardozamudio)
- [Goofy](https://github.com/goofy-bz)
- [Hairetdin](https://github.com/hairetdin)
- [Hendrik Stocker](https://github.com/hendrik-stoker)
- [Honda2](https://github.com/honda2)
- [Iterate From 0](https://github.com/freebsd-kanboard)
- [Jan Dittrich](https://github.com/jdittrich)
- [Janne Mäntyharju](https://github.com/JanneMantyharju)
- [Jannik Winkel](https://github.com/kiney)
- [Jean-François Magnier](https://github.com/lefakir)
- [Jeff Guillou](https://github.com/jf-guillou)
- [Jesusaplsoft](https://github.com/jesusaplsoft)
- [Jesús Marín](https://github.com/alu0100502114)
- [Jules Verhaeren](https://github.com/julesverhaeren)
- [JunglaCODE]https://github.com/junglaCODE)
- [Karol J](https://github.com/dzudek)
- [Kiswa](https://github.com/kiswa)
- [Kralo](https://github.com/kralo)
@ -112,6 +119,7 @@ Contributors:
- [StavrosKa](https://github.com/StavrosKa)
- [Sylvain Veyrié](https://github.com/turb)
- [Thomas Lutz](https://github.com/phoen1x)
- [Thomas Stinner](https://github.com/stinnux)
- [Timo](https://github.com/BlueTeck)
- [Timotheus Pokorra](https://github.com/tpokorra)
- [Tomáš Votruba](https://github.com/TomasVotruba)
@ -125,6 +133,7 @@ Contributors:
- [Vitaliy S. Orlov](https://github.com/orlov0562)
- [Vladimir Babin](https://github.com/Chiliec)
- [Yannick Ihmels](https://github.com/ihmels)
- [Yakovenkov](https://github.com/yakovenkov)
- [Ybarc](https://github.com/ybarc)
- [Yu Yongwoo](https://github.com/uyu423)
- [Yuichi Murata](https://github.com/yuichi1004)

View File

@ -1,3 +1,82 @@
Version 1.0.32
--------------
New features:
* New automated actions:
- Close tasks without activity in a specific column
- Set due date automatically
- Move a task to another column when closed
- Move a task to another column when not moved during a given period
* New filter "moved" for moved date of tasks
* Added internal task links to activity stream
* Added new event for removed comments
* Added search filter for task priority
* Added the possibility to hide tasks in dashboard for a specific column
* Documentation translated in Russian
Improvements:
* Improve background worker and job handler
* New template hooks
* Removed individual column scrolling on board, columns use the height of all tasks
* Improve project page titles
* Remove sidebar titles when not necessary
* Internal events management refactoring
* Handle header X-Real-IP to get IP address
* Display project name for task auto-complete fields
* Make search attributes not case sensitive
* Display TOTP issuer for 2FA
* Make sure that the table schema_version use InnoDB for Mysql
* Use the library PicoFeed to generate RSS/Atom feeds
* Change all links to the new repository
Bug fixes:
* Allow users to see inactive projects
* Fixed typo in template that prevent project permissions to be duplicated
* Fixed search query with multiple assignees (nested OR conditions)
* Fixed Markdown editor auto-grow on the task form (Safari)
* Fixed compatibility issue with PHP 5.3 for OAuthUserProvider class
Version 1.0.31
--------------
New features:
* Added tags: global and specific by project
* Added application and project roles validation for API procedure calls
* Added new API call: "getProjectByIdentifier"
* Added new API calls for external task links, project attachments and subtask time tracking
Improvements:
* Use PHP 7 for the Docker image
* Preserve role for existing users when using ReverseProxy authentication
* Handle priority for task and project duplication
* Expose task reference field to the user interface
* Improve ICal export
* Added argument owner_id and identifier to project API calls
* Rewrite integration tests to run with Docker containers
* Use the same task form layout everywhere
* Removed some tasks dropdown menus that are now available with task edit form
* Make embedded documentation readable in multiple languages (if a translation is available)
* Added acceptance tests (browser tests)
Bug fixes:
* Fixed broken CSV exports
* Fixed identical background color for LetterAvatar on 32bits platforms (Hash greater than PHP_MAX_INT)
* Fixed lexer issue with non word characters
* Flush memory cache in worker to get latest config values
* Fixed empty title for web notification with only one overdue task
* Take default swimlane into consideration for SwimlaneModel::getFirstActiveSwimlane()
* Fixed "due today" highlighting
Breaking changes:
* Docker volume paths are changed to /var/www/app/{data,plugins}
Version 1.0.30
--------------

View File

@ -1,36 +1,13 @@
FROM alpine:3.4
MAINTAINER Frederic Guillot <fred@kanboard.net>
FROM fguillot/alpine-nginx-php7
RUN apk update && \
apk add nginx bash ca-certificates s6 curl \
php5-fpm php5-json php5-zlib php5-xml php5-dom php5-ctype php5-opcache php5-zip \
php5-pdo php5-pdo_mysql php5-pdo_sqlite php5-pdo_pgsql php5-ldap \
php5-gd php5-mcrypt php5-openssl php5-phar && \
rm -rf /var/cache/apk/*
COPY . /var/www/app
COPY docker/kanboard/config.php /var/www/app/config.php
COPY docker/crontab/cronjob.alpine /var/spool/cron/crontabs/nginx
COPY docker/services.d/cron /etc/services.d/cron
COPY docker/php/env.conf /etc/php7/php-fpm.d/env.conf
RUN curl -sS https://getcomposer.org/installer | php -- --filename=/usr/local/bin/composer
RUN cd /var/www/app && composer --prefer-dist --no-dev --optimize-autoloader --quiet install
RUN chown -R nginx:nginx /var/www/app/data /var/www/app/plugins
RUN cd /var/www \
&& curl -LO https://github.com/fguillot/kanboard/archive/master.zip \
&& unzip -qq master.zip \
&& rm -f *.zip \
&& mv kanboard-master kanboard \
&& cd /var/www/kanboard && composer --prefer-dist --no-dev --optimize-autoloader --quiet install \
&& chown -R nginx:nginx /var/www/kanboard \
&& chown -R nginx:nginx /var/lib/nginx
COPY .docker/services.d /etc/services.d
COPY .docker/php/conf.d/local.ini /etc/php5/conf.d/
COPY .docker/php/php-fpm.conf /etc/php5/
COPY .docker/nginx/nginx.conf /etc/nginx/
COPY .docker/kanboard/config.php /var/www/kanboard/
COPY .docker/kanboard/config.php /var/www/kanboard/
COPY .docker/crontab/kanboard /var/spool/cron/crontabs/nginx
EXPOSE 80
VOLUME /var/www/kanboard/data
VOLUME /var/www/kanboard/plugins
ENTRYPOINT ["/bin/s6-svscan", "/etc/services.d"]
CMD []
VOLUME /var/www/app/data
VOLUME /var/www/app/plugins

View File

@ -9,7 +9,7 @@ static:
archive:
@ echo "Build archive: version=${version}, destination=${dst}"
@ rm -rf ${BUILD_DIR}/kanboard ${BUILD_DIR}/kanboard-*.zip
@ cd ${BUILD_DIR} && git clone --depth 1 -q https://github.com/fguillot/kanboard.git
@ cd ${BUILD_DIR} && git clone --depth 1 -q https://github.com/kanboard/kanboard.git
@ cd ${BUILD_DIR}/kanboard && composer --prefer-dist --no-dev --optimize-autoloader --quiet install
@ rm -rf ${BUILD_DIR}/kanboard/data/*.sqlite
@ rm -rf ${BUILD_DIR}/kanboard/data/*.log
@ -26,7 +26,8 @@ archive:
@ rm -rf ${BUILD_DIR}/kanboard/*.lock
@ rm -rf ${BUILD_DIR}/kanboard/*.json
@ rm -rf ${BUILD_DIR}/kanboard/*.js
@ rm -rf ${BUILD_DIR}/kanboard/.docker
@ rm -rf ${BUILD_DIR}/kanboard/.dockerignore
@ rm -rf ${BUILD_DIR}/kanboard/docker
@ rm -rf ${BUILD_DIR}/kanboard/nitrous*
@ cd ${BUILD_DIR}/kanboard && find ./vendor -name doc -type d -exec rm -rf {} +;
@ cd ${BUILD_DIR}/kanboard && find ./vendor -name notes -type d -exec rm -rf {} +;
@ -58,7 +59,28 @@ test-postgres:
unittest: test-sqlite test-mysql test-postgres
test-browser:
@ phpunit -c tests/acceptance.xml
@ phpunit -c tests/acceptance.xml
integration-test-mysql:
@ composer install
@ docker-compose -f tests/docker/compose.integration.mysql.yaml build
@ docker-compose -f tests/docker/compose.integration.mysql.yaml up -d mysql app
@ docker-compose -f tests/docker/compose.integration.mysql.yaml up tests
@ docker-compose -f tests/docker/compose.integration.mysql.yaml down
integration-test-postgres:
@ composer install
@ docker-compose -f tests/docker/compose.integration.postgres.yaml build
@ docker-compose -f tests/docker/compose.integration.postgres.yaml up -d postgres app
@ docker-compose -f tests/docker/compose.integration.postgres.yaml up tests
@ docker-compose -f tests/docker/compose.integration.postgres.yaml down
integration-test-sqlite:
@ composer install
@ docker-compose -f tests/docker/compose.integration.sqlite.yaml build
@ docker-compose -f tests/docker/compose.integration.sqlite.yaml up -d app
@ docker-compose -f tests/docker/compose.integration.sqlite.yaml up tests
@ docker-compose -f tests/docker/compose.integration.sqlite.yaml down
sql:
@ pg_dump --schema-only --no-owner --no-privileges --quote-all-identifiers -n public --file app/Schema/Sql/postgres.sql kanboard

View File

@ -1,10 +1,8 @@
Kanboard
========
[![Build Status](https://travis-ci.org/fguillot/kanboard.svg)](https://travis-ci.org/fguillot/kanboard)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fguillot/kanboard/badges/quality-score.png?s=2b6490781608657cc8c43d02285bfafb4f489528)](https://scrutinizer-ci.com/g/fguillot/kanboard/)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/5e50750e-fc62-4a1f-b02a-71991123a2a7/mini.png)](https://insight.sensiolabs.com/projects/5e50750e-fc62-4a1f-b02a-71991123a2a7)
[![Gitter chat](https://badges.gitter.im/kanboard/gitter.png)](https://gitter.im/kanboard/kanboard)
[![Build Status](https://travis-ci.org/kanboard/kanboard.svg?branch=master)](https://travis-ci.org/kanboard/kanboard)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/kanboard/kanboard/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/kanboard/kanboard/?branch=master)
Kanboard is a project management software that focus on the Kanban methodology.
@ -15,16 +13,17 @@ Official website: <https://kanboard.net>
- Open source and self-hosted
- Super simple installation
- Translated in many languages
- Distributed under [MIT License](https://github.com/fguillot/kanboard/blob/master/LICENSE)
- Distributed under [MIT License](https://github.com/kanboard/kanboard/blob/master/LICENSE)
- The complete [list of features are available on the website](https://kanboard.net/features)
- [Change Log](https://github.com/fguillot/kanboard/blob/master/ChangeLog)
- [Documentation](https://github.com/fguillot/kanboard/blob/master/doc/index.markdown)
- [Change Log](https://github.com/kanboard/kanboard/blob/master/ChangeLog)
- [Documentation](https://github.com/kanboard/kanboard/blob/master/doc/index.markdown)
- IRC channel: [#kanboard](ircs://chat.freenode.net:6697/#kanboard) (Freenode)
Authors
-------
- Main developer: [Frédéric Guillot](https://github.com/fguillot)
- [List of contributors](https://github.com/fguillot/kanboard/blob/master/CONTRIBUTORS.md)
- [List of contributors](https://github.com/kanboard/kanboard/blob/master/CONTRIBUTORS.md)
Installation and Upgrade
------------------------

View File

@ -1,7 +1,7 @@
{
"name": "Kanboard",
"description": "Kanboard is a simple visual task board",
"repository": "https://github.com/fguillot/kanboard",
"repository": "https://github.com/kanboard/kanboard",
"logo": "https://kanboard.net/assets/img/icon.svg",
"keywords": ["kanboard", "kanban", "php", "agile"],
"addons": ["heroku-postgresql:hobby-dev"]

View File

@ -7,7 +7,7 @@ use Kanboard\Event\GenericEvent;
/**
* Base class for automatic actions
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
abstract class Base extends \Kanboard\Core\Base
@ -216,7 +216,8 @@ abstract class Base extends \Kanboard\Core\Base
*/
public function hasRequiredProject(array $data)
{
return isset($data['project_id']) && $data['project_id'] == $this->getProjectId();
return (isset($data['project_id']) && $data['project_id'] == $this->getProjectId()) ||
(isset($data['task']['project_id']) && $data['task']['project_id'] == $this->getProjectId());
}
/**
@ -226,10 +227,14 @@ abstract class Base extends \Kanboard\Core\Base
* @param array $data Event data dictionary
* @return bool True if all keys are there
*/
public function hasRequiredParameters(array $data)
public function hasRequiredParameters(array $data, array $parameters = array())
{
foreach ($this->getEventRequiredParameters() as $parameter) {
if (! isset($data[$parameter])) {
$parameters = $parameters ?: $this->getEventRequiredParameters();
foreach ($parameters as $key => $value) {
if (is_array($value)) {
return isset($data[$key]) && $this->hasRequiredParameters($data[$key], $value);
} else if (! isset($data[$value])) {
return false;
}
}

View File

@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Create automatically a comment from a webhook
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class CommentCreation extends Base

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Add a comment of the triggering event to the task description.
*
* @package action
* @package Kanboard\Action
* @author Oren Ben-Kiki
*/
class CommentCreationMoveTaskColumn extends Base
@ -55,7 +55,13 @@ class CommentCreationMoveTaskColumn extends Base
*/
public function getEventRequiredParameters()
{
return array('task_id', 'column_id');
return array(
'task_id',
'task' => array(
'column_id',
'project_id',
),
);
}
/**
@ -71,7 +77,7 @@ class CommentCreationMoveTaskColumn extends Base
return false;
}
$column = $this->columnModel->getById($data['column_id']);
$column = $this->columnModel->getById($data['task']['column_id']);
return (bool) $this->commentModel->create(array(
'comment' => t('Moved to column %s', $column['title']),
@ -89,6 +95,6 @@ class CommentCreationMoveTaskColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
return $data['task']['column_id'] == $this->getParam('column_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Set a category automatically according to the color
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryColor extends Base
@ -60,7 +60,10 @@ class TaskAssignCategoryColor extends Base
{
return array(
'task_id',
'color_id',
'task' => array(
'project_id',
'color_id',
),
);
}
@ -90,6 +93,6 @@ class TaskAssignCategoryColor extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['color_id'] == $this->getParam('color_id');
return $data['task']['color_id'] == $this->getParam('color_id');
}
}

View File

@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Set a category automatically according to a label
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryLabel extends Base

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskLinkModel;
/**
* Set a category automatically according to a task link
*
* @package action
* @package Kanboard\Action
* @author Olivier Maridat
* @author Frederic Guillot
*/
@ -60,8 +60,10 @@ class TaskAssignCategoryLink extends Base
public function getEventRequiredParameters()
{
return array(
'task_id',
'link_id',
'task_link' => array(
'task_id',
'link_id',
)
);
}
@ -75,7 +77,7 @@ class TaskAssignCategoryLink extends Base
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'id' => $data['task_link']['task_id'],
'category_id' => $this->getParam('category_id'),
);
@ -91,9 +93,8 @@ class TaskAssignCategoryLink extends Base
*/
public function hasRequiredCondition(array $data)
{
if ($data['link_id'] == $this->getParam('link_id')) {
$task = $this->taskFinderModel->getById($data['task_id']);
return empty($task['category_id']);
if ($data['task_link']['link_id'] == $this->getParam('link_id')) {
return empty($data['task']['category_id']);
}
return false;

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific category
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorCategory extends Base
@ -60,7 +60,10 @@ class TaskAssignColorCategory extends Base
{
return array(
'task_id',
'category_id',
'task' => array(
'project_id',
'category_id',
),
);
}
@ -90,6 +93,6 @@ class TaskAssignColorCategory extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['category_id'] == $this->getParam('category_id');
return $data['task']['category_id'] == $this->getParam('category_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a task
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorColumn extends Base
@ -61,7 +61,10 @@ class TaskAssignColorColumn extends Base
{
return array(
'task_id',
'column_id',
'task' => array(
'project_id',
'column_id',
),
);
}
@ -91,6 +94,6 @@ class TaskAssignColorColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
return $data['task']['column_id'] == $this->getParam('column_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskLinkModel;
/**
* Assign a color to a specific task link
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorLink extends Base
@ -59,8 +59,10 @@ class TaskAssignColorLink extends Base
public function getEventRequiredParameters()
{
return array(
'task_id',
'link_id',
'task_link' => array(
'task_id',
'link_id',
)
);
}
@ -74,7 +76,7 @@ class TaskAssignColorLink extends Base
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'id' => $data['task_link']['task_id'],
'color_id' => $this->getParam('color_id'),
);
@ -90,6 +92,6 @@ class TaskAssignColorLink extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['link_id'] == $this->getParam('link_id');
return $data['task_link']['link_id'] == $this->getParam('link_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a priority
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorPriority extends Base
@ -60,7 +60,10 @@ class TaskAssignColorPriority extends Base
{
return array(
'task_id',
'priority',
'task' => array(
'project_id',
'priority',
),
);
}
@ -90,6 +93,6 @@ class TaskAssignColorPriority extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['priority'] == $this->getParam('priority');
return $data['task']['priority'] == $this->getParam('priority');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific user
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorUser extends Base
@ -61,7 +61,10 @@ class TaskAssignColorUser extends Base
{
return array(
'task_id',
'owner_id',
'task' => array(
'project_id',
'owner_id',
),
);
}
@ -91,6 +94,6 @@ class TaskAssignColorUser extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['owner_id'] == $this->getParam('user_id');
return $data['task']['owner_id'] == $this->getParam('user_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUser extends Base

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user on column change
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUserColumn extends Base
@ -59,7 +59,10 @@ class TaskAssignCurrentUserColumn extends Base
{
return array(
'task_id',
'column_id',
'task' => array(
'project_id',
'column_id',
),
);
}
@ -93,6 +96,6 @@ class TaskAssignCurrentUserColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
return $data['task']['column_id'] == $this->getParam('column_id');
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Set the due date of task
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignDueDateOnCreation extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Automatically set the due date on task creation');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
),
);
}
/**
* Execute the action (set the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'date_due' => strtotime('+'.$this->getParam('duration').'days'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return true;
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to a specific user
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignSpecificUser extends Base
@ -61,7 +61,10 @@ class TaskAssignSpecificUser extends Base
{
return array(
'task_id',
'column_id',
'task' => array(
'project_id',
'column_id',
),
);
}
@ -91,6 +94,6 @@ class TaskAssignSpecificUser extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
return $data['task']['column_id'] == $this->getParam('column_id');
}
}

View File

@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Assign a task to someone
*
* @package action
* @package Kanboard\Actionv
* @author Frederic Guillot
*/
class TaskAssignUser extends Base

View File

@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Close automatically a task
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskClose extends Base

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Close automatically a task in a specific column
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseColumn extends Base
@ -55,7 +55,13 @@ class TaskCloseColumn extends Base
*/
public function getEventRequiredParameters()
{
return array('task_id', 'column_id');
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
)
);
}
/**
@ -79,6 +85,6 @@ class TaskCloseColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
return $data['task']['column_id'] == $this->getParam('column_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Close automatically a task after when inactive
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNoActivity extends Base

View File

@ -0,0 +1,96 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Close automatically a task after inactive and in an defined column
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNoActivityColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Close a task when there is no activity in an specific column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days'),
'column_id' => t('Column')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$max = $this->getParam('duration') * 86400;
foreach ($data['tasks'] as $task) {
$duration = time() - $task['date_modification'];
if ($duration > $max && $task['column_id'] == $this->getParam('column_id')) {
$results[] = $this->taskStatusModel->close($task['id']);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}

View File

@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Create automatically a task from a webhook
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCreation extends Base
@ -52,6 +52,7 @@ class TaskCreation extends Base
public function getEventRequiredParameters()
{
return array(
'project_id',
'reference',
'title',
);

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Duplicate a task to another project
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskDuplicateAnotherProject extends Base
@ -62,7 +62,10 @@ class TaskDuplicateAnotherProject extends Base
{
return array(
'task_id',
'column_id',
'task' => array(
'project_id',
'column_id',
)
);
}
@ -76,7 +79,12 @@ class TaskDuplicateAnotherProject extends Base
public function doAction(array $data)
{
$destination_column_id = $this->columnModel->getFirstColumnId($this->getParam('project_id'));
return (bool) $this->taskDuplicationModel->duplicateToProject($data['task_id'], $this->getParam('project_id'), null, $destination_column_id);
return (bool) $this->taskProjectDuplicationModel->duplicateToProject(
$data['task_id'],
$this->getParam('project_id'),
null,
$destination_column_id
);
}
/**
@ -88,6 +96,6 @@ class TaskDuplicateAnotherProject extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id');
return $data['task']['column_id'] == $this->getParam('column_id') && $data['task']['project_id'] != $this->getParam('project_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Email a task to someone
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmail extends Base
@ -62,7 +62,10 @@ class TaskEmail extends Base
{
return array(
'task_id',
'column_id',
'task' => array(
'project_id',
'column_id',
),
);
}
@ -78,13 +81,14 @@ class TaskEmail extends Base
$user = $this->userModel->getById($this->getParam('user_id'));
if (! empty($user['email'])) {
$task = $this->taskFinderModel->getDetails($data['task_id']);
$this->emailClient->send(
$user['email'],
$user['name'] ?: $user['username'],
$this->getParam('subject'),
$this->template->render('notification/task_create', array('task' => $task, 'application_url' => $this->configModel->get('application_url')))
$this->template->render('notification/task_create', array(
'task' => $data['task'],
'application_url' => $this->configModel->get('application_url'),
))
);
return true;
@ -102,6 +106,6 @@ class TaskEmail extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
return $data['task']['column_id'] == $this->getParam('column_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Email a task with no activity
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmailNoActivity extends Base

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another project
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveAnotherProject extends Base
@ -61,8 +61,10 @@ class TaskMoveAnotherProject extends Base
{
return array(
'task_id',
'column_id',
'project_id',
'task' => array(
'project_id',
'column_id',
)
);
}
@ -75,7 +77,7 @@ class TaskMoveAnotherProject extends Base
*/
public function doAction(array $data)
{
return $this->taskDuplicationModel->moveToProject($data['task_id'], $this->getParam('project_id'));
return $this->taskProjectMoveModel->moveToProject($data['task_id'], $this->getParam('project_id'));
}
/**
@ -87,6 +89,6 @@ class TaskMoveAnotherProject extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id');
return $data['task']['column_id'] == $this->getParam('column_id') && $data['task']['project_id'] != $this->getParam('project_id');
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is set
*
* @package action
* @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnAssigned extends Base
@ -61,8 +61,13 @@ class TaskMoveColumnAssigned extends Base
{
return array(
'task_id',
'column_id',
'owner_id'
'task' => array(
'project_id',
'column_id',
'owner_id',
'position',
'swimlane_id',
)
);
}
@ -75,14 +80,12 @@ class TaskMoveColumnAssigned extends Base
*/
public function doAction(array $data)
{
$original_task = $this->taskFinderModel->getById($data['task_id']);
return $this->taskPositionModel->movePosition(
$data['project_id'],
$data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$original_task['position'],
$original_task['swimlane_id'],
$data['task']['position'],
$data['task']['swimlane_id'],
false
);
}
@ -96,6 +99,6 @@ class TaskMoveColumnAssigned extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('src_column_id') && $data['owner_id'] > 0;
return $data['task']['column_id'] == $this->getParam('src_column_id') && $data['task']['owner_id'] > 0;
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when the category is changed
*
* @package action
* @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnCategoryChange extends Base
@ -60,8 +60,13 @@ class TaskMoveColumnCategoryChange extends Base
{
return array(
'task_id',
'column_id',
'category_id',
'task' => array(
'project_id',
'column_id',
'category_id',
'position',
'swimlane_id',
)
);
}
@ -74,14 +79,12 @@ class TaskMoveColumnCategoryChange extends Base
*/
public function doAction(array $data)
{
$original_task = $this->taskFinderModel->getById($data['task_id']);
return $this->taskPositionModel->movePosition(
$data['project_id'],
$data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$original_task['position'],
$original_task['swimlane_id'],
$data['task']['position'],
$data['task']['swimlane_id'],
false
);
}
@ -95,6 +98,6 @@ class TaskMoveColumnCategoryChange extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] != $this->getParam('dest_column_id') && $data['category_id'] == $this->getParam('category_id');
return $data['task']['column_id'] != $this->getParam('dest_column_id') && $data['task']['category_id'] == $this->getParam('category_id');
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when the task is closed
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveColumnClosed extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when closed');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CLOSE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'dest_column_id' => t('Destination column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'swimlane_id',
'is_active',
)
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskPositionModel->movePosition(
$data['task']['project_id'],
$data['task']['id'],
$this->getParam('dest_column_id'),
1,
$data['task']['swimlane_id'],
false,
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] != $this->getParam('dest_column_id') && $data['task']['is_active'] == 0;
}
}

View File

@ -0,0 +1,104 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when not moved during a given period
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveColumnNotMovedPeriod extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when not moved during a given period');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days'),
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$max = $this->getParam('duration') * 86400;
foreach ($data['tasks'] as $task) {
$duration = time() - $task['date_moved'];
if ($duration > $max && $task['column_id'] == $this->getParam('src_column_id')) {
$results[] = $this->taskPositionModel->movePosition(
$task['project_id'],
$task['id'],
$this->getParam('dest_column_id'),
1,
$task['swimlane_id'],
false
);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is cleared
*
* @package action
* @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnUnAssigned extends Base
@ -61,8 +61,13 @@ class TaskMoveColumnUnAssigned extends Base
{
return array(
'task_id',
'column_id',
'owner_id'
'task' => array(
'project_id',
'column_id',
'owner_id',
'position',
'swimlane_id',
)
);
}
@ -75,14 +80,12 @@ class TaskMoveColumnUnAssigned extends Base
*/
public function doAction(array $data)
{
$original_task = $this->taskFinderModel->getById($data['task_id']);
return $this->taskPositionModel->movePosition(
$data['project_id'],
$data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$original_task['position'],
$original_task['swimlane_id'],
$data['task']['position'],
$data['task']['swimlane_id'],
false
);
}
@ -96,6 +99,6 @@ class TaskMoveColumnUnAssigned extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('src_column_id') && $data['owner_id'] == 0;
return $data['task']['column_id'] == $this->getParam('src_column_id') && $data['task']['owner_id'] == 0;
}
}

View File

@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Open automatically a task
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskOpen extends Base

View File

@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Set the start date of task
*
* @package action
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskUpdateStartDate extends Base
@ -59,7 +59,10 @@ class TaskUpdateStartDate extends Base
{
return array(
'task_id',
'column_id',
'task' => array(
'project_id',
'column_id',
),
);
}
@ -89,6 +92,6 @@ class TaskUpdateStartDate extends Base
*/
public function hasRequiredCondition(array $data)
{
return $data['column_id'] == $this->getParam('column_id');
return $data['task']['column_id'] == $this->getParam('column_id');
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class ActionAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ActionAuthorization extends ProjectAuthorization
{
public function check($class, $method, $action_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->actionModel->getProjectId($action_id));
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class CategoryAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class CategoryAuthorization extends ProjectAuthorization
{
public function check($class, $method, $category_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->categoryModel->getProjectId($category_id));
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class ColumnAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ColumnAuthorization extends ProjectAuthorization
{
public function check($class, $method, $column_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->columnModel->getProjectId($column_id));
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class CommentAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class CommentAuthorization extends ProjectAuthorization
{
public function check($class, $method, $comment_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->commentModel->getProjectId($comment_id));
}
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class ProcedureAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ProcedureAuthorization extends Base
{
private $userSpecificProcedures = array(
'getMe',
'getMyDashboard',
'getMyActivityStream',
'createMyPrivateProject',
'getMyProjectsList',
'getMyProjects',
'getMyOverdueTasks',
);
public function check($procedure)
{
if (! $this->userSession->isLogged() && in_array($procedure, $this->userSpecificProcedures)) {
throw new AccessDeniedException('This procedure is not available with the API credentials');
}
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class ProjectAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ProjectAuthorization extends Base
{
public function check($class, $method, $project_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $project_id);
}
}
protected function checkProjectPermission($class, $method, $project_id)
{
if (empty($project_id)) {
throw new AccessDeniedException('Project not found');
}
$role = $this->projectUserRoleModel->getUserRole($project_id, $this->userSession->getId());
if (! $this->apiProjectAuthorization->isAllowed($class, $method, $role)) {
throw new AccessDeniedException('Project access denied');
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class SubtaskAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class SubtaskAuthorization extends ProjectAuthorization
{
public function check($class, $method, $subtask_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->subtaskModel->getProjectId($subtask_id));
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskAuthorization extends ProjectAuthorization
{
public function check($class, $method, $category_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($category_id));
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskFileAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskFileAuthorization extends ProjectAuthorization
{
public function check($class, $method, $file_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskFileModel->getProjectId($file_id));
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskLinkAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskLinkAuthorization extends ProjectAuthorization
{
public function check($class, $method, $task_link_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskLinkModel->getProjectId($task_link_id));
}
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class UserAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class UserAuthorization extends Base
{
public function check($class, $method)
{
if ($this->userSession->isLogged() && ! $this->apiAuthorization->isAllowed($class, $method, $this->userSession->getRole())) {
throw new AccessDeniedException('You are not allowed to access to this resource');
}
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace Kanboard\Api;
/**
* Board API controller
*
* @package Kanboard\Api
* @author Frederic Guillot
*/
class BoardApi extends BaseApi
{
public function getBoard($project_id)
{
$this->checkProjectPermission($project_id);
return $this->boardModel->getBoard($project_id);
}
}

View File

@ -13,46 +13,8 @@ use Kanboard\Core\Base;
* @package Kanboard\Api\Middleware
* @author Frederic Guillot
*/
class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
class AuthenticationMiddleware extends Base implements MiddlewareInterface
{
private $user_allowed_procedures = array(
'getMe',
'getMyDashboard',
'getMyActivityStream',
'createMyPrivateProject',
'getMyProjectsList',
'getMyProjects',
'getMyOverdueTasks',
);
private $both_allowed_procedures = array(
'getTimezone',
'getVersion',
'getDefaultTaskColor',
'getDefaultTaskColors',
'getColorList',
'getProjectById',
'getSubTask',
'getTask',
'getTaskByReference',
'getTimeSpent',
'getAllTasks',
'getAllSubTasks',
'hasTimer',
'logStartTime',
'logEndTime',
'openTask',
'closeTask',
'moveTaskPosition',
'createTask',
'createSubtask',
'updateTask',
'getBoard',
'getProjectActivity',
'getOverdueTasksByProject',
'searchTasks',
);
/**
* Execute Middleware
*
@ -68,11 +30,8 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
$this->dispatcher->dispatch('app.bootstrap');
if ($this->isUserAuthenticated($username, $password)) {
$this->checkProcedurePermission(true, $procedureName);
$this->userSession->initialize($this->userModel->getByUsername($username));
} elseif ($this->isAppAuthenticated($username, $password)) {
$this->checkProcedurePermission(false, $procedureName);
} else {
} elseif (! $this->isAppAuthenticated($username, $password)) {
$this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailureException('Wrong credentials');
}
@ -120,18 +79,4 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
return $this->configModel->get('api_token');
}
public function checkProcedurePermission($is_user, $procedure)
{
$is_both_procedure = in_array($procedure, $this->both_allowed_procedures);
$is_user_procedure = in_array($procedure, $this->user_allowed_procedures);
if ($is_user && ! $is_both_procedure && ! $is_user_procedure) {
throw new AccessDeniedException('Permission denied');
} elseif (! $is_user && ! $is_both_procedure && $is_user_procedure) {
throw new AccessDeniedException('Permission denied');
}
$this->logger->debug('API call: '.$procedure);
}
}

View File

@ -1,16 +1,17 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use Kanboard\Api\Authorization\ActionAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Action API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ActionApi extends Base
class ActionProcedure extends BaseProcedure
{
public function getAvailableActions()
{
@ -29,16 +30,19 @@ class ActionApi extends Base
public function removeAction($action_id)
{
ActionAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAction', $action_id);
return $this->actionModel->remove($action_id);
}
public function getActions($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActions', $project_id);
return $this->actionModel->getAllByProject($project_id);
}
public function createAction($project_id, $event_name, $action_name, array $params)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createAction', $project_id);
$values = array(
'project_id' => $project_id,
'event_name' => $event_name,

View File

@ -1,16 +1,14 @@
<?php
namespace Kanboard\Api;
use Kanboard\Core\Base;
namespace Kanboard\Api\Procedure;
/**
* App API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class AppApi extends Base
class AppProcedure extends BaseProcedure
{
public function getTimezone()
{

View File

@ -1,30 +1,24 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Api\Authorization\ProcedureAuthorization;
use Kanboard\Api\Authorization\UserAuthorization;
use Kanboard\Core\Base;
use ReflectionClass;
/**
* Base class
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
abstract class BaseApi extends Base
abstract class BaseProcedure extends Base
{
public function checkProjectPermission($project_id)
public function beforeProcedure($procedure)
{
if ($this->userSession->isLogged() && ! $this->projectPermissionModel->isUserAllowed($project_id, $this->userSession->getId())) {
throw new AccessDeniedException('Permission denied');
}
}
public function checkTaskPermission($task_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($this->taskFinderModel->getProjectId($task_id));
}
ProcedureAuthorization::getInstance($this->container)->check($procedure);
UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure);
}
protected function formatTask($task)
@ -71,4 +65,21 @@ abstract class BaseApi extends Base
return $projects;
}
protected function filterValues(array $values)
{
foreach ($values as $key => $value) {
if (is_null($value)) {
unset($values[$key]);
}
}
return $values;
}
protected function getClassName()
{
$reflection = new ReflectionClass(get_called_class());
return $reflection->getShortName();
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Formatter\BoardFormatter;
/**
* Board API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class BoardProcedure extends BaseProcedure
{
public function getBoard($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id);
return BoardFormatter::getInstance($this->container)
->withProjectId($project_id)
->withQuery($this->taskFinderModel->getExtendedQuery())
->format();
}
}

View File

@ -1,34 +1,40 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use Kanboard\Api\Authorization\CategoryAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Category API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class CategoryApi extends Base
class CategoryProcedure extends BaseProcedure
{
public function getCategory($category_id)
{
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'getCategory', $category_id);
return $this->categoryModel->getById($category_id);
}
public function getAllCategories($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllCategories', $project_id);
return $this->categoryModel->getAll($project_id);
}
public function removeCategory($category_id)
{
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeCategory', $category_id);
return $this->categoryModel->remove($category_id);
}
public function createCategory($project_id, $name)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createCategory', $project_id);
$values = array(
'project_id' => $project_id,
'name' => $name,
@ -40,6 +46,8 @@ class CategoryApi extends Base
public function updateCategory($id, $name)
{
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id);
$values = array(
'id' => $id,
'name' => $name,

View File

@ -1,42 +1,51 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ColumnAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Column API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ColumnApi extends BaseApi
class ColumnProcedure extends BaseProcedure
{
public function getColumns($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumns', $project_id);
return $this->columnModel->getAll($project_id);
}
public function getColumn($column_id)
{
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumn', $column_id);
return $this->columnModel->getById($column_id);
}
public function updateColumn($column_id, $title, $task_limit = 0, $description = '')
{
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateColumn', $column_id);
return $this->columnModel->update($column_id, $title, $task_limit, $description);
}
public function addColumn($project_id, $title, $task_limit = 0, $description = '')
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addColumn', $project_id);
return $this->columnModel->create($project_id, $title, $task_limit, $description);
}
public function removeColumn($column_id)
{
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeColumn', $column_id);
return $this->columnModel->remove($column_id);
}
public function changeColumnPosition($project_id, $column_id, $position)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeColumnPosition', $project_id);
return $this->columnModel->changePosition($project_id, $column_id, $position);
}
}

View File

@ -1,34 +1,40 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use Kanboard\Api\Authorization\CommentAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
/**
* Comment API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class CommentApi extends Base
class CommentProcedure extends BaseProcedure
{
public function getComment($comment_id)
{
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'getComment', $comment_id);
return $this->commentModel->getById($comment_id);
}
public function getAllComments($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllComments', $task_id);
return $this->commentModel->getAll($task_id);
}
public function removeComment($comment_id)
{
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeComment', $comment_id);
return $this->commentModel->remove($comment_id);
}
public function createComment($task_id, $user_id, $content, $reference = '')
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createComment', $task_id);
$values = array(
'task_id' => $task_id,
'user_id' => $user_id,
@ -43,6 +49,8 @@ class CommentApi extends Base
public function updateComment($id, $content)
{
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id);
$values = array(
'id' => $id,
'comment' => $content,

View File

@ -1,16 +1,14 @@
<?php
namespace Kanboard\Api;
use Kanboard\Core\Base;
namespace Kanboard\Api\Procedure;
/**
* Group Member API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class GroupMemberApi extends Base
class GroupMemberProcedure extends BaseProcedure
{
public function getMemberGroups($user_id)
{

View File

@ -1,16 +1,14 @@
<?php
namespace Kanboard\Api;
use Kanboard\Core\Base;
namespace Kanboard\Api\Procedure;
/**
* Group API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class GroupApi extends Base
class GroupProcedure extends BaseProcedure
{
public function createGroup($name, $external_id = '')
{

View File

@ -1,16 +1,14 @@
<?php
namespace Kanboard\Api;
use Kanboard\Core\Base;
namespace Kanboard\Api\Procedure;
/**
* Link API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class LinkApi extends Base
class LinkProcedure extends BaseProcedure
{
/**
* Get a link by id

View File

@ -1,16 +1,16 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Model\SubtaskModel;
/**
* Me API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class MeApi extends BaseApi
class MeProcedure extends BaseProcedure
{
public function getMe()
{

View File

@ -0,0 +1,68 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
* Project File API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectFileProcedure extends BaseProcedure
{
public function getProjectFile($project_id, $file_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectFile', $project_id);
return $this->projectFileModel->getById($file_id);
}
public function getAllProjectFiles($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllProjectFiles', $project_id);
return $this->projectFileModel->getAll($project_id);
}
public function downloadProjectFile($project_id, $file_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadProjectFile', $project_id);
try {
$file = $this->projectFileModel->getById($file_id);
if (! empty($file)) {
return base64_encode($this->objectStorage->get($file['path']));
}
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
return '';
}
public function createProjectFile($project_id, $filename, $blob)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createProjectFile', $project_id);
try {
return $this->projectFileModel->uploadContent($project_id, $filename, $blob);
} catch (ObjectStorageException $e) {
$this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
public function removeProjectFile($project_id, $file_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectFile', $project_id);
return $this->projectFileModel->remove($file_id);
}
public function removeAllProjectFiles($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllProjectFiles', $project_id);
return $this->projectFileModel->removeAll($project_id);
}
}

View File

@ -1,73 +1,69 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Core\Security\Role;
/**
* Project Permission API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectPermissionApi extends Base
class ProjectPermissionProcedure extends BaseProcedure
{
public function getProjectUsers($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUsers', $project_id);
return $this->projectUserRoleModel->getAllUsers($project_id);
}
public function getAssignableUsers($project_id, $prepend_unassigned = false)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAssignableUsers', $project_id);
return $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned);
}
public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectUser', $project_id);
return $this->projectUserRoleModel->addUser($project_id, $user_id, $role);
}
public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectGroup', $project_id);
return $this->projectGroupRoleModel->addGroup($project_id, $group_id, $role);
}
public function removeProjectUser($project_id, $user_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectUser', $project_id);
return $this->projectUserRoleModel->removeUser($project_id, $user_id);
}
public function removeProjectGroup($project_id, $group_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectGroup', $project_id);
return $this->projectGroupRoleModel->removeGroup($project_id, $group_id);
}
public function changeProjectUserRole($project_id, $user_id, $role)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectUserRole', $project_id);
return $this->projectUserRoleModel->changeUserRole($project_id, $user_id, $role);
}
public function changeProjectGroupRole($project_id, $group_id, $role)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectGroupRole', $project_id);
return $this->projectGroupRoleModel->changeGroupRole($project_id, $group_id, $role);
}
// Deprecated
public function getMembers($project_id)
public function getProjectUserRole($project_id, $user_id)
{
return $this->getProjectUsers($project_id);
}
// Deprecated
public function revokeUser($project_id, $user_id)
{
return $this->removeProjectUser($project_id, $user_id);
}
// Deprecated
public function allowUser($project_id, $user_id)
{
return $this->addProjectUser($project_id, $user_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUserRole', $project_id);
return $this->projectUserRoleModel->getUserRole($project_id, $user_id);
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Project API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectProcedure extends BaseProcedure
{
public function getProjectById($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectById', $project_id);
return $this->formatProject($this->projectModel->getById($project_id));
}
public function getProjectByName($name)
{
$project = $this->projectModel->getByName($name);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByName', $project['id']);
return $this->formatProject($project);
}
public function getProjectByIdentifier($identifier)
{
$project = $this->formatProject($this->projectModel->getByIdentifier($identifier));
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByIdentifier', $project['id']);
return $this->formatProject($project);
}
public function getAllProjects()
{
return $this->formatProjects($this->projectModel->getAll());
}
public function removeProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProject', $project_id);
return $this->projectModel->remove($project_id);
}
public function enableProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProject', $project_id);
return $this->projectModel->enable($project_id);
}
public function disableProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProject', $project_id);
return $this->projectModel->disable($project_id);
}
public function enableProjectPublicAccess($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProjectPublicAccess', $project_id);
return $this->projectModel->enablePublicAccess($project_id);
}
public function disableProjectPublicAccess($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProjectPublicAccess', $project_id);
return $this->projectModel->disablePublicAccess($project_id);
}
public function getProjectActivities(array $project_ids)
{
foreach ($project_ids as $project_id) {
ProjectAuthorization::getInstance($this->container)
->check($this->getClassName(), 'getProjectActivities', $project_id);
}
return $this->helper->projectActivity->getProjectsEvents($project_ids);
}
public function getProjectActivity($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectActivity', $project_id);
return $this->helper->projectActivity->getProjectEvents($project_id);
}
public function createProject($name, $description = null, $owner_id = 0, $identifier = null)
{
$values = $this->filterValues(array(
'name' => $name,
'description' => $description,
'identifier' => $identifier,
));
list($valid, ) = $this->projectValidator->validateCreation($values);
return $valid ? $this->projectModel->create($values, $owner_id, $this->userSession->isLogged()) : false;
}
public function updateProject($project_id, $name = null, $description = null, $owner_id = null, $identifier = null)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateProject', $project_id);
$values = $this->filterValues(array(
'id' => $project_id,
'name' => $name,
'description' => $description,
'owner_id' => $owner_id,
'identifier' => $identifier,
));
list($valid, ) = $this->projectValidator->validateModification($values);
return $valid && $this->projectModel->update($values);
}
}

View File

@ -1,34 +1,40 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use Kanboard\Api\Authorization\SubtaskAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
/**
* Subtask API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class SubtaskApi extends Base
class SubtaskProcedure extends BaseProcedure
{
public function getSubtask($subtask_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtask', $subtask_id);
return $this->subtaskModel->getById($subtask_id);
}
public function getAllSubtasks($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSubtasks', $task_id);
return $this->subtaskModel->getAll($task_id);
}
public function removeSubtask($subtask_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSubtask', $subtask_id);
return $this->subtaskModel->remove($subtask_id);
}
public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createSubtask', $task_id);
$values = array(
'title' => $title,
'task_id' => $task_id,
@ -44,6 +50,8 @@ class SubtaskApi extends Base
public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSubtask', $task_id);
$values = array(
'id' => $id,
'task_id' => $task_id,

View File

@ -0,0 +1,39 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\SubtaskAuthorization;
/**
* Subtask Time Tracking API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
* @author Nikolaos Georgakis
*/
class SubtaskTimeTrackingProcedure extends BaseProcedure
{
public function hasSubtaskTimer($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'hasSubtaskTimer', $subtask_id);
return $this->subtaskTimeTrackingModel->hasTimer($subtask_id, $user_id);
}
public function setSubtaskStartTime($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskStartTime', $subtask_id);
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id);
}
public function setSubtaskEndTime($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskEndTime', $subtask_id);
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id);
}
public function getSubtaskTimeSpent($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id);
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id);
}
}

View File

@ -1,34 +1,39 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Swimlane API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class SwimlaneApi extends Base
class SwimlaneProcedure extends BaseProcedure
{
public function getActiveSwimlanes($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActiveSwimlanes', $project_id);
return $this->swimlaneModel->getSwimlanes($project_id);
}
public function getAllSwimlanes($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSwimlanes', $project_id);
return $this->swimlaneModel->getAll($project_id);
}
public function getSwimlaneById($swimlane_id)
{
return $this->swimlaneModel->getById($swimlane_id);
$swimlane = $this->swimlaneModel->getById($swimlane_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneById', $swimlane['project_id']);
return $swimlane;
}
public function getSwimlaneByName($project_id, $name)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneByName', $project_id);
return $this->swimlaneModel->getByName($project_id, $name);
}
@ -39,11 +44,13 @@ class SwimlaneApi extends Base
public function getDefaultSwimlane($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getDefaultSwimlane', $project_id);
return $this->swimlaneModel->getDefault($project_id);
}
public function addSwimlane($project_id, $name, $description = '')
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addSwimlane', $project_id);
return $this->swimlaneModel->create(array('project_id' => $project_id, 'name' => $name, 'description' => $description));
}
@ -60,21 +67,25 @@ class SwimlaneApi extends Base
public function removeSwimlane($project_id, $swimlane_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSwimlane', $project_id);
return $this->swimlaneModel->remove($project_id, $swimlane_id);
}
public function disableSwimlane($project_id, $swimlane_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableSwimlane', $project_id);
return $this->swimlaneModel->disable($project_id, $swimlane_id);
}
public function enableSwimlane($project_id, $swimlane_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableSwimlane', $project_id);
return $this->swimlaneModel->enable($project_id, $swimlane_id);
}
public function changeSwimlanePosition($project_id, $swimlane_id, $position)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeSwimlanePosition', $project_id);
return $this->swimlaneModel->changePosition($project_id, $swimlane_id, $position);
}
}

View File

@ -0,0 +1,106 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Core\ExternalLink\ExternalLinkManager;
use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
/**
* Task External Link API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskExternalLinkProcedure extends BaseProcedure
{
public function getExternalTaskLinkTypes()
{
return $this->externalLinkManager->getTypes();
}
public function getExternalTaskLinkProviderDependencies($providerName)
{
try {
return $this->externalLinkManager->getProvider($providerName)->getDependencies();
} catch (ExternalLinkProviderNotFound $e) {
$this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
public function getExternalTaskLinkById($task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLink', $task_id);
return $this->taskExternalLinkModel->getById($link_id);
}
public function getAllExternalTaskLinks($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLinks', $task_id);
return $this->taskExternalLinkModel->getAll($task_id);
}
public function createExternalTaskLink($task_id, $url, $dependency, $type = ExternalLinkManager::TYPE_AUTO, $title = '')
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createExternalTaskLink', $task_id);
try {
$provider = $this->externalLinkManager
->setUserInputText($url)
->setUserInputType($type)
->find();
$link = $provider->getLink();
$values = array(
'task_id' => $task_id,
'title' => $title ?: $link->getTitle(),
'url' => $link->getUrl(),
'link_type' => $provider->getType(),
'dependency' => $dependency,
);
list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
if (! $valid) {
$this->logger->error(__METHOD__.': '.var_export($errors));
return false;
}
return $this->taskExternalLinkModel->create($values);
} catch (ExternalLinkProviderNotFound $e) {
$this->logger->error(__METHOD__.': '.$e->getMessage());
}
return false;
}
public function updateExternalTaskLink($task_id, $link_id, $title = null, $url = null, $dependency = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateExternalTaskLink', $task_id);
$link = $this->taskExternalLinkModel->getById($link_id);
$values = $this->filterValues(array(
'title' => $title,
'url' => $url,
'dependency' => $dependency,
));
$values = array_merge($link, $values);
list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
if (! $valid) {
$this->logger->error(__METHOD__.': '.var_export($errors));
return false;
}
return $this->taskExternalLinkModel->update($values);
}
public function removeExternalTaskLink($task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeExternalTaskLink', $task_id);
return $this->taskExternalLinkModel->remove($link_id);
}
}

View File

@ -1,29 +1,36 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Api\Authorization\TaskFileAuthorization;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
* File API controller
* Task File API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class FileApi extends BaseApi
class TaskFileProcedure extends BaseProcedure
{
public function getTaskFile($file_id)
{
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskFile', $file_id);
return $this->taskFileModel->getById($file_id);
}
public function getAllTaskFiles($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskFiles', $task_id);
return $this->taskFileModel->getAll($task_id);
}
public function downloadTaskFile($file_id)
{
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id);
try {
$file = $this->taskFileModel->getById($file_id);
@ -33,59 +40,31 @@ class FileApi extends BaseApi
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
return '';
}
public function createTaskFile($project_id, $task_id, $filename, $blob)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $project_id);
try {
return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
$this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
public function removeTaskFile($file_id)
{
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskFile', $file_id);
return $this->taskFileModel->remove($file_id);
}
public function removeAllTaskFiles($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllTaskFiles', $task_id);
return $this->taskFileModel->removeAll($task_id);
}
// Deprecated procedures
public function getFile($file_id)
{
return $this->getTaskFile($file_id);
}
public function getAllFiles($task_id)
{
return $this->getAllTaskFiles($task_id);
}
public function downloadFile($file_id)
{
return $this->downloadTaskFile($file_id);
}
public function createFile($project_id, $task_id, $filename, $blob)
{
return $this->createTaskFile($project_id, $task_id, $filename, $blob);
}
public function removeFile($file_id)
{
return $this->removeTaskFile($file_id);
}
public function removeAllFiles($task_id)
{
return $this->removeAllTaskFiles($task_id);
}
}

View File

@ -1,16 +1,17 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Api\Authorization\TaskLinkAuthorization;
/**
* TaskLink API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskLinkApi extends Base
class TaskLinkProcedure extends BaseProcedure
{
/**
* Get a task link
@ -21,6 +22,7 @@ class TaskLinkApi extends Base
*/
public function getTaskLinkById($task_link_id)
{
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id);
return $this->taskLinkModel->getById($task_link_id);
}
@ -33,6 +35,7 @@ class TaskLinkApi extends Base
*/
public function getAllTaskLinks($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $task_id);
return $this->taskLinkModel->getAll($task_id);
}
@ -47,6 +50,7 @@ class TaskLinkApi extends Base
*/
public function createTaskLink($task_id, $opposite_task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskLink', $task_id);
return $this->taskLinkModel->create($task_id, $opposite_task_id, $link_id);
}
@ -62,6 +66,7 @@ class TaskLinkApi extends Base
*/
public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTaskLink', $task_id);
return $this->taskLinkModel->update($task_link_id, $task_id, $opposite_task_id, $link_id);
}
@ -74,6 +79,7 @@ class TaskLinkApi extends Base
*/
public function removeTaskLink($task_link_id)
{
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id);
return $this->taskLinkModel->remove($task_link_id);
}
}

View File

@ -1,39 +1,41 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\TaskModel;
/**
* Task API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskApi extends BaseApi
class TaskProcedure extends BaseProcedure
{
public function searchTasks($project_id, $query)
{
$this->checkProjectPermission($project_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'searchTasks', $project_id);
return $this->taskLexer->build($query)->withFilter(new TaskProjectFilter($project_id))->toArray();
}
public function getTask($task_id)
{
$this->checkTaskPermission($task_id);
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id);
return $this->formatTask($this->taskFinderModel->getById($task_id));
}
public function getTaskByReference($project_id, $reference)
{
$this->checkProjectPermission($project_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskByReference', $project_id);
return $this->formatTask($this->taskFinderModel->getByReference($project_id, $reference));
}
public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN)
{
$this->checkProjectPermission($project_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id);
return $this->formatTasks($this->taskFinderModel->getAll($project_id, $status_id));
}
@ -44,41 +46,44 @@ class TaskApi extends BaseApi
public function getOverdueTasksByProject($project_id)
{
$this->checkProjectPermission($project_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getOverdueTasksByProject', $project_id);
return $this->taskFinderModel->getOverdueTasksByProject($project_id);
}
public function openTask($task_id)
{
$this->checkTaskPermission($task_id);
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'openTask', $task_id);
return $this->taskStatusModel->open($task_id);
}
public function closeTask($task_id)
{
$this->checkTaskPermission($task_id);
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'closeTask', $task_id);
return $this->taskStatusModel->close($task_id);
}
public function removeTask($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTask', $task_id);
return $this->taskModel->remove($task_id);
}
public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0)
{
$this->checkProjectPermission($project_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $project_id);
return $this->taskPositionModel->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id);
}
public function moveTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{
return $this->taskDuplicationModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $project_id);
return $this->taskProjectMoveModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
}
public function duplicateTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{
return $this->taskDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $project_id);
return $this->taskProjectDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
}
public function createTask($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0,
@ -86,8 +91,8 @@ class TaskApi extends BaseApi
$recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
$recurrence_basedate = 0, $reference = '')
{
$this->checkProjectPermission($project_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id);
if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
return false;
}
@ -127,8 +132,7 @@ class TaskApi extends BaseApi
$recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
$recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
{
$this->checkTaskPermission($id);
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id);
$project_id = $this->taskFinderModel->getProjectId($id);
if ($project_id === 0) {
@ -139,7 +143,7 @@ class TaskApi extends BaseApi
return false;
}
$values = array(
$values = $this->filterValues(array(
'id' => $id,
'title' => $title,
'color_id' => $color_id,
@ -155,13 +159,7 @@ class TaskApi extends BaseApi
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
);
foreach ($values as $key => $value) {
if (is_null($value)) {
unset($values[$key]);
}
}
));
list($valid) = $this->taskValidator->validateApiModification($values);
return $valid && $this->taskModificationModel->update($values);

View File

@ -1,8 +1,7 @@
<?php
namespace Kanboard\Api;
namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
use LogicException;
use Kanboard\Core\Security\Role;
use Kanboard\Core\Ldap\Client as LdapClient;
@ -12,10 +11,10 @@ use Kanboard\Core\Ldap\User as LdapUser;
/**
* User API controller
*
* @package Kanboard\Api
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class UserApi extends Base
class UserProcedure extends BaseProcedure
{
public function getUser($user_id)
{
@ -118,19 +117,13 @@ class UserApi extends Base
public function updateUser($id, $username = null, $name = null, $email = null, $role = null)
{
$values = array(
$values = $this->filterValues(array(
'id' => $id,
'username' => $username,
'name' => $name,
'email' => $email,
'role' => $role,
);
foreach ($values as $key => $value) {
if (is_null($value)) {
unset($values[$key]);
}
}
));
list($valid, ) = $this->userValidator->validateApiModification($values);
return $valid && $this->userModel->update($values);

View File

@ -1,87 +0,0 @@
<?php
namespace Kanboard\Api;
/**
* Project API controller
*
* @package Kanboard\Api
* @author Frederic Guillot
*/
class ProjectApi extends BaseApi
{
public function getProjectById($project_id)
{
$this->checkProjectPermission($project_id);
return $this->formatProject($this->projectModel->getById($project_id));
}
public function getProjectByName($name)
{
return $this->formatProject($this->projectModel->getByName($name));
}
public function getAllProjects()
{
return $this->formatProjects($this->projectModel->getAll());
}
public function removeProject($project_id)
{
return $this->projectModel->remove($project_id);
}
public function enableProject($project_id)
{
return $this->projectModel->enable($project_id);
}
public function disableProject($project_id)
{
return $this->projectModel->disable($project_id);
}
public function enableProjectPublicAccess($project_id)
{
return $this->projectModel->enablePublicAccess($project_id);
}
public function disableProjectPublicAccess($project_id)
{
return $this->projectModel->disablePublicAccess($project_id);
}
public function getProjectActivities(array $project_ids)
{
return $this->helper->projectActivity->getProjectsEvents($project_ids);
}
public function getProjectActivity($project_id)
{
$this->checkProjectPermission($project_id);
return $this->helper->projectActivity->getProjectEvents($project_id);
}
public function createProject($name, $description = null)
{
$values = array(
'name' => $name,
'description' => $description
);
list($valid, ) = $this->projectValidator->validateCreation($values);
return $valid ? $this->projectModel->create($values) : false;
}
public function updateProject($id, $name, $description = null)
{
$values = array(
'id' => $id,
'name' => $name,
'description' => $description
);
list($valid, ) = $this->projectValidator->validateModification($values);
return $valid && $this->projectModel->update($values);
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace Kanboard\Api;
use Kanboard\Core\Base;
/**
* Subtask Time Tracking API controller
*
* @package api
* @author Nikolaos Georgakis
*/
class SubtaskTimeTrackingApi extends Base
{
public function hasTimer($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->hasTimer($subtask_id,$user_id);
}
public function logStartTime($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id,$user_id);
}
public function logEndTime($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id,$user_id);
}
public function getTimeSpent($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id,$user_id);
}
}

View File

@ -45,7 +45,8 @@ class ReverseProxyAuth extends Base implements PreAuthenticationProviderInterfac
$username = $this->request->getRemoteUser();
if (! empty($username)) {
$this->userInfo = new ReverseProxyUserProvider($username);
$userProfile = $this->userModel->getByUsername($username);
$this->userInfo = new ReverseProxyUserProvider($username, $userProfile ?: array());
return true;
}

View File

@ -123,7 +123,8 @@ class TotpAuth extends Base implements PostAuthenticationProviderInterface
return '';
}
return GoogleAuthenticator::getQrCodeUrl('totp', $label, $this->secret);
$options = array('issuer' => TOTP_ISSUER);
return GoogleAuthenticator::getQrCodeUrl('totp', $label, $this->secret, null, $options);
}
/**
@ -139,6 +140,7 @@ class TotpAuth extends Base implements PostAuthenticationProviderInterface
return '';
}
return GoogleAuthenticator::getKeyUri('totp', $label, $this->secret);
$options = array('issuer' => TOTP_ISSUER);
return GoogleAuthenticator::getKeyUri('totp', $label, $this->secret, null, $options);
}
}

View File

@ -149,7 +149,7 @@ class TaskOverdueNotificationCommand extends BaseCommand
$this->userNotificationModel->sendUserNotification(
$user,
TaskModel::EVENT_OVERDUE,
array('tasks' => $user_tasks, 'project_name' => implode(", ", $project_names))
array('tasks' => $user_tasks, 'project_name' => implode(', ', $project_names))
);
}
}

View File

@ -81,7 +81,7 @@ class ActionCreationController extends BaseController
'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($project['id']),
'links_list' => $this->linkModel->getList(0, false),
'priorities_list' => $this->projectModel->getPriorities($project),
'priorities_list' => $this->projectTaskPriorityModel->getPriorities($project),
'project' => $project,
'available_actions' => $this->actionManager->getAvailableActions(),
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),

View File

@ -33,7 +33,7 @@ class AnalyticController extends BaseController
'metrics' => $this->projectDailyStatsModel->getRawMetrics($project['id'], $from, $to),
'date_format' => $this->configModel->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
'title' => t('Lead and Cycle time for "%s"', $project['name']),
'title' => t('Lead and cycle time'),
)));
}
@ -60,7 +60,7 @@ class AnalyticController extends BaseController
'project' => $project,
'paginator' => $paginator,
'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']),
'title' => t('Compare hours for "%s"', $project['name']),
'title' => t('Estimated vs actual time'),
)));
}
@ -76,7 +76,7 @@ class AnalyticController extends BaseController
$this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array(
'project' => $project,
'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']),
'title' => t('Average time spent into each column for "%s"', $project['name']),
'title' => t('Average time into each column'),
)));
}
@ -92,7 +92,7 @@ class AnalyticController extends BaseController
$this->response->html($this->helper->layout->analytic('analytic/tasks', array(
'project' => $project,
'metrics' => $this->taskDistributionAnalytic->build($project['id']),
'title' => t('Task repartition for "%s"', $project['name']),
'title' => t('Task distribution'),
)));
}
@ -108,7 +108,7 @@ class AnalyticController extends BaseController
$this->response->html($this->helper->layout->analytic('analytic/users', array(
'project' => $project,
'metrics' => $this->userDistributionAnalytic->build($project['id']),
'title' => t('User repartition for "%s"', $project['name']),
'title' => t('User repartition'),
)));
}
@ -119,7 +119,7 @@ class AnalyticController extends BaseController
*/
public function cfd()
{
$this->commonAggregateMetrics('analytic/cfd', 'total', 'Cumulative flow diagram for "%s"');
$this->commonAggregateMetrics('analytic/cfd', 'total', t('Cumulative flow diagram'));
}
/**
@ -129,7 +129,7 @@ class AnalyticController extends BaseController
*/
public function burndown()
{
$this->commonAggregateMetrics('analytic/burndown', 'score', 'Burndown chart for "%s"');
$this->commonAggregateMetrics('analytic/burndown', 'score', t('Burndown chart'));
}
/**
@ -157,7 +157,7 @@ class AnalyticController extends BaseController
'project' => $project,
'date_format' => $this->configModel->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
'title' => t($title, $project['name']),
'title' => $title,
)));
}

View File

@ -134,7 +134,7 @@ class BoardAjaxController extends BaseController
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($this->userSession->getFilters($project_id))
->format(BoardFormatter::getInstance($this->container)->setProjectId($project_id))
->format(BoardFormatter::getInstance($this->container)->withProjectId($project_id))
));
}
}

View File

@ -107,9 +107,9 @@ class BoardTooltipController extends BaseController
$this->response->html($this->template->render('task_recurrence/info', array(
'task' => $task,
'recurrence_trigger_list' => $this->taskModel->getRecurrenceTriggerList(),
'recurrence_timeframe_list' => $this->taskModel->getRecurrenceTimeframeList(),
'recurrence_basedate_list' => $this->taskModel->getRecurrenceBasedateList(),
'recurrence_trigger_list' => $this->taskRecurrenceModel->getRecurrenceTriggerList(),
'recurrence_timeframe_list' => $this->taskRecurrenceModel->getRecurrenceTimeframeList(),
'recurrence_basedate_list' => $this->taskRecurrenceModel->getRecurrenceBasedateList(),
)));
}

View File

@ -30,7 +30,11 @@ class BoardViewController extends BaseController
$this->response->html($this->helper->layout->app('board/view_public', array(
'project' => $project,
'swimlanes' => $this->boardModel->getBoard($project['id']),
'swimlanes' => BoardFormatter::getInstance($this->container)
->withProjectId($project['id'])
->withQuery($this->taskFinderModel->getExtendedQuery())
->format()
,
'title' => $project['name'],
'description' => $project['description'],
'no_layout' => true,
@ -59,7 +63,7 @@ class BoardViewController extends BaseController
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($search)
->format(BoardFormatter::getInstance($this->container)->setProjectId($project['id']))
->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id']))
)));
}
}

View File

@ -66,7 +66,15 @@ class ColumnController extends BaseController
list($valid, $errors) = $this->columnValidator->validateCreation($values);
if ($valid) {
if ($this->columnModel->create($project['id'], $values['title'], $values['task_limit'], $values['description']) !== false) {
$result = $this->columnModel->create(
$project['id'],
$values['title'],
$values['task_limit'],
$values['description'],
isset($values['hide_in_dashboard']) ? $values['hide_in_dashboard'] : 0
);
if ($result !== false) {
$this->flash->success(t('Column created successfully.'));
return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true);
} else {
@ -111,7 +119,15 @@ class ColumnController extends BaseController
list($valid, $errors) = $this->columnValidator->validateModification($values);
if ($valid) {
if ($this->columnModel->update($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
$result = $this->columnModel->update(
$values['id'],
$values['title'],
$values['task_limit'],
$values['description'],
isset($values['hide_in_dashboard']) ? $values['hide_in_dashboard'] : 0
);
if ($result) {
$this->flash->success(t('Board updated successfully.'));
return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])));
} else {

View File

@ -2,9 +2,6 @@
namespace Kanboard\Controller;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\SubtaskModel;
/**
* Dashboard Controller
*
@ -13,63 +10,6 @@ use Kanboard\Model\SubtaskModel;
*/
class DashboardController extends BaseController
{
/**
* Get project pagination
*
* @access private
* @param integer $user_id
* @param string $action
* @param integer $max
* @return \Kanboard\Core\Paginator
*/
private function getProjectPaginator($user_id, $action, $max)
{
return $this->paginator
->setUrl('DashboardController', $action, array('pagination' => 'projects', 'user_id' => $user_id))
->setMax($max)
->setOrder(ProjectModel::TABLE.'.name')
->setQuery($this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id)))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
}
/**
* Get task pagination
*
* @access private
* @param integer $user_id
* @param string $action
* @param integer $max
* @return \Kanboard\Core\Paginator
*/
private function getTaskPaginator($user_id, $action, $max)
{
return $this->paginator
->setUrl('DashboardController', $action, array('pagination' => 'tasks', 'user_id' => $user_id))
->setMax($max)
->setOrder('tasks.id')
->setQuery($this->taskFinderModel->getUserQuery($user_id))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
}
/**
* Get subtask pagination
*
* @access private
* @param integer $user_id
* @param string $action
* @param integer $max
* @return \Kanboard\Core\Paginator
*/
private function getSubtaskPaginator($user_id, $action, $max)
{
return $this->paginator
->setUrl('DashboardController', $action, array('pagination' => 'subtasks', 'user_id' => $user_id))
->setMax($max)
->setOrder('tasks.id')
->setQuery($this->subtaskModel->getUserQuery($user_id, array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS)))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
}
/**
* Dashboard overview
*
@ -80,10 +20,10 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/show', array(
'title' => t('Dashboard'),
'project_paginator' => $this->getProjectPaginator($user['id'], 'show', 10),
'task_paginator' => $this->getTaskPaginator($user['id'], 'show', 10),
'subtask_paginator' => $this->getSubtaskPaginator($user['id'], 'show', 10),
'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)),
'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', 10),
'task_paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'show', 10),
'subtask_paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'show', 10),
'user' => $user,
)));
}
@ -98,8 +38,8 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/tasks', array(
'title' => t('My tasks'),
'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50),
'title' => t('Tasks overview for %s', $this->helper->user->getFullname($user)),
'paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'tasks', 50),
'user' => $user,
)));
}
@ -114,8 +54,8 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array(
'title' => t('My subtasks'),
'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50),
'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)),
'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'subtasks', 50),
'user' => $user,
)));
}
@ -130,8 +70,8 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/projects', array(
'title' => t('My projects'),
'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25),
'title' => t('Projects overview for %s', $this->helper->user->getFullname($user)),
'paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'projects', 25),
'user' => $user,
)));
}
@ -146,7 +86,7 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/activity', array(
'title' => t('My activity stream'),
'title' => t('Activity stream for %s', $this->helper->user->getFullname($user)),
'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']), 100),
'user' => $user,
)));
@ -159,9 +99,11 @@ class DashboardController extends BaseController
*/
public function calendar()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/calendar', array(
'title' => t('My calendar'),
'user' => $this->getUser(),
'title' => t('Calendar for %s', $this->helper->user->getFullname($user)),
'user' => $user,
)));
}
@ -175,7 +117,7 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/notifications', array(
'title' => t('My notifications'),
'title' => t('Notifications for %s', $this->helper->user->getFullname($user)),
'notifications' => $this->userUnreadNotificationModel->getAll($user['id']),
'user' => $user,
)));

View File

@ -20,16 +20,7 @@ class DocumentationController extends BaseController
$page = 'index';
}
if ($this->languageModel->getCurrentLanguage() === 'fr_FR') {
$filename = __DIR__.'/../../doc/fr/' . $page . '.markdown';
} else {
$filename = __DIR__ . '/../../doc/' . $page . '.markdown';
}
if (!file_exists($filename)) {
$filename = __DIR__.'/../../doc/index.markdown';
}
$filename = $this->getPageFilename($page);
$this->response->html($this->helper->layout->app('doc/show', $this->render($filename)));
}
@ -83,10 +74,63 @@ class DocumentationController extends BaseController
*/
public function replaceImageUrl(array $matches)
{
if ($this->languageModel->getCurrentLanguage() === 'fr_FR') {
return '('.$this->helper->url->base().'doc/fr/'.$matches[1].')';
return '('.$this->getFileBaseUrl($matches[1]).')';
}
/**
* Get Markdown file according to the current language
*
* @access private
* @param string $page
* @return string
*/
private function getPageFilename($page)
{
return $this->getFileLocation($page . '.markdown') ?:
implode(DIRECTORY_SEPARATOR, array(ROOT_DIR, 'doc', 'index.markdown'));
}
/**
* Get base URL for Markdown links
*
* @access private
* @param string $filename
* @return string
*/
private function getFileBaseUrl($filename)
{
$language = $this->languageModel->getCurrentLanguage();
$path = $this->getFileLocation($filename);
if (strpos($path, $language) !== false) {
$url = implode('/', array('doc', $language, $filename));
} else {
$url = implode('/', array('doc', $filename));
}
return '('.$this->helper->url->base().'doc/'.$matches[1].')';
return $this->helper->url->base().$url;
}
/**
* Get file location according to the current language
*
* @access private
* @param string $filename
* @return string
*/
private function getFileLocation($filename)
{
$files = array(
implode(DIRECTORY_SEPARATOR, array(ROOT_DIR, 'doc', $this->languageModel->getCurrentLanguage(), $filename)),
implode(DIRECTORY_SEPARATOR, array(ROOT_DIR, 'doc', $filename)),
);
foreach ($files as $filename) {
if (file_exists($filename)) {
return $filename;
}
}
return '';
}
}

View File

@ -31,22 +31,23 @@ class ExportController extends BaseController
$data = $this->$model->$method($project['id'], $from, $to);
$this->response->withFileDownload($filename.'.csv');
$this->response->csv($data);
}
} else {
$this->response->html($this->helper->layout->project('export/'.$action, array(
'values' => array(
'controller' => 'ExportController',
'action' => $action,
'project_id' => $project['id'],
'from' => $from,
'to' => $to,
),
'errors' => array(),
'date_format' => $this->configModel->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
'project' => $project,
'title' => $page_title,
), 'export/sidebar'));
$this->response->html($this->helper->layout->project('export/'.$action, array(
'values' => array(
'controller' => 'ExportController',
'action' => $action,
'project_id' => $project['id'],
'from' => $from,
'to' => $to,
),
'errors' => array(),
'date_format' => $this->configModel->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
'project' => $project,
'title' => $page_title,
), 'export/sidebar'));
}
}
/**
@ -76,7 +77,7 @@ class ExportController extends BaseController
*/
public function summary()
{
$this->common('projectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
$this->common('projectDailyColumnStatsModel', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
}
/**

Some files were not shown because too many files have changed in this diff Show More