mirror of
https://github.com/YunoHost-Apps/kanboard_ynh.git
synced 2024-09-03 19:36:17 +02:00
Download sources during installation and upgrade to 1.0.36 (#38)
* Download sources (except dependencies) during installation and upgrade to 1.0.35 * Download dependencies during installation Upgrade to 1.0.36 * Download kanboard zip from kanboard.net website * Take review into account
This commit is contained in:
parent
26e016a46b
commit
7ae3c03fb1
1832 changed files with 67 additions and 195207 deletions
|
@ -33,7 +33,7 @@ From command line:
|
|||
|
||||
Infos
|
||||
-----
|
||||
Kanboard v1.0.31
|
||||
Kanboard v1.0.36
|
||||
|
||||
Yunohost forum thread: <https://forum.yunohost.org/t/kanboard-package/78>
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<?php
|
||||
|
||||
// Data folder (must be writeable by the web server user)
|
||||
define('DATA_DIR', 'data');
|
||||
|
||||
// Enable/Disable debug
|
||||
define('DEBUG', false);
|
||||
|
||||
|
@ -7,13 +10,25 @@ define('DEBUG', false);
|
|||
define('LOG_DRIVER', '');
|
||||
|
||||
// Log filename if the log driver is "file"
|
||||
define('LOG_FILE', __DIR__.DIRECTORY_SEPARATOR.'data'.DIRECTORY_SEPARATOR.'debug.log');
|
||||
define('LOG_FILE', DATA_DIR.DIRECTORY_SEPARATOR.'debug.log');
|
||||
|
||||
// Plugins directory
|
||||
define('PLUGINS_DIR', 'plugins');
|
||||
|
||||
// Folder for uploaded files
|
||||
define('FILES_DIR', 'data'.DIRECTORY_SEPARATOR.'files');
|
||||
// Plugins directory URL
|
||||
define('PLUGIN_API_URL', 'https://kanboard.net/plugins.json');
|
||||
|
||||
// Enable/Disable plugin installer
|
||||
define('PLUGIN_INSTALLER', true);
|
||||
|
||||
// Available cache drivers are "file" and "memory"
|
||||
define('CACHE_DRIVER', 'memory');
|
||||
|
||||
// Cache folder to use if cache driver is "file" (must be writeable by the web server user)
|
||||
define('CACHE_DIR', DATA_DIR.DIRECTORY_SEPARATOR.'cache');
|
||||
|
||||
// Folder for uploaded files (must be writeable by the web server user)
|
||||
define('FILES_DIR', DATA_DIR.DIRECTORY_SEPARATOR.'files');
|
||||
|
||||
// E-mail address for the "From" header (notifications)
|
||||
define('MAIL_FROM', 'yuno_email');
|
||||
|
@ -31,6 +46,11 @@ define('MAIL_SMTP_ENCRYPTION', null); // Valid values are "null", "ssl" or "tls"
|
|||
// Sendmail command to use when the transport is "sendmail"
|
||||
define('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs');
|
||||
|
||||
// Run automatically database migrations
|
||||
// If set to false, you will have to run manually the SQL migrations from the CLI during the next Kanboard upgrade
|
||||
// Do not run the migrations from multiple processes at the same time (example: web page + background worker)
|
||||
define('DB_RUN_MIGRATIONS', true);
|
||||
|
||||
// Database driver: sqlite, mysql or postgres (sqlite by default)
|
||||
define('DB_DRIVER', 'mysql');
|
||||
|
||||
|
@ -183,7 +203,7 @@ define('ENABLE_URL_REWRITE', false);
|
|||
// Hide login form, useful if all your users use Google/Github/ReverseProxy authentication
|
||||
define('HIDE_LOGIN_FORM', true);
|
||||
|
||||
// Disabling logout (for external SSO authentication)
|
||||
// Disabling logout (useful for external SSO authentication)
|
||||
define('DISABLE_LOGOUT', true);
|
||||
|
||||
// Enable captcha after 3 authentication failure
|
||||
|
@ -204,3 +224,6 @@ define('HTTP_PROXY_HOSTNAME', '');
|
|||
define('HTTP_PROXY_PORT', '3128');
|
||||
define('HTTP_PROXY_USERNAME', '');
|
||||
define('HTTP_PROXY_PASSWORD', '');
|
||||
|
||||
// TOTP (2FA) issuer name
|
||||
define('TOTP_ISSUER', 'Kanboard');
|
28
scripts/_common.sh
Normal file
28
scripts/_common.sh
Normal file
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# Common variables
|
||||
#
|
||||
|
||||
# Application version
|
||||
VERSION="1.0.36"
|
||||
|
||||
# Remote URL to fetch application source archive
|
||||
APPLICATION_SOURCE_URL="https://kanboard.net/kanboard-${VERSION}.zip"
|
||||
|
||||
#
|
||||
# Common helpers
|
||||
#
|
||||
|
||||
# Download and extract application sources to the given directory
|
||||
# usage: extract_application_to DESTDIR
|
||||
extract_application() {
|
||||
local DESTDIR=$1
|
||||
TMPDIR=$(mktemp -d)
|
||||
chmod 755 $TMPDIR
|
||||
archive="${TMPDIR}/application.zip"
|
||||
wget -q -O "$archive" "$APPLICATION_SOURCE_URL" \
|
||||
|| ynh_die "Unable to download application archive"
|
||||
unzip -qq "$archive" -d "$TMPDIR" \
|
||||
|| ynh_die "Unable to extract application archive"
|
||||
rm "$archive"
|
||||
sudo rsync -a "$TMPDIR"/*/* "$DESTDIR"
|
||||
}
|
|
@ -4,6 +4,9 @@ set -eu
|
|||
|
||||
app="kanboard"
|
||||
|
||||
# Source local helpers
|
||||
source ./_common.sh
|
||||
|
||||
# Source app helpers
|
||||
source /usr/share/yunohost/helpers
|
||||
|
||||
|
@ -42,9 +45,7 @@ ynh_app_setting_set $app mysqlpwd $dbpass
|
|||
ynh_app_setting_set $app adminusername $admin
|
||||
ynh_app_setting_set $app is_public $is_public
|
||||
|
||||
# Copy sources
|
||||
sudo mkdir -p $DESTDIR
|
||||
sudo cp -a ../sources/. $DESTDIR
|
||||
extract_application "$DESTDIR"
|
||||
|
||||
# Copy and edit config.php
|
||||
sudo cp ../conf/config.php ${DESTDIR}
|
||||
|
@ -83,6 +84,7 @@ then
|
|||
fi
|
||||
|
||||
# Init database
|
||||
#sudo chmod o+x ${DESTDIR} ${DESTDIR}/app/ ${DESTDIR}/app/Schema/ ${DESTDIR}/app/Schema/Sql
|
||||
ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" < ${DESTDIR}/app/Schema/Sql/mysql.sql
|
||||
|
||||
# Reload services
|
||||
|
|
|
@ -4,6 +4,9 @@ set -eu
|
|||
|
||||
app="kanboard"
|
||||
|
||||
# Source local helpers
|
||||
source ./_common.sh
|
||||
|
||||
# Set app specific variables
|
||||
dbname=$app
|
||||
dbuser=$app
|
||||
|
@ -36,10 +39,11 @@ sudo rm -rf /var/lib/php5/session/*
|
|||
# Move old app dir
|
||||
sudo mv ${DESTDIR} ${DESTDIR}.old
|
||||
|
||||
sudo mkdir -p ${DESTDIR}
|
||||
sudo cp -a ../sources/. ${DESTDIR}
|
||||
extract_application "$DESTDIR"
|
||||
|
||||
# restore data
|
||||
sudo cp -a ${DESTDIR}.old/data ${DESTDIR}
|
||||
|
||||
# restore plugins
|
||||
if [ -e ${DESTDIR}.old/plugins ]
|
||||
then
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
Options -MultiViews
|
||||
|
||||
SetEnv HTTP_MOD_REWRITE On
|
||||
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [QSA,L]
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch "(kanboard|config.php|config.default.php)">
|
||||
<IfModule mod_version.c>
|
||||
<IfVersion >= 2.3>
|
||||
Require all denied
|
||||
</IfVersion>
|
||||
<IfVersion < 2.3>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfVersion>
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_version.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</FilesMatch>
|
|
@ -1,637 +0,0 @@
|
|||
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
|
||||
|
||||
Version 1.0.30
|
||||
--------------
|
||||
|
||||
Improvements:
|
||||
|
||||
* Show tasks that are due today in a different color
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fixed wrong controller for search in dashboard
|
||||
* Fixed plural form in alert message
|
||||
* Fixed CSS cosmetic issue with popover and tooltips
|
||||
|
||||
Version 1.0.29
|
||||
--------------
|
||||
|
||||
New features:
|
||||
|
||||
* Manage plugin from the user interface and from the command line
|
||||
* Added support for background workers
|
||||
* Added the possibility to convert a subtask to a task
|
||||
* Added menu entry to add tasks from all project views
|
||||
* Add tasks in bulk from the board
|
||||
* Add dropdown for projects
|
||||
* Added config parameter to allow self-signed certificates for the HTTP client
|
||||
|
||||
Improvements:
|
||||
|
||||
* Display local date format in date picker
|
||||
* Configure email settings with the user interface in addition to config file
|
||||
* Upgrade Docker image to Alpine Linux 3.4
|
||||
* Move task import to a separate section
|
||||
* Mark web notification as read when clicking on it
|
||||
* Support strtotime strings for date search
|
||||
* Reset failed login counter and unlock user when changing password
|
||||
* Task do not open anymore in a new window on the Gantt chart
|
||||
* Do not display task progress for tasks with no start/end date
|
||||
* Use Gulp and Bower to manage assets
|
||||
* Controller and Middleware refactoring
|
||||
* Replace jQuery mobile detection by the library isMobile
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fixed user date format parsing for dates that can be valid in multiple formats
|
||||
* Do not sync user role if LDAP groups are not configured
|
||||
* Fixed issue with unicode handling for letter based avatars and user initials
|
||||
* Do not send notifications to disabled users
|
||||
* Fixed wrong redirect when removing a task from the task view page
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* Webhook to create tasks have been removed, use the API instead
|
||||
* All controllers have been renamed, people who are not using URL rewriting will see different URLs
|
||||
* All models have been renamed, plugin maintainers will have to update their plugins
|
||||
|
||||
Version 1.0.28
|
||||
--------------
|
||||
|
||||
New features:
|
||||
|
||||
* Added automated action to change task color based on the priority
|
||||
* Added support for LDAP Posix Groups (OpenLDAP with memberUid or groupOfNames)
|
||||
* Added support for LDAP user photo attribute (Avatar image)
|
||||
* Added support for language LDAP attribute
|
||||
* Added support for Mysql SSL connection
|
||||
* Search in activity stream
|
||||
* Search in comments
|
||||
* Search by task creator
|
||||
* Added command line utility to reset user password and to disable 2FA
|
||||
|
||||
Improvements:
|
||||
|
||||
* Improve Avatar upload form
|
||||
* User roles are now synced with LDAP at each login
|
||||
* Improve web page title on the task view
|
||||
* Unify task drop-down menu between different views
|
||||
* Improve LDAP user group membership synchronization
|
||||
* Category and user filters do not append anymore in search field
|
||||
* Added more template hooks
|
||||
* Added tasks search with the API
|
||||
* Added priority field to API procedures
|
||||
* Added API procedure "getMemberGroups"
|
||||
* Added parameters for overdue tasks notifications: group by projects and send only to managers
|
||||
* Allow people to install Kanboard outside of the DocumentRoot
|
||||
* Allow plugins to be loaded from another folder
|
||||
* Filter/Lexer/QueryBuilder refactoring
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Allow a project owner to manage his own public project
|
||||
* Fixed PHP warning when removing a user with no Avatar image
|
||||
* Fixed improper Markdown escaping for some tooltips
|
||||
* Closing all tasks by column, also update closed tasks
|
||||
* Fixed wrong task link generation within Markdown text
|
||||
* Fixed wrong URL on comment toggle link for sorting
|
||||
* Fixed form submission with Meta+Enter keyboard shortcut
|
||||
* Removed PHP notices in comment suppression view
|
||||
|
||||
Version 1.0.27
|
||||
--------------
|
||||
|
||||
New features:
|
||||
|
||||
* Added Markdown editor
|
||||
* Added user avatars with pluggable system
|
||||
- Default is a letter based avatar
|
||||
- Gravatar
|
||||
- Avatar Image upload
|
||||
* Added Korean translation
|
||||
|
||||
Improvements:
|
||||
|
||||
* Added more logging for LDAP client
|
||||
* Improve schema migration process
|
||||
* Improve notification configuration form
|
||||
* Handle state in OAuth2 client
|
||||
* Allow to use the original template in overridden templates
|
||||
* Unification of the project header
|
||||
* Refactoring of Javascript code
|
||||
* Improve comments design
|
||||
* Improve task summary sections
|
||||
* Put back the action sidebar in task view
|
||||
* Added support for multiple placeholders for LDAP_USER_FILTER
|
||||
* Added local file link provider
|
||||
* Show configuration in settings page
|
||||
* Added "?" to display list of keyboard shortcuts
|
||||
* Added new keyboard shortcuts for task view
|
||||
* Always display project name and task title in task views
|
||||
* Improve automatic action creation
|
||||
* Move notifications to the bottom of the screen
|
||||
* Added the possibility to import automatic actions from another project
|
||||
* Added Ajax loading icon for submit buttons
|
||||
* Added support for HTTP header "X-Forwarded-Proto: https"
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fix bad unique constraints in Mysql table user_has_notifications
|
||||
* Force integer type for aggregated metrics (Burndown chart concat values instead of summing)
|
||||
* Fixes cycle time calculation when the start date is defined in the future
|
||||
* Access allowed to any tasks from the shared public board by changing the URL parameters
|
||||
* Fix invalid user filter for API procedure createLdapUser()
|
||||
* Ambiguous column name with very old version of Sqlite
|
||||
|
||||
Version 1.0.26
|
||||
--------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* API procedures:
|
||||
- "moveColumnUp" and "moveColumnDown" are replaced by "changeColumnPosition"
|
||||
- "moveSwimlaneUp" and "moveSwimlaneDown" are replaced by "changeSwimlanePosition"
|
||||
|
||||
New features:
|
||||
|
||||
* Add drag and drop to change subtasks, swimlanes and columns positions
|
||||
* Add file drag and drop and asynchronous upload
|
||||
* Enable/Disable users
|
||||
* Add setting option to disable private projects
|
||||
* Add new config option to disable logout
|
||||
|
||||
Improvements:
|
||||
|
||||
* Use inline popup to create new columns
|
||||
* Improve filter box design
|
||||
* Improve image thumbnails and files table
|
||||
* Add confirmation inline popup to remove custom filter
|
||||
* Increase client_max_body_size value for Nginx
|
||||
* Split Board model into multiple classes
|
||||
* Improve logging for the Docker image
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fix PHP notices during creation of first project and in subtasks table
|
||||
* Fix filter dropdown not accessible when there are too many items
|
||||
* Fix regression: unable to change project in "task move/duplicate to another project"
|
||||
|
||||
Version 1.0.25
|
||||
--------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* Core functionalities moved to external plugins:
|
||||
- Google Auth: https://github.com/kanboard/plugin-google-auth
|
||||
- Github Auth: https://github.com/kanboard/plugin-github-auth
|
||||
- Gitlab Auth: https://github.com/kanboard/plugin-gitlab-auth
|
||||
|
||||
New features:
|
||||
|
||||
* When creating a new project, have the possibility to select another project to duplicate
|
||||
* Add a "Me" button to assignee form element
|
||||
* Add external links for tasks with plugin api
|
||||
* Add project owner (Directly Responsible Individual)
|
||||
* Add configurable task priority
|
||||
* Add Greek translation
|
||||
* Add automatic actions to close tasks with no activity
|
||||
* Add automatic actions to send an email when there is no activity on a task
|
||||
* Regroup all daily background tasks in one command: "cronjob"
|
||||
* Add task dropdown menu on listing pages
|
||||
|
||||
Improvements:
|
||||
|
||||
* New Dockerfile based on Alpine Linux and Nginx/PHP-FPM
|
||||
* The date time format can be chosen in application settings
|
||||
* Export only open tasks in iCal feed
|
||||
* Remove time form on task summary page and move that to task edit form
|
||||
* Replace box shadow by a larger border width when a task is recently modified
|
||||
* Do not refresh the whole page when changing subtask status
|
||||
* Add dropdown menu with inline popup for all task actions
|
||||
* Change sidebar style
|
||||
* Change task summary layout
|
||||
* Use inline popup for subtasks, categories, swimlanes, actions and columns
|
||||
* Move homepage menus to the user dropdown
|
||||
* Have a new task assigned to the creator by default instead of "no assignee"
|
||||
* Show progress for task links in board tooltips
|
||||
* Simplify code to handle ajax popover and redirects
|
||||
* Simplify layout and templates generation
|
||||
* Move task form elements to Task helper
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Category label is broken on the board if there's a url in the description
|
||||
* Fix pagination on task time tracking page
|
||||
|
||||
Version 1.0.24
|
||||
--------------
|
||||
|
||||
New features:
|
||||
|
||||
* Forgot Password
|
||||
* Add drop-down menu on each board column title to close all tasks
|
||||
* Add Malay language
|
||||
* Add new API procedures for groups, roles, project permissions and to move/duplicate tasks to another project
|
||||
|
||||
Improvements:
|
||||
|
||||
* Avoid to send XHR request when a task has not moved after a drag and drop
|
||||
* Set maximum dropzone height when the individual column scrolling is disabled
|
||||
* Always show the search box in board selector
|
||||
* Replace logout link by a drop-down menu
|
||||
* Handle notification for group members attached to a project
|
||||
* Return the highest role for a project when a user is member of multiple groups
|
||||
* Show in user interface the saving state of the task
|
||||
* Add drop-down menu for subtasks, categories, swimlanes, columns, custom filters, task links and groups
|
||||
* Add new template hooks
|
||||
* Application settings are not cached anymore in the session
|
||||
* Do not check board status during task move
|
||||
* Move validators to a separate namespace
|
||||
* Improve and write unit tests for reports
|
||||
* Reduce the number of SQL queries for project daily column stats
|
||||
* Remove event subscriber to update date_moved field
|
||||
* Make sure that some event subscribers are not executed multiple times
|
||||
* Show rendering time of individual templates when debug mode is enabled
|
||||
* Make sure that no events are fired if nothing has been modified in the task
|
||||
* Make dashboard section title clickable
|
||||
* Add unit tests for LastLogin
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Automatic action listeners were using the same instance
|
||||
* Fix wrong link for category in task footer
|
||||
* Unable to set currency rate with Postgres database
|
||||
* Avoid automatic actions that change the color to fire subsequent events
|
||||
* Unable to unassign a task from the API
|
||||
* Revert back previous optimizations of TaskPosition (incompatibility with some environment)
|
||||
|
||||
Version 1.0.23
|
||||
--------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* Plugin API changes for Automatic Actions
|
||||
* Automatic Action to close a task doesn't have the column parameter anymore (use the action "Close a task in a specific column")
|
||||
* Action name stored in the database is now the absolute class name
|
||||
* Core functionalities moved to external plugins:
|
||||
- Github Webhook: https://github.com/kanboard/plugin-github-webhook
|
||||
- Gitlab Webhook: https://github.com/kanboard/plugin-gitlab-webhook
|
||||
- Bitbucket Webhook: https://github.com/kanboard/plugin-bitbucket-webhook
|
||||
|
||||
New features:
|
||||
|
||||
* Added support of user mentions (@username)
|
||||
* Added report to compare working hours between open and closed tasks
|
||||
* Added the possibility to define custom routes from plugins
|
||||
* Added new method to remove metadata
|
||||
|
||||
Improvements:
|
||||
|
||||
* Improve Two-Factor activation and plugin API
|
||||
* Improving performance during task position change (SQL queries are 3 times faster than before)
|
||||
* Do not show window scrollbars when individual column scrolling is enabled
|
||||
* Automatic Actions code improvements and unit tests
|
||||
* Increase action name column length in actions table
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fix compatibility issue with FreeBSD for session.hash_function parameter
|
||||
* Fix wrong constant name that causes a PHP error in project management section
|
||||
* Fix pagination in group members listing
|
||||
* Avoid PHP error when enabling LDAP group provider with PHP < 5.5
|
||||
|
||||
Version 1.0.22
|
||||
--------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* LDAP configuration parameters changes (See documentation)
|
||||
* SQL table changes:
|
||||
- "users" table: added new column "role" and removed columns "is_admin" and "is_project_admin"
|
||||
- "project_has_users" table: replaced column "is_owner" with column "role"
|
||||
- Sqlite does not support alter table, old columns still there but unused
|
||||
* API procedure changes:
|
||||
- createUser
|
||||
- createLdapUser
|
||||
- updateUser
|
||||
- updateTask
|
||||
* Event removed: "session.bootstrap", use "app.boostrap" instead
|
||||
|
||||
New features:
|
||||
|
||||
* Add pluggable authentication and authorization system (complete rewrite)
|
||||
* Add groups (teams/organization)
|
||||
* Add LDAP groups synchronization
|
||||
* Add project group permissions
|
||||
* Add new project role Viewer
|
||||
* Add generic LDAP client library
|
||||
* Add search query attribute for task link
|
||||
* Add the possibility to define API token in config file
|
||||
* Add capability to reopen Gitlab issues
|
||||
* Try to load config.php from /data if not available
|
||||
|
||||
Version 1.0.21
|
||||
--------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* Projects with duplicate names are now allowed:
|
||||
- For Postgres and Mysql the unique constraint is removed by database migration
|
||||
- However Sqlite does not support alter table, only new databases will have the unique constraint removed
|
||||
|
||||
New features:
|
||||
|
||||
* New automatic action: Assign a category based on a link
|
||||
* Added Bosnian translation
|
||||
|
||||
Improvements:
|
||||
|
||||
* Dropdown menu entries are now clickable outside of the html link
|
||||
* Improve error handling of plugins
|
||||
* Use PHP7 function random_bytes() to generate tokens if available
|
||||
* CSV task export show the assignee name in addition to the assignee username
|
||||
* Add new hooks for plugins
|
||||
* Remove workaround for "INSERT ON DUPLICATE KEY UPDATE..."
|
||||
|
||||
Internal code refactoring:
|
||||
|
||||
* Rewrite of session management
|
||||
* Move some classes to a new namespace Kanboard\Core\Http
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Loading cs_CZ locale display the wrong language in datetime picker
|
||||
* Datepicker is closed unexpectedly on blur event
|
||||
* Fix bug in daily project summary CSV export
|
||||
* Fix PHP error when adding a new user with email notification enabled
|
||||
* Add missing template for activity stream to show event "file.create"
|
||||
* Fix wrong value for PLUGINS_DIR in config.default.php
|
||||
* Make CSV export compatible with PHP 5.3
|
||||
* Avoid Safari to append .html at the end of downloaded files
|
||||
|
||||
Version 1.0.20
|
||||
--------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* Add namespace Kanboard (update your plugins)
|
||||
* Move Mailgun, Sendgrid, Postmark, Slack, Hipchat and Jabber to plugins
|
||||
* ReverseProxy authentication check for each request that the username match the user session
|
||||
|
||||
New features:
|
||||
|
||||
* Add CSV import for users and tasks
|
||||
* Add Task, User and Project metadata for plugin creators
|
||||
|
||||
Improvements:
|
||||
|
||||
* Allow to change comments sorting
|
||||
* Add the possibility to append or not custom filters
|
||||
* Make mail transports pluggable
|
||||
* Do not show scroll-bars when a column is collapsed on Windows systems
|
||||
* Regenerate thumbnails if missing
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* People should not see any tasks during a search when they are not associated to a project
|
||||
* Avoid disabling the default swimlane during renaming when there is no other activated swimlane
|
||||
|
||||
Version 1.0.19
|
||||
--------------
|
||||
|
||||
New features:
|
||||
|
||||
* Added web notifications
|
||||
* Added LDAP group sync
|
||||
* Added swimlane description
|
||||
* New plugin system (alpha)
|
||||
* Added Bahasa Indonesia translation
|
||||
* Added API procedures: getMyOverdueTasks, getOverdueTasksByProject and GetMyProjects
|
||||
* Added user API access for procedure getProjectActivity()
|
||||
* Added config parameter to enable/disable Syslog
|
||||
* Added custom filters
|
||||
* Added http client proxy support
|
||||
|
||||
Core functionalities moved to plugins:
|
||||
|
||||
* Budget planning: https://github.com/kanboard/plugin-budget
|
||||
* SubtaskForecast: https://github.com/kanboard/plugin-subtask-forecast
|
||||
* Timetable: https://github.com/kanboard/plugin-timetable
|
||||
|
||||
Improvements:
|
||||
|
||||
* When duplicating a task redirect to the new task
|
||||
* Include more shortcut links into the view "My projects"
|
||||
* Duplicate a project with tasks will copy the new tasks in the same columns
|
||||
* Offer alternative method to create Mysql and Postgres databases (import sql dump)
|
||||
* Make sure there is always a trailing slash for application_url
|
||||
* Do not show the checkbox "Show default swimlane" when there is no active swimlanes
|
||||
* Append filters instead of replacing value for users and categories drop-downs
|
||||
* Do not show empty swimlanes in public view
|
||||
* Change swimlane layout to save space on the screen
|
||||
* Add the possibility to set/unset max column height (column scrolling)
|
||||
* Show "Open this task" in drop-down menu for closed tasks
|
||||
* Show assignee on card only when someone is assigned (hide nobody text)
|
||||
* Highlight selected item in drop-down menus
|
||||
* Gantt chart: change bar color according to task progress
|
||||
* Replace color drop-down by color picker in task forms
|
||||
* Creating another task stay in the popover (no full page refresh anymore)
|
||||
* Avoid scrollbar in Gantt chart for row title on Windows platform
|
||||
* Remove unnecessary margin for calendar header
|
||||
* Show localized documentation if available
|
||||
* Add event subtask.delete
|
||||
* Add abstract storage layer
|
||||
* Add abstract cache layer
|
||||
* Add Docker tag for stable version
|
||||
|
||||
Others:
|
||||
|
||||
* Data directory permission are not checked anymore
|
||||
* Data directory is not mandatory anymore for people that use a remote database and remote object storage
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fix typo in template that prevents Gitlab OAuth link to be displayed
|
||||
* Fix Markdown preview links focus
|
||||
* Avoid drop-down menu to be truncated inside a column with scrolling
|
||||
* Deleting subtask doesn't update task time tracking
|
||||
* Fix Mysql error about gitlab_id when creating remote user
|
||||
* Fix subtask timer bug (event called recursively)
|
||||
* Fix Postgres issue "Cardinality violation" when there is multiple "is_milestone_of" links
|
||||
* Fix issue with due date greater than year 2038
|
||||
|
||||
Version 1.0.18
|
||||
--------------
|
||||
|
||||
New features:
|
||||
|
||||
* Include documentation in the application
|
||||
* Add Gitlab authentication
|
||||
* Add users and categories filters on the board
|
||||
* Add hide/show columns
|
||||
* Add Gantt chart for projects and tasks
|
||||
* Add new role "Project Administrator"
|
||||
* Add login brute force protection with captcha and account lockdown
|
||||
* Add new api procedures: getDefaultTaskColor(), getDefaultTaskColors() and getColorList()
|
||||
* Add user api access
|
||||
* Add config parameter to define session duration
|
||||
* Add config parameter to disable/enable RememberMe authentication
|
||||
* Add start/end date for projects
|
||||
* Add new automated action to change task color based on the task link
|
||||
* Add milestone marker in board task
|
||||
* Add search for task title when using an integer only input
|
||||
* Add Portuguese (European) translation
|
||||
* Add Norwegian translation
|
||||
|
||||
Improvements:
|
||||
|
||||
* Add handle to move tasks on touch devices
|
||||
* Improve file attachments tooltip on the board
|
||||
* Adjust automatically the height of the placeholder during drag and drop
|
||||
* Show all tasks when using no search criteria
|
||||
* Add column vertical scrolling
|
||||
* Set dynamically column height based on viewport size
|
||||
* Enable support for Github Enterprise when using Github Authentication
|
||||
* Update iCalendar library to display organizer name
|
||||
* Improve sidebar menus
|
||||
* Add no referrer policy in meta tags
|
||||
* Run automated unit tests with Sqlite/Mysql/Postgres on Travis-ci
|
||||
* Add Makefile and remove the "scripts" directory
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Wrong template name for subtasks tooltip due to previous refactoring
|
||||
* Fix broken url for closed tasks in project view
|
||||
* Fix permission issue when changing the url manually
|
||||
* Fix bug task estimate is reset when using subtask timer
|
||||
* Fix screenshot feature with Firefox 40
|
||||
* Fix bug when uploading files with Cyrilic characters
|
||||
|
||||
Version 1.0.17
|
||||
--------------
|
||||
|
||||
New features:
|
||||
|
||||
* Added url rewrite and new routes
|
||||
* Added new search engine with advanced syntax
|
||||
* Added global search section
|
||||
* Added search form on the dashboard
|
||||
* Added new dashboard layout
|
||||
* Added new layout for board/calendar/list views
|
||||
* Added filters helper for search forms
|
||||
* Added setting option to disable subtask timer
|
||||
* Added setting option to include or exclude closed tasks into CFD
|
||||
* Added setting option to define the default task color
|
||||
* Added new config option to disable automatic creation of LDAP accounts
|
||||
* Added loading icon on board view
|
||||
* Prompt user when moving or duplicate a task to another project
|
||||
* Added current values when moving/duplicate a task to another project and add a loading icon
|
||||
* Added memory consumption to debug log
|
||||
* Added form to create remote user
|
||||
* Added edit form for user authentication
|
||||
* Added config option to hide login form
|
||||
* Display OAuth2 urls on integration page
|
||||
* Added keyboard shortcuts to switch between board/calendar/list view
|
||||
* Added keyboard shortcut to focus on the search box
|
||||
* Added Slack channel override
|
||||
* Added new report: Lead and cycle time for projects
|
||||
* Added new report: Average time spent into each column
|
||||
* Added task analytics
|
||||
* Added icon to set the start date automatically
|
||||
* Added datetime picker for start date
|
||||
|
||||
Improvements:
|
||||
|
||||
* Updated documentation
|
||||
* Display user initials when tasks are in collapsed mode
|
||||
* Show title in tooltip for collapsed tasks
|
||||
* Improve alert box fadeout to avoid an empty space
|
||||
* Set focus on the drop-down for category popover
|
||||
* Make escape keyboard shortcut global
|
||||
* Check the box remember me by default
|
||||
* Store redirect login url in session instead of using url parameter
|
||||
* Update Gitlab webhook
|
||||
* Do not rewrite remember me cookie for each request
|
||||
* Set the assignee as organizer for ical events
|
||||
* Increase date range for ics export
|
||||
* Reduce spacing on cards
|
||||
* Move board collapse/expand mode to server side to avoid board flickering
|
||||
* Use ajax requests for board collapse/expand
|
||||
* Do not set anchor for the default swimlane on the link back to board
|
||||
* Replace timeserie axis to category axis for charts
|
||||
* Hide task age in compact mode
|
||||
* Improve quick-add subtasks form
|
||||
* Reduce the size of the filter box for smaller screen
|
||||
* Added icon to hide/show sidebar
|
||||
* Update GitLab logo
|
||||
* Improve Dockerfile
|
||||
|
||||
Translations:
|
||||
|
||||
* Added Czech translation
|
||||
* Updated Spanish translation
|
||||
* Updated German Translation
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Screenshot drop-down: unexpected scroll down on the board view and focus lost when clicking on the drop zone
|
||||
* No creator when duplicating a task
|
||||
* Avoid the creation of multiple subtask timer for the same task and user
|
||||
|
||||
Code refactoring:
|
||||
|
||||
* Split task controller into smaller classes
|
||||
* Remove method Category::getBoardCategories()
|
||||
* Rewrite movePosition() to improve performances
|
||||
* Refactoring of Github and Google authentication
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* New OAuth url for Google and Github authentication
|
||||
|
||||
API:
|
||||
|
||||
* Add urls in api response for tasks and projects
|
||||
|
||||
Other:
|
||||
|
||||
* Added automated Docker build
|
||||
* Remove edit recurrence from the task menu on the board
|
||||
* Switch to MIT License instead of AGPLv3
|
||||
|
||||
Version 1.0.0 to 1.0.16
|
||||
-----------------------
|
||||
|
||||
* See commit history and website news
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2016 Frédéric Guillot
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,7 +0,0 @@
|
|||
<IfVersion >= 2.3>
|
||||
Require all denied
|
||||
</IfVersion>
|
||||
<IfVersion < 2.3>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfVersion>
|
|
@ -1,297 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Event\GenericEvent;
|
||||
|
||||
/**
|
||||
* Base class for automatic actions
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class Base extends \Kanboard\Core\Base
|
||||
{
|
||||
/**
|
||||
* Extended events
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $compatibleEvents = array();
|
||||
|
||||
/**
|
||||
* Flag for called listener
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private $called = false;
|
||||
|
||||
/**
|
||||
* Project id
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $projectId = 0;
|
||||
|
||||
/**
|
||||
* User parameters
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $params = array();
|
||||
|
||||
/**
|
||||
* Get automatic action name
|
||||
*
|
||||
* @final
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
final public function getName()
|
||||
{
|
||||
return '\\'.get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getDescription();
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
abstract public function doAction(array $data);
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getActionRequiredParameters();
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event (check if for the event data)
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getEventRequiredParameters();
|
||||
|
||||
/**
|
||||
* Get the compatible events
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getCompatibleEvents();
|
||||
|
||||
/**
|
||||
* Check if the event data meet the action condition
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function hasRequiredCondition(array $data);
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$params = array();
|
||||
|
||||
foreach ($this->params as $key => $value) {
|
||||
$params[] = $key.'='.var_export($value, true);
|
||||
}
|
||||
|
||||
return $this->getName().'('.implode('|', $params).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set project id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return Base
|
||||
*/
|
||||
public function setProjectId($project_id)
|
||||
{
|
||||
$this->projectId = $project_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getProjectId()
|
||||
{
|
||||
return $this->projectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an user defined parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parameter name
|
||||
* @param mixed $value Value
|
||||
* @return Base
|
||||
*/
|
||||
public function setParam($name, $value)
|
||||
{
|
||||
$this->params[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an user defined parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parameter name
|
||||
* @param mixed $default Default value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParam($name, $default = null)
|
||||
{
|
||||
return isset($this->params[$name]) ? $this->params[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action is executable (right project and required parameters)
|
||||
*
|
||||
* @access public
|
||||
* @param array $data
|
||||
* @param string $eventName
|
||||
* @return bool
|
||||
*/
|
||||
public function isExecutable(array $data, $eventName)
|
||||
{
|
||||
return $this->hasCompatibleEvent($eventName) &&
|
||||
$this->hasRequiredProject($data) &&
|
||||
$this->hasRequiredParameters($data) &&
|
||||
$this->hasRequiredCondition($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the event is compatible with the action
|
||||
*
|
||||
* @access public
|
||||
* @param string $eventName
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCompatibleEvent($eventName)
|
||||
{
|
||||
return in_array($eventName, $this->getEvents());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the event data has the required project
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRequiredProject(array $data)
|
||||
{
|
||||
return isset($data['project_id']) && $data['project_id'] == $this->getProjectId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the event data has required parameters to execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if all keys are there
|
||||
*/
|
||||
public function hasRequiredParameters(array $data)
|
||||
{
|
||||
foreach ($this->getEventRequiredParameters() as $parameter) {
|
||||
if (! isset($data[$parameter])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param \Kanboard\Event\GenericEvent $event
|
||||
* @param string $eventName
|
||||
* @return bool
|
||||
*/
|
||||
public function execute(GenericEvent $event, $eventName)
|
||||
{
|
||||
// Avoid infinite loop, a listener instance can be called only one time
|
||||
if ($this->called) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $event->getAll();
|
||||
$executable = $this->isExecutable($data, $eventName);
|
||||
$executed = false;
|
||||
|
||||
if ($executable) {
|
||||
$this->called = true;
|
||||
$executed = $this->doAction($data);
|
||||
}
|
||||
|
||||
$this->logger->debug($this.' ['.$eventName.'] => executable='.var_export($executable, true).' exec_success='.var_export($executed, true));
|
||||
|
||||
return $executed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new event for the automatic action
|
||||
*
|
||||
* @access public
|
||||
* @param string $event
|
||||
* @param string $description
|
||||
* @return Base
|
||||
*/
|
||||
public function addEvent($event, $description = '')
|
||||
{
|
||||
if ($description !== '') {
|
||||
$this->eventManager->register($event, $description);
|
||||
}
|
||||
|
||||
$this->compatibleEvents[] = $event;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all compatible events of an automatic action
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getEvents()
|
||||
{
|
||||
return array_unique(array_merge($this->getCompatibleEvents(), $this->compatibleEvents));
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
/**
|
||||
* Create automatically a comment from a webhook
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class CommentCreation extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Create a comment from an external provider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (create a new comment)
|
||||
*
|
||||
* @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 (bool) $this->commentModel->create(array(
|
||||
'reference' => isset($data['reference']) ? $data['reference'] : '',
|
||||
'comment' => $data['comment'],
|
||||
'task_id' => $data['task_id'],
|
||||
'user_id' => isset($data['user_id']) && $this->projectPermissionModel->isAssignable($this->getProjectId(), $data['user_id']) ? $data['user_id'] : 0,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ! empty($data['comment']);
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Add a comment of the triggering event to the task description.
|
||||
*
|
||||
* @package action
|
||||
* @author Oren Ben-Kiki
|
||||
*/
|
||||
class CommentCreationMoveTaskColumn extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Add a comment log when moving the task between columns');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array('column_id' => t('Column'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array('task_id', 'column_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (append to the task description).
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if (! $this->userSession->isLogged()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$column = $this->columnModel->getById($data['column_id']);
|
||||
|
||||
return (bool) $this->commentModel->create(array(
|
||||
'comment' => t('Moved to column %s', $column['title']),
|
||||
'task_id' => $data['task_id'],
|
||||
'user_id' => $this->userSession->getId(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Set a category automatically according to the color
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignCategoryColor extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign automatically a category based on a color');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_CREATE_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'color_id' => t('Color'),
|
||||
'category_id' => t('Category'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'color_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (change the category)
|
||||
*
|
||||
* @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'],
|
||||
'category_id' => $this->getParam('category_id'),
|
||||
);
|
||||
|
||||
return $this->taskModificationModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['color_id'] == $this->getParam('color_id');
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
/**
|
||||
* Set a category automatically according to a label
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignCategoryLabel extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Change the category based on an external label');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'label' => t('Label'),
|
||||
'category_id' => t('Category'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'label',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (change the category)
|
||||
*
|
||||
* @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'],
|
||||
'category_id' => $this->getParam('category_id'),
|
||||
);
|
||||
|
||||
return $this->taskModificationModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['label'] == $this->getParam('label') && empty($data['category_id']);
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskLinkModel;
|
||||
|
||||
/**
|
||||
* Set a category automatically according to a task link
|
||||
*
|
||||
* @package action
|
||||
* @author Olivier Maridat
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignCategoryLink extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign automatically a category based on a link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskLinkModel::EVENT_CREATE_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'category_id' => t('Category'),
|
||||
'link_id' => t('Link type'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'link_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (change the category)
|
||||
*
|
||||
* @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'],
|
||||
'category_id' => $this->getParam('category_id'),
|
||||
);
|
||||
|
||||
return $this->taskModificationModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the event data meet the action condition
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool
|
||||
*/
|
||||
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']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Assign a color to a specific category
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignColorCategory extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign automatically a color based on a category');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_CREATE_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'color_id' => t('Color'),
|
||||
'category_id' => t('Category'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'category_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (change 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'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
);
|
||||
|
||||
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 $data['category_id'] == $this->getParam('category_id');
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Assign a color to a task
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignColorColumn extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign a color when the task is moved to a specific column');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_CREATE,
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'color_id' => t('Color'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_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'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
);
|
||||
|
||||
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 $data['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskLinkModel;
|
||||
|
||||
/**
|
||||
* Assign a color to a specific task link
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignColorLink extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Change task color when using a specific task link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskLinkModel::EVENT_CREATE_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'color_id' => t('Color'),
|
||||
'link_id' => t('Link type'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'link_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (change 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'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
);
|
||||
|
||||
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 $data['link_id'] == $this->getParam('link_id');
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Assign a color to a priority
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignColorPriority extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign automatically a color based on a priority');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_CREATE_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'color_id' => t('Color'),
|
||||
'priority' => t('Priority'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'priority',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (change 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'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
);
|
||||
|
||||
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 $data['priority'] == $this->getParam('priority');
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Assign a color to a specific user
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignColorUser extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign a color to a specific user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_CREATE,
|
||||
TaskModel::EVENT_ASSIGNEE_CHANGE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'color_id' => t('Color'),
|
||||
'user_id' => t('Assignee'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'owner_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (change 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'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
);
|
||||
|
||||
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 $data['owner_id'] == $this->getParam('user_id');
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Assign a task to the logged user
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignCurrentUser extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign the task to the person who does the action');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if (! $this->userSession->isLogged()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'id' => $data['task_id'],
|
||||
'owner_id' => $this->userSession->getId(),
|
||||
);
|
||||
|
||||
return $this->taskModificationModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Assign a task to the logged user on column change
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignCurrentUserColumn extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign the task to the person who does the action when the column is changed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if (! $this->userSession->isLogged()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'id' => $data['task_id'],
|
||||
'owner_id' => $this->userSession->getId(),
|
||||
);
|
||||
|
||||
return $this->taskModificationModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Assign a task to a specific user
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignSpecificUser extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Assign the task to a specific user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_CREATE_UPDATE,
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'user_id' => t('Assignee'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (assign the given user)
|
||||
*
|
||||
* @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'],
|
||||
'owner_id' => $this->getParam('user_id'),
|
||||
);
|
||||
|
||||
return $this->taskModificationModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
/**
|
||||
* Assign a task to someone
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignUser extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Change the assignee based on an external username');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'owner_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (assign the given user)
|
||||
*
|
||||
* @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'],
|
||||
'owner_id' => $data['owner_id'],
|
||||
);
|
||||
|
||||
return $this->taskModificationModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $this->projectPermissionModel->isAssignable($this->getProjectId(), $data['owner_id']);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
/**
|
||||
* Close automatically a task
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskClose extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Close a task');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array('task_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return $this->taskStatusModel->close($data['task_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Close automatically a task in a specific column
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskCloseColumn extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Close a task in a specific column');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array('column_id' => t('Column'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array('task_id', 'column_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return $this->taskStatusModel->close($data['task_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Close automatically a task after when inactive
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskCloseNoActivity extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Close a task when there is no activity');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$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;
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
/**
|
||||
* Create automatically a task from a webhook
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskCreation extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Create a task from an external provider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'reference',
|
||||
'title',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (create a new 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)
|
||||
{
|
||||
return (bool) $this->taskCreationModel->create(array(
|
||||
'project_id' => $data['project_id'],
|
||||
'title' => $data['title'],
|
||||
'reference' => $data['reference'],
|
||||
'description' => isset($data['description']) ? $data['description'] : '',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskDuplicateAnotherProject extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Duplicate the task to another project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
TaskModel::EVENT_CLOSE,
|
||||
TaskModel::EVENT_CREATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'project_id' => t('Project'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (duplicate the task to another project)
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$destination_column_id = $this->columnModel->getFirstColumnId($this->getParam('project_id'));
|
||||
return (bool) $this->taskProjectDuplicationModel->duplicateToProject($data['task_id'], $this->getParam('project_id'), null, $destination_column_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id');
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Email a task to someone
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskEmail extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Send a task by email to someone');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
TaskModel::EVENT_CLOSE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'user_id' => t('User that will receive the email'),
|
||||
'subject' => t('Email subject'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$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')))
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return 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['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Email a task with no activity
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskEmailNoActivity extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Send email when there is no activity on a task');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
'user_id' => t('User that will receive the email'),
|
||||
'subject' => t('Email subject'),
|
||||
'duration' => t('Duration in days'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array('tasks');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$results = array();
|
||||
$max = $this->getParam('duration') * 86400;
|
||||
$user = $this->userModel->getById($this->getParam('user_id'));
|
||||
|
||||
if (! empty($user['email'])) {
|
||||
foreach ($data['tasks'] as $task) {
|
||||
$duration = time() - $task['date_modification'];
|
||||
|
||||
if ($duration > $max) {
|
||||
$results[] = $this->sendEmail($task['id'], $user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return in_array(true, $results, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email
|
||||
*
|
||||
* @access private
|
||||
* @param integer $task_id
|
||||
* @param array $user
|
||||
* @return boolean
|
||||
*/
|
||||
private function sendEmail($task_id, array $user)
|
||||
{
|
||||
$task = $this->taskFinderModel->getDetails($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')))
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Move a task to another project
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskMoveAnotherProject extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Move the task to another project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
TaskModel::EVENT_CLOSE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'project_id' => t('Project'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
'project_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action (move the task to another project)
|
||||
*
|
||||
* @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->taskProjectMoveModel->moveToProject($data['task_id'], $this->getParam('project_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id');
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Move a task to another column when an assignee is set
|
||||
*
|
||||
* @package action
|
||||
* @author Francois Ferrand
|
||||
*/
|
||||
class TaskMoveColumnAssigned extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Move the task to another column when assigned to a user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_ASSIGNEE_CHANGE,
|
||||
TaskModel::EVENT_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'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(
|
||||
'task_id',
|
||||
'column_id',
|
||||
'owner_id'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$original_task = $this->taskFinderModel->getById($data['task_id']);
|
||||
|
||||
return $this->taskPositionModel->movePosition(
|
||||
$data['project_id'],
|
||||
$data['task_id'],
|
||||
$this->getParam('dest_column_id'),
|
||||
$original_task['position'],
|
||||
$original_task['swimlane_id'],
|
||||
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['column_id'] == $this->getParam('src_column_id') && $data['owner_id'] > 0;
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Move a task to another column when the category is changed
|
||||
*
|
||||
* @package action
|
||||
* @author Francois Ferrand
|
||||
*/
|
||||
class TaskMoveColumnCategoryChange extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Move the task to another column when the category is changed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'),
|
||||
'category_id' => t('Category'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
'category_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$original_task = $this->taskFinderModel->getById($data['task_id']);
|
||||
|
||||
return $this->taskPositionModel->movePosition(
|
||||
$data['project_id'],
|
||||
$data['task_id'],
|
||||
$this->getParam('dest_column_id'),
|
||||
$original_task['position'],
|
||||
$original_task['swimlane_id'],
|
||||
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['column_id'] != $this->getParam('dest_column_id') && $data['category_id'] == $this->getParam('category_id');
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Move a task to another column when an assignee is cleared
|
||||
*
|
||||
* @package action
|
||||
* @author Francois Ferrand
|
||||
*/
|
||||
class TaskMoveColumnUnAssigned extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Move the task to another column when assignee is cleared');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_ASSIGNEE_CHANGE,
|
||||
TaskModel::EVENT_UPDATE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'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(
|
||||
'task_id',
|
||||
'column_id',
|
||||
'owner_id'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$original_task = $this->taskFinderModel->getById($data['task_id']);
|
||||
|
||||
return $this->taskPositionModel->movePosition(
|
||||
$data['project_id'],
|
||||
$data['task_id'],
|
||||
$this->getParam('dest_column_id'),
|
||||
$original_task['position'],
|
||||
$original_task['swimlane_id'],
|
||||
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['column_id'] == $this->getParam('src_column_id') && $data['owner_id'] == 0;
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
/**
|
||||
* Open automatically a task
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskOpen extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Open a task');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array('task_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return $this->taskStatusModel->open($data['task_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Set the start date of task
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskUpdateStartDate extends Base
|
||||
{
|
||||
/**
|
||||
* Get automatic action description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return t('Automatically update the start date');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of compatible events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleEvents()
|
||||
{
|
||||
return array(
|
||||
TaskModel::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_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_started' => time(),
|
||||
);
|
||||
|
||||
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 $data['column_id'] == $this->getParam('column_id');
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Analytic;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Average Lead and Cycle Time
|
||||
*
|
||||
* @package analytic
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AverageLeadCycleTimeAnalytic extends Base
|
||||
{
|
||||
/**
|
||||
* Build report
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function build($project_id)
|
||||
{
|
||||
$stats = array(
|
||||
'count' => 0,
|
||||
'total_lead_time' => 0,
|
||||
'total_cycle_time' => 0,
|
||||
'avg_lead_time' => 0,
|
||||
'avg_cycle_time' => 0,
|
||||
);
|
||||
|
||||
$tasks = $this->getTasks($project_id);
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
$stats['count']++;
|
||||
$stats['total_lead_time'] += $this->calculateLeadTime($task);
|
||||
$stats['total_cycle_time'] += $this->calculateCycleTime($task);
|
||||
}
|
||||
|
||||
$stats['avg_lead_time'] = $this->calculateAverage($stats, 'total_lead_time');
|
||||
$stats['avg_cycle_time'] = $this->calculateAverage($stats, 'total_cycle_time');
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate average
|
||||
*
|
||||
* @access private
|
||||
* @param array &$stats
|
||||
* @param string $field
|
||||
* @return float
|
||||
*/
|
||||
private function calculateAverage(array &$stats, $field)
|
||||
{
|
||||
if ($stats['count'] > 0) {
|
||||
return (int) ($stats[$field] / $stats['count']);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate lead time
|
||||
*
|
||||
* @access private
|
||||
* @param array &$task
|
||||
* @return integer
|
||||
*/
|
||||
private function calculateLeadTime(array &$task)
|
||||
{
|
||||
$end = $task['date_completed'] ?: time();
|
||||
$start = $task['date_creation'];
|
||||
|
||||
return $end - $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate cycle time
|
||||
*
|
||||
* @access private
|
||||
* @param array &$task
|
||||
* @return integer
|
||||
*/
|
||||
private function calculateCycleTime(array &$task)
|
||||
{
|
||||
$end = (int) $task['date_completed'] ?: time();
|
||||
$start = (int) $task['date_started'];
|
||||
|
||||
// Start date can be in the future when defined with the Gantt chart
|
||||
if ($start > 0 && $end > $start) {
|
||||
return $end - $start;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 1000 last created tasks
|
||||
*
|
||||
* @access private
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
private function getTasks($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(TaskModel::TABLE)
|
||||
->columns('date_completed', 'date_creation', 'date_started')
|
||||
->eq('project_id', $project_id)
|
||||
->desc('id')
|
||||
->limit(1000)
|
||||
->findAll();
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Analytic;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Average Time Spent by Column
|
||||
*
|
||||
* @package analytic
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AverageTimeSpentColumnAnalytic extends Base
|
||||
{
|
||||
/**
|
||||
* Build report
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function build($project_id)
|
||||
{
|
||||
$stats = $this->initialize($project_id);
|
||||
|
||||
$this->processTasks($stats, $project_id);
|
||||
$this->calculateAverage($stats);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize default values for each column
|
||||
*
|
||||
* @access private
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
private function initialize($project_id)
|
||||
{
|
||||
$stats = array();
|
||||
$columns = $this->columnModel->getList($project_id);
|
||||
|
||||
foreach ($columns as $column_id => $column_title) {
|
||||
$stats[$column_id] = array(
|
||||
'count' => 0,
|
||||
'time_spent' => 0,
|
||||
'average' => 0,
|
||||
'title' => $column_title,
|
||||
);
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate time spent for each tasks for each columns
|
||||
*
|
||||
* @access private
|
||||
* @param array $stats
|
||||
* @param integer $project_id
|
||||
*/
|
||||
private function processTasks(array &$stats, $project_id)
|
||||
{
|
||||
$tasks = $this->getTasks($project_id);
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
foreach ($this->getTaskTimeByColumns($task) as $column_id => $time_spent) {
|
||||
if (isset($stats[$column_id])) {
|
||||
$stats[$column_id]['count']++;
|
||||
$stats[$column_id]['time_spent'] += $time_spent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate averages
|
||||
*
|
||||
* @access private
|
||||
* @param array $stats
|
||||
*/
|
||||
private function calculateAverage(array &$stats)
|
||||
{
|
||||
foreach ($stats as &$column) {
|
||||
$this->calculateColumnAverage($column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate column average
|
||||
*
|
||||
* @access private
|
||||
* @param array $column
|
||||
*/
|
||||
private function calculateColumnAverage(array &$column)
|
||||
{
|
||||
if ($column['count'] > 0) {
|
||||
$column['average'] = (int) ($column['time_spent'] / $column['count']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time spent for each column for a given task
|
||||
*
|
||||
* @access private
|
||||
* @param array $task
|
||||
* @return array
|
||||
*/
|
||||
private function getTaskTimeByColumns(array &$task)
|
||||
{
|
||||
$columns = $this->transitionModel->getTimeSpentByTask($task['id']);
|
||||
|
||||
if (! isset($columns[$task['column_id']])) {
|
||||
$columns[$task['column_id']] = 0;
|
||||
}
|
||||
|
||||
$columns[$task['column_id']] += $this->getTaskTimeSpentInCurrentColumn($task);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate time spent of a task in the current column
|
||||
*
|
||||
* @access private
|
||||
* @param array $task
|
||||
* @return integer
|
||||
*/
|
||||
private function getTaskTimeSpentInCurrentColumn(array &$task)
|
||||
{
|
||||
$end = $task['date_completed'] ?: time();
|
||||
return $end - $task['date_moved'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the last 1000 tasks
|
||||
*
|
||||
* @access private
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
private function getTasks($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(TaskModel::TABLE)
|
||||
->columns('id', 'date_completed', 'date_moved', 'column_id')
|
||||
->eq('project_id', $project_id)
|
||||
->desc('id')
|
||||
->limit(1000)
|
||||
->findAll();
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Analytic;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Estimated/Spent Time Comparison
|
||||
*
|
||||
* @package analytic
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class EstimatedTimeComparisonAnalytic extends Base
|
||||
{
|
||||
/**
|
||||
* Build report
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function build($project_id)
|
||||
{
|
||||
$rows = $this->db->table(TaskModel::TABLE)
|
||||
->columns('SUM(time_estimated) AS time_estimated', 'SUM(time_spent) AS time_spent', 'is_active')
|
||||
->eq('project_id', $project_id)
|
||||
->groupBy('is_active')
|
||||
->findAll();
|
||||
|
||||
$metrics = array(
|
||||
'open' => array(
|
||||
'time_spent' => 0,
|
||||
'time_estimated' => 0,
|
||||
),
|
||||
'closed' => array(
|
||||
'time_spent' => 0,
|
||||
'time_estimated' => 0,
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$key = $row['is_active'] == TaskModel::STATUS_OPEN ? 'open' : 'closed';
|
||||
$metrics[$key]['time_spent'] = $row['time_spent'];
|
||||
$metrics[$key]['time_estimated'] = $row['time_estimated'];
|
||||
}
|
||||
|
||||
return $metrics;
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Analytic;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Task Distribution
|
||||
*
|
||||
* @package analytic
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskDistributionAnalytic extends Base
|
||||
{
|
||||
/**
|
||||
* Build report
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function build($project_id)
|
||||
{
|
||||
$metrics = array();
|
||||
$total = 0;
|
||||
$columns = $this->columnModel->getAll($project_id);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$nb_tasks = $this->taskFinderModel->countByColumnId($project_id, $column['id']);
|
||||
$total += $nb_tasks;
|
||||
|
||||
$metrics[] = array(
|
||||
'column_title' => $column['title'],
|
||||
'nb_tasks' => $nb_tasks,
|
||||
);
|
||||
}
|
||||
|
||||
if ($total === 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ($metrics as &$metric) {
|
||||
$metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2);
|
||||
}
|
||||
|
||||
return $metrics;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Analytic;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* User Distribution
|
||||
*
|
||||
* @package analytic
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserDistributionAnalytic extends Base
|
||||
{
|
||||
/**
|
||||
* Build Report
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
public function build($project_id)
|
||||
{
|
||||
$metrics = array();
|
||||
$total = 0;
|
||||
$tasks = $this->taskFinderModel->getAll($project_id);
|
||||
$users = $this->projectUserRoleModel->getAssignableUsersList($project_id);
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$user = isset($users[$task['owner_id']]) ? $users[$task['owner_id']] : $users[0];
|
||||
$total++;
|
||||
|
||||
if (! isset($metrics[$user])) {
|
||||
$metrics[$user] = array(
|
||||
'nb_tasks' => 0,
|
||||
'percentage' => 0,
|
||||
'user' => $user,
|
||||
);
|
||||
}
|
||||
|
||||
$metrics[$user]['nb_tasks']++;
|
||||
}
|
||||
|
||||
if ($total === 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ($metrics as &$metric) {
|
||||
$metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2);
|
||||
}
|
||||
|
||||
ksort($metrics);
|
||||
|
||||
return array_values($metrics);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Middleware;
|
||||
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
use JsonRPC\MiddlewareInterface;
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Class AuthenticationApiMiddleware
|
||||
*
|
||||
* @package Kanboard\Api\Middleware
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AuthenticationMiddleware extends Base implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Execute Middleware
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $procedureName
|
||||
* @throws AccessDeniedException
|
||||
* @throws AuthenticationFailureException
|
||||
*/
|
||||
public function execute($username, $password, $procedureName)
|
||||
{
|
||||
$this->dispatcher->dispatch('app.bootstrap');
|
||||
|
||||
if ($this->isUserAuthenticated($username, $password)) {
|
||||
$this->userSession->initialize($this->userModel->getByUsername($username));
|
||||
} elseif (! $this->isAppAuthenticated($username, $password)) {
|
||||
$this->logger->error('API authentication failure for '.$username);
|
||||
throw new AuthenticationFailureException('Wrong credentials');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user credentials
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
*/
|
||||
private function isUserAuthenticated($username, $password)
|
||||
{
|
||||
return $username !== 'jsonrpc' &&
|
||||
! $this->userLockingModel->isLocked($username) &&
|
||||
$this->authenticationManager->passwordAuthentication($username, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check administrative credentials
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
*/
|
||||
private function isAppAuthenticated($username, $password)
|
||||
{
|
||||
return $username === 'jsonrpc' && $password === $this->getApiToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API Token
|
||||
*
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getApiToken()
|
||||
{
|
||||
if (defined('API_AUTHENTICATION_TOKEN')) {
|
||||
return API_AUTHENTICATION_TOKEN;
|
||||
}
|
||||
|
||||
return $this->configModel->get('api_token');
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\ActionAuthorization;
|
||||
use Kanboard\Api\Authorization\ProjectAuthorization;
|
||||
|
||||
/**
|
||||
* Action API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ActionProcedure extends BaseProcedure
|
||||
{
|
||||
public function getAvailableActions()
|
||||
{
|
||||
return $this->actionManager->getAvailableActions();
|
||||
}
|
||||
|
||||
public function getAvailableActionEvents()
|
||||
{
|
||||
return $this->eventManager->getAll();
|
||||
}
|
||||
|
||||
public function getCompatibleActionEvents($action_name)
|
||||
{
|
||||
return $this->actionManager->getCompatibleEvents($action_name);
|
||||
}
|
||||
|
||||
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,
|
||||
'action_name' => $action_name,
|
||||
'params' => $params,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->actionValidator->validateCreation($values);
|
||||
|
||||
if (! $valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the action exists
|
||||
$actions = $this->actionManager->getAvailableActions();
|
||||
|
||||
if (! isset($actions[$action_name])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the event
|
||||
$action = $this->actionManager->getAction($action_name);
|
||||
|
||||
if (! in_array($event_name, $action->getEvents())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$required_params = $action->getActionRequiredParameters();
|
||||
|
||||
// Check missing parameters
|
||||
foreach ($required_params as $param => $value) {
|
||||
if (! isset($params[$param])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check extra parameters
|
||||
foreach ($params as $param => $value) {
|
||||
if (! isset($required_params[$param])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->actionModel->create($values);
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
/**
|
||||
* App API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AppProcedure extends BaseProcedure
|
||||
{
|
||||
public function getTimezone()
|
||||
{
|
||||
return $this->timezoneModel->getCurrentTimezone();
|
||||
}
|
||||
|
||||
public function getVersion()
|
||||
{
|
||||
return APP_VERSION;
|
||||
}
|
||||
|
||||
public function getDefaultTaskColor()
|
||||
{
|
||||
return $this->colorModel->getDefaultColor();
|
||||
}
|
||||
|
||||
public function getDefaultTaskColors()
|
||||
{
|
||||
return $this->colorModel->getDefaultColors();
|
||||
}
|
||||
|
||||
public function getColorList()
|
||||
{
|
||||
return $this->colorModel->getList();
|
||||
}
|
||||
|
||||
public function getApplicationRoles()
|
||||
{
|
||||
return $this->role->getApplicationRoles();
|
||||
}
|
||||
|
||||
public function getProjectRoles()
|
||||
{
|
||||
return $this->role->getProjectRoles();
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\ProcedureAuthorization;
|
||||
use Kanboard\Api\Authorization\UserAuthorization;
|
||||
use Kanboard\Core\Base;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Base class
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseProcedure extends Base
|
||||
{
|
||||
public function beforeProcedure($procedure)
|
||||
{
|
||||
ProcedureAuthorization::getInstance($this->container)->check($procedure);
|
||||
UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure);
|
||||
}
|
||||
|
||||
protected function formatTask($task)
|
||||
{
|
||||
if (! empty($task)) {
|
||||
$task['url'] = $this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), '', true);
|
||||
$task['color'] = $this->colorModel->getColorProperties($task['color_id']);
|
||||
}
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
protected function formatTasks($tasks)
|
||||
{
|
||||
if (! empty($tasks)) {
|
||||
foreach ($tasks as &$task) {
|
||||
$task = $this->formatTask($task);
|
||||
}
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
protected function formatProject($project)
|
||||
{
|
||||
if (! empty($project)) {
|
||||
$project['url'] = array(
|
||||
'board' => $this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id']), '', true),
|
||||
'calendar' => $this->helper->url->to('CalendarController', 'show', array('project_id' => $project['id']), '', true),
|
||||
'list' => $this->helper->url->to('TaskListController', 'show', array('project_id' => $project['id']), '', true),
|
||||
);
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
protected function formatProjects($projects)
|
||||
{
|
||||
if (! empty($projects)) {
|
||||
foreach ($projects as &$project) {
|
||||
$project = $this->formatProject($project);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\CategoryAuthorization;
|
||||
use Kanboard\Api\Authorization\ProjectAuthorization;
|
||||
|
||||
/**
|
||||
* Category API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
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,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->categoryValidator->validateCreation($values);
|
||||
return $valid ? $this->categoryModel->create($values) : false;
|
||||
}
|
||||
|
||||
public function updateCategory($id, $name)
|
||||
{
|
||||
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id);
|
||||
|
||||
$values = array(
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->categoryValidator->validateModification($values);
|
||||
return $valid && $this->categoryModel->update($values);
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\ColumnAuthorization;
|
||||
use Kanboard\Api\Authorization\ProjectAuthorization;
|
||||
|
||||
/**
|
||||
* Column API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\CommentAuthorization;
|
||||
use Kanboard\Api\Authorization\TaskAuthorization;
|
||||
|
||||
/**
|
||||
* Comment API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
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,
|
||||
'comment' => $content,
|
||||
'reference' => $reference,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->commentValidator->validateCreation($values);
|
||||
|
||||
return $valid ? $this->commentModel->create($values) : false;
|
||||
}
|
||||
|
||||
public function updateComment($id, $content)
|
||||
{
|
||||
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id);
|
||||
|
||||
$values = array(
|
||||
'id' => $id,
|
||||
'comment' => $content,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->commentValidator->validateModification($values);
|
||||
return $valid && $this->commentModel->update($values);
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
/**
|
||||
* Group Member API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class GroupMemberProcedure extends BaseProcedure
|
||||
{
|
||||
public function getMemberGroups($user_id)
|
||||
{
|
||||
return $this->groupMemberModel->getGroups($user_id);
|
||||
}
|
||||
|
||||
public function getGroupMembers($group_id)
|
||||
{
|
||||
return $this->groupMemberModel->getMembers($group_id);
|
||||
}
|
||||
|
||||
public function addGroupMember($group_id, $user_id)
|
||||
{
|
||||
return $this->groupMemberModel->addUser($group_id, $user_id);
|
||||
}
|
||||
|
||||
public function removeGroupMember($group_id, $user_id)
|
||||
{
|
||||
return $this->groupMemberModel->removeUser($group_id, $user_id);
|
||||
}
|
||||
|
||||
public function isGroupMember($group_id, $user_id)
|
||||
{
|
||||
return $this->groupMemberModel->isMember($group_id, $user_id);
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
/**
|
||||
* Group API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class GroupProcedure extends BaseProcedure
|
||||
{
|
||||
public function createGroup($name, $external_id = '')
|
||||
{
|
||||
return $this->groupModel->create($name, $external_id);
|
||||
}
|
||||
|
||||
public function updateGroup($group_id, $name = null, $external_id = null)
|
||||
{
|
||||
$values = array(
|
||||
'id' => $group_id,
|
||||
'name' => $name,
|
||||
'external_id' => $external_id,
|
||||
);
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if (is_null($value)) {
|
||||
unset($values[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->groupModel->update($values);
|
||||
}
|
||||
|
||||
public function removeGroup($group_id)
|
||||
{
|
||||
return $this->groupModel->remove($group_id);
|
||||
}
|
||||
|
||||
public function getGroup($group_id)
|
||||
{
|
||||
return $this->groupModel->getById($group_id);
|
||||
}
|
||||
|
||||
public function getAllGroups()
|
||||
{
|
||||
return $this->groupModel->getAll();
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
/**
|
||||
* Link API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class LinkProcedure extends BaseProcedure
|
||||
{
|
||||
/**
|
||||
* Get a link by id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id Link id
|
||||
* @return array
|
||||
*/
|
||||
public function getLinkById($link_id)
|
||||
{
|
||||
return $this->linkModel->getById($link_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a link by name
|
||||
*
|
||||
* @access public
|
||||
* @param string $label
|
||||
* @return array
|
||||
*/
|
||||
public function getLinkByLabel($label)
|
||||
{
|
||||
return $this->linkModel->getByLabel($label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opposite link id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id Link id
|
||||
* @return integer
|
||||
*/
|
||||
public function getOppositeLinkId($link_id)
|
||||
{
|
||||
return $this->linkModel->getOppositeLinkId($link_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all links
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAllLinks()
|
||||
{
|
||||
return $this->linkModel->getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new link label
|
||||
*
|
||||
* @access public
|
||||
* @param string $label
|
||||
* @param string $opposite_label
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function createLink($label, $opposite_label = '')
|
||||
{
|
||||
$values = array(
|
||||
'label' => $label,
|
||||
'opposite_label' => $opposite_label,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->linkValidator->validateCreation($values);
|
||||
return $valid ? $this->linkModel->create($label, $opposite_label) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a link
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id
|
||||
* @param integer $opposite_link_id
|
||||
* @param string $label
|
||||
* @return boolean
|
||||
*/
|
||||
public function updateLink($link_id, $opposite_link_id, $label)
|
||||
{
|
||||
$values = array(
|
||||
'id' => $link_id,
|
||||
'opposite_id' => $opposite_link_id,
|
||||
'label' => $label,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->linkValidator->validateModification($values);
|
||||
return $valid && $this->linkModel->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link a the relation to its opposite
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeLink($link_id)
|
||||
{
|
||||
return $this->linkModel->remove($link_id);
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Model\SubtaskModel;
|
||||
|
||||
/**
|
||||
* Me API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class MeProcedure extends BaseProcedure
|
||||
{
|
||||
public function getMe()
|
||||
{
|
||||
return $this->sessionStorage->user;
|
||||
}
|
||||
|
||||
public function getMyDashboard()
|
||||
{
|
||||
$user_id = $this->userSession->getId();
|
||||
$projects = $this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id))->findAll();
|
||||
$tasks = $this->taskFinderModel->getUserQuery($user_id)->findAll();
|
||||
|
||||
return array(
|
||||
'projects' => $this->formatProjects($projects),
|
||||
'tasks' => $this->formatTasks($tasks),
|
||||
'subtasks' => $this->subtaskModel->getUserQuery($user_id, array(SubtaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS))->findAll(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getMyActivityStream()
|
||||
{
|
||||
$project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
|
||||
return $this->helper->projectActivity->getProjectsEvents($project_ids, 100);
|
||||
}
|
||||
|
||||
public function createMyPrivateProject($name, $description = null)
|
||||
{
|
||||
if ($this->configModel->get('disable_private_project', 0) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'name' => $name,
|
||||
'description' => $description,
|
||||
'is_private' => 1,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->projectValidator->validateCreation($values);
|
||||
return $valid ? $this->projectModel->create($values, $this->userSession->getId(), true) : false;
|
||||
}
|
||||
|
||||
public function getMyProjectsList()
|
||||
{
|
||||
return $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId());
|
||||
}
|
||||
|
||||
public function getMyOverdueTasks()
|
||||
{
|
||||
return $this->taskFinderModel->getOverdueTasksByUser($this->userSession->getId());
|
||||
}
|
||||
|
||||
public function getMyProjects()
|
||||
{
|
||||
$project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
|
||||
$projects = $this->projectModel->getAllByIds($project_ids);
|
||||
|
||||
return $this->formatProjects($projects);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\ProjectAuthorization;
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* Project Permission API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
public function getProjectUserRole($project_id, $user_id)
|
||||
{
|
||||
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUserRole', $project_id);
|
||||
return $this->projectUserRoleModel->getUserRole($project_id, $user_id);
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\SubtaskAuthorization;
|
||||
use Kanboard\Api\Authorization\TaskAuthorization;
|
||||
|
||||
/**
|
||||
* Subtask API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
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,
|
||||
'user_id' => $user_id,
|
||||
'time_estimated' => $time_estimated,
|
||||
'time_spent' => $time_spent,
|
||||
'status' => $status,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->subtaskValidator->validateCreation($values);
|
||||
return $valid ? $this->subtaskModel->create($values) : false;
|
||||
}
|
||||
|
||||
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,
|
||||
'title' => $title,
|
||||
'user_id' => $user_id,
|
||||
'time_estimated' => $time_estimated,
|
||||
'time_spent' => $time_spent,
|
||||
'status' => $status,
|
||||
);
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if (is_null($value)) {
|
||||
unset($values[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
list($valid, ) = $this->subtaskValidator->validateApiModification($values);
|
||||
return $valid && $this->subtaskModel->update($values);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\ProjectAuthorization;
|
||||
|
||||
/**
|
||||
* Swimlane API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
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)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
public function getSwimlane($swimlane_id)
|
||||
{
|
||||
return $this->swimlaneModel->getById($swimlane_id);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public function updateSwimlane($swimlane_id, $name, $description = null)
|
||||
{
|
||||
$values = array('id' => $swimlane_id, 'name' => $name);
|
||||
|
||||
if (!is_null($description)) {
|
||||
$values['description'] = $description;
|
||||
}
|
||||
|
||||
return $this->swimlaneModel->update($values);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\ProjectAuthorization;
|
||||
use Kanboard\Api\Authorization\TaskAuthorization;
|
||||
use Kanboard\Api\Authorization\TaskFileAuthorization;
|
||||
use Kanboard\Core\ObjectStorage\ObjectStorageException;
|
||||
|
||||
/**
|
||||
* Task File API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
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);
|
||||
|
||||
if (! empty($file)) {
|
||||
return base64_encode($this->objectStorage->get($file['path']));
|
||||
}
|
||||
} 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(__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);
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use Kanboard\Api\Authorization\TaskAuthorization;
|
||||
use Kanboard\Api\Authorization\TaskLinkAuthorization;
|
||||
|
||||
/**
|
||||
* TaskLink API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskLinkProcedure extends BaseProcedure
|
||||
{
|
||||
/**
|
||||
* Get a task link
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_link_id Task link id
|
||||
* @return array
|
||||
*/
|
||||
public function getTaskLinkById($task_link_id)
|
||||
{
|
||||
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id);
|
||||
return $this->taskLinkModel->getById($task_link_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all links attached to a task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllTaskLinks($task_id)
|
||||
{
|
||||
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $task_id);
|
||||
return $this->taskLinkModel->getAll($task_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new link
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $opposite_task_id Opposite task id
|
||||
* @param integer $link_id Link id
|
||||
* @return integer Task link id
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a task link
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_link_id Task link id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $opposite_task_id Opposite task id
|
||||
* @param integer $link_id Link id
|
||||
* @return boolean
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link between two tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_link_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeTaskLink($task_link_id)
|
||||
{
|
||||
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id);
|
||||
return $this->taskLinkModel->remove($task_link_id);
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
<?php
|
||||
|
||||
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\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskProcedure extends BaseProcedure
|
||||
{
|
||||
public function searchTasks($project_id, $query)
|
||||
{
|
||||
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)
|
||||
{
|
||||
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id);
|
||||
return $this->formatTask($this->taskFinderModel->getById($task_id));
|
||||
}
|
||||
|
||||
public function getTaskByReference($project_id, $reference)
|
||||
{
|
||||
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)
|
||||
{
|
||||
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id);
|
||||
return $this->formatTasks($this->taskFinderModel->getAll($project_id, $status_id));
|
||||
}
|
||||
|
||||
public function getOverdueTasks()
|
||||
{
|
||||
return $this->taskFinderModel->getOverdueTasks();
|
||||
}
|
||||
|
||||
public function getOverdueTasksByProject($project_id)
|
||||
{
|
||||
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getOverdueTasksByProject', $project_id);
|
||||
return $this->taskFinderModel->getOverdueTasksByProject($project_id);
|
||||
}
|
||||
|
||||
public function openTask($task_id)
|
||||
{
|
||||
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'openTask', $task_id);
|
||||
return $this->taskStatusModel->open($task_id);
|
||||
}
|
||||
|
||||
public function closeTask($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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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,
|
||||
$date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, $priority = 0,
|
||||
$recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
|
||||
$recurrence_basedate = 0, $reference = '')
|
||||
{
|
||||
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id);
|
||||
|
||||
if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->userSession->isLogged()) {
|
||||
$creator_id = $this->userSession->getId();
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'title' => $title,
|
||||
'project_id' => $project_id,
|
||||
'color_id' => $color_id,
|
||||
'column_id' => $column_id,
|
||||
'owner_id' => $owner_id,
|
||||
'creator_id' => $creator_id,
|
||||
'date_due' => $date_due,
|
||||
'description' => $description,
|
||||
'category_id' => $category_id,
|
||||
'score' => $score,
|
||||
'swimlane_id' => $swimlane_id,
|
||||
'recurrence_status' => $recurrence_status,
|
||||
'recurrence_trigger' => $recurrence_trigger,
|
||||
'recurrence_factor' => $recurrence_factor,
|
||||
'recurrence_timeframe' => $recurrence_timeframe,
|
||||
'recurrence_basedate' => $recurrence_basedate,
|
||||
'reference' => $reference,
|
||||
'priority' => $priority,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->taskValidator->validateCreation($values);
|
||||
|
||||
return $valid ? $this->taskCreationModel->create($values) : false;
|
||||
}
|
||||
|
||||
public function updateTask($id, $title = null, $color_id = null, $owner_id = null,
|
||||
$date_due = null, $description = null, $category_id = null, $score = null, $priority = null,
|
||||
$recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
|
||||
$recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
|
||||
{
|
||||
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id);
|
||||
$project_id = $this->taskFinderModel->getProjectId($id);
|
||||
|
||||
if ($project_id === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($owner_id !== null && $owner_id != 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = $this->filterValues(array(
|
||||
'id' => $id,
|
||||
'title' => $title,
|
||||
'color_id' => $color_id,
|
||||
'owner_id' => $owner_id,
|
||||
'date_due' => $date_due,
|
||||
'description' => $description,
|
||||
'category_id' => $category_id,
|
||||
'score' => $score,
|
||||
'recurrence_status' => $recurrence_status,
|
||||
'recurrence_trigger' => $recurrence_trigger,
|
||||
'recurrence_factor' => $recurrence_factor,
|
||||
'recurrence_timeframe' => $recurrence_timeframe,
|
||||
'recurrence_basedate' => $recurrence_basedate,
|
||||
'reference' => $reference,
|
||||
'priority' => $priority,
|
||||
));
|
||||
|
||||
list($valid) = $this->taskValidator->validateApiModification($values);
|
||||
return $valid && $this->taskModificationModel->update($values);
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api\Procedure;
|
||||
|
||||
use LogicException;
|
||||
use Kanboard\Core\Security\Role;
|
||||
use Kanboard\Core\Ldap\Client as LdapClient;
|
||||
use Kanboard\Core\Ldap\ClientException as LdapException;
|
||||
use Kanboard\Core\Ldap\User as LdapUser;
|
||||
|
||||
/**
|
||||
* User API controller
|
||||
*
|
||||
* @package Kanboard\Api\Procedure
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserProcedure extends BaseProcedure
|
||||
{
|
||||
public function getUser($user_id)
|
||||
{
|
||||
return $this->userModel->getById($user_id);
|
||||
}
|
||||
|
||||
public function getUserByName($username)
|
||||
{
|
||||
return $this->userModel->getByUsername($username);
|
||||
}
|
||||
|
||||
public function getAllUsers()
|
||||
{
|
||||
return $this->userModel->getAll();
|
||||
}
|
||||
|
||||
public function removeUser($user_id)
|
||||
{
|
||||
return $this->userModel->remove($user_id);
|
||||
}
|
||||
|
||||
public function disableUser($user_id)
|
||||
{
|
||||
return $this->userModel->disable($user_id);
|
||||
}
|
||||
|
||||
public function enableUser($user_id)
|
||||
{
|
||||
return $this->userModel->enable($user_id);
|
||||
}
|
||||
|
||||
public function isActiveUser($user_id)
|
||||
{
|
||||
return $this->userModel->isActive($user_id);
|
||||
}
|
||||
|
||||
public function createUser($username, $password, $name = '', $email = '', $role = Role::APP_USER)
|
||||
{
|
||||
$values = array(
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'confirmation' => $password,
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'role' => $role,
|
||||
);
|
||||
|
||||
list($valid, ) = $this->userValidator->validateCreation($values);
|
||||
return $valid ? $this->userModel->create($values) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create LDAP user in the database
|
||||
*
|
||||
* Only "anonymous" and "proxy" LDAP authentication are supported by this method
|
||||
*
|
||||
* User information will be fetched from the LDAP server
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return bool|int
|
||||
*/
|
||||
public function createLdapUser($username)
|
||||
{
|
||||
if (LDAP_BIND_TYPE === 'user') {
|
||||
$this->logger->error('LDAP authentication "user" is not supported by this API call');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
$ldap = LdapClient::connect();
|
||||
$ldap->setLogger($this->logger);
|
||||
$user = LdapUser::getUser($ldap, $username);
|
||||
|
||||
if ($user === null) {
|
||||
$this->logger->info('User not found in LDAP server');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user->getUsername() === '') {
|
||||
throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME');
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'username' => $user->getUsername(),
|
||||
'name' => $user->getName(),
|
||||
'email' => $user->getEmail(),
|
||||
'role' => $user->getRole(),
|
||||
'is_ldap_user' => 1,
|
||||
);
|
||||
|
||||
return $this->userModel->create($values);
|
||||
|
||||
} catch (LdapException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function updateUser($id, $username = null, $name = null, $email = null, $role = null)
|
||||
{
|
||||
$values = $this->filterValues(array(
|
||||
'id' => $id,
|
||||
'username' => $username,
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'role' => $role,
|
||||
));
|
||||
|
||||
list($valid, ) = $this->userValidator->validateApiModification($values);
|
||||
return $valid && $this->userModel->update($values);
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
|
||||
use Kanboard\Core\Security\SessionCheckProviderInterface;
|
||||
use Kanboard\Model\UserModel;
|
||||
use Kanboard\User\DatabaseUserProvider;
|
||||
|
||||
/**
|
||||
* Database Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterface, SessionCheckProviderInterface
|
||||
{
|
||||
/**
|
||||
* User properties
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $userInfo = array();
|
||||
|
||||
/**
|
||||
* Username
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $username = '';
|
||||
|
||||
/**
|
||||
* Password
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $password = '';
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Database';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$user = $this->db
|
||||
->table(UserModel::TABLE)
|
||||
->columns('id', 'password')
|
||||
->eq('username', $this->username)
|
||||
->eq('disable_login_form', 0)
|
||||
->eq('is_ldap_user', 0)
|
||||
->eq('is_active', 1)
|
||||
->findOne();
|
||||
|
||||
if (! empty($user) && password_verify($this->password, $user['password'])) {
|
||||
$this->userInfo = $user;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user session is valid
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValidSession()
|
||||
{
|
||||
return $this->userModel->isActive($this->userSession->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user object
|
||||
*
|
||||
* @access public
|
||||
* @return \Kanboard\User\DatabaseUserProvider
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
if (empty($this->userInfo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DatabaseUserProvider($this->userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set username
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password
|
||||
*
|
||||
* @access public
|
||||
* @param string $password
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use LogicException;
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Ldap\Client as LdapClient;
|
||||
use Kanboard\Core\Ldap\ClientException as LdapException;
|
||||
use Kanboard\Core\Ldap\User as LdapUser;
|
||||
use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
|
||||
|
||||
/**
|
||||
* LDAP Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class LdapAuth extends Base implements PasswordAuthenticationProviderInterface
|
||||
{
|
||||
/**
|
||||
* User properties
|
||||
*
|
||||
* @access protected
|
||||
* @var \Kanboard\User\LdapUserProvider
|
||||
*/
|
||||
protected $userInfo = null;
|
||||
|
||||
/**
|
||||
* Username
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $username = '';
|
||||
|
||||
/**
|
||||
* Password
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $password = '';
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'LDAP';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
try {
|
||||
|
||||
$client = LdapClient::connect($this->getLdapUsername(), $this->getLdapPassword());
|
||||
$client->setLogger($this->logger);
|
||||
|
||||
$user = LdapUser::getUser($client, $this->username);
|
||||
|
||||
if ($user === null) {
|
||||
$this->logger->info('User ('.$this->username.') not found in LDAP server');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user->getUsername() === '') {
|
||||
throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME');
|
||||
}
|
||||
|
||||
$this->logger->info('Authenticate this user: '.$user->getDn());
|
||||
|
||||
if ($client->authenticate($user->getDn(), $this->password)) {
|
||||
$this->userInfo = $user;
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (LdapException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user object
|
||||
*
|
||||
* @access public
|
||||
* @return \Kanboard\User\LdapUserProvider
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set username
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password
|
||||
*
|
||||
* @access public
|
||||
* @param string $password
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP username (proxy auth)
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getLdapUsername()
|
||||
{
|
||||
switch ($this->getLdapBindType()) {
|
||||
case 'proxy':
|
||||
return LDAP_USERNAME;
|
||||
case 'user':
|
||||
return sprintf(LDAP_USERNAME, $this->username);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP password (proxy auth)
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getLdapPassword()
|
||||
{
|
||||
switch ($this->getLdapBindType()) {
|
||||
case 'proxy':
|
||||
return LDAP_PASSWORD;
|
||||
case 'user':
|
||||
return $this->password;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP bind type
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getLdapBindType()
|
||||
{
|
||||
if (LDAP_BIND_TYPE !== 'user' && LDAP_BIND_TYPE !== 'proxy' && LDAP_BIND_TYPE !== 'anonymous') {
|
||||
throw new LogicException('Wrong value for the parameter LDAP_BIND_TYPE');
|
||||
}
|
||||
|
||||
return LDAP_BIND_TYPE;
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Security\PreAuthenticationProviderInterface;
|
||||
use Kanboard\User\DatabaseUserProvider;
|
||||
|
||||
/**
|
||||
* Rember Me Cookie Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RememberMeAuth extends Base implements PreAuthenticationProviderInterface
|
||||
{
|
||||
/**
|
||||
* User properties
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $userInfo = array();
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'RememberMe';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$credentials = $this->rememberMeCookie->read();
|
||||
|
||||
if ($credentials !== false) {
|
||||
$session = $this->rememberMeSessionModel->find($credentials['token'], $credentials['sequence']);
|
||||
|
||||
if (! empty($session)) {
|
||||
$this->rememberMeCookie->write(
|
||||
$session['token'],
|
||||
$this->rememberMeSessionModel->updateSequence($session['token']),
|
||||
$session['expiration']
|
||||
);
|
||||
|
||||
$this->userInfo = $this->userModel->getById($session['user_id']);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user object
|
||||
*
|
||||
* @access public
|
||||
* @return DatabaseUserProvider
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
if (empty($this->userInfo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DatabaseUserProvider($this->userInfo);
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Security\PreAuthenticationProviderInterface;
|
||||
use Kanboard\Core\Security\SessionCheckProviderInterface;
|
||||
use Kanboard\User\ReverseProxyUserProvider;
|
||||
|
||||
/**
|
||||
* Reverse-Proxy Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ReverseProxyAuth extends Base implements PreAuthenticationProviderInterface, SessionCheckProviderInterface
|
||||
{
|
||||
/**
|
||||
* User properties
|
||||
*
|
||||
* @access protected
|
||||
* @var \Kanboard\User\ReverseProxyUserProvider
|
||||
*/
|
||||
protected $userInfo = null;
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'ReverseProxy';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$username = $this->request->getRemoteUser();
|
||||
|
||||
if (! empty($username)) {
|
||||
$userProfile = $this->userModel->getByUsername($username);
|
||||
$this->userInfo = new ReverseProxyUserProvider($username, $userProfile ?: array());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user session is valid
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValidSession()
|
||||
{
|
||||
return $this->request->getRemoteUser() === $this->userSession->getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user object
|
||||
*
|
||||
* @access public
|
||||
* @return ReverseProxyUserProvider
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use Otp\Otp;
|
||||
use Otp\GoogleAuthenticator;
|
||||
use Base32\Base32;
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Security\PostAuthenticationProviderInterface;
|
||||
|
||||
/**
|
||||
* TOTP Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TotpAuth extends Base implements PostAuthenticationProviderInterface
|
||||
{
|
||||
/**
|
||||
* User pin code
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $code = '';
|
||||
|
||||
/**
|
||||
* Private key
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $secret = '';
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return t('Time-based One-time Password Algorithm');
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$otp = new Otp;
|
||||
return $otp->checkTotp(Base32::decode($this->secret), $this->code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before to prompt the user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beforeCode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set validation code
|
||||
*
|
||||
* @access public
|
||||
* @param string $code
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate secret
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function generateSecret()
|
||||
{
|
||||
$this->secret = GoogleAuthenticator::generateRandom();
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set secret token
|
||||
*
|
||||
* @access public
|
||||
* @param string $secret
|
||||
*/
|
||||
public function setSecret($secret)
|
||||
{
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secret token
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSecret()
|
||||
{
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get QR code url
|
||||
*
|
||||
* @access public
|
||||
* @param string $label
|
||||
* @return string
|
||||
*/
|
||||
public function getQrCodeUrl($label)
|
||||
{
|
||||
if (empty($this->secret)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return GoogleAuthenticator::getQrCodeUrl('totp', $label, $this->secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key url (empty if no url can be provided)
|
||||
*
|
||||
* @access public
|
||||
* @param string $label
|
||||
* @return string
|
||||
*/
|
||||
public function getKeyUrl($label)
|
||||
{
|
||||
if (empty($this->secret)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return GoogleAuthenticator::getKeyUri('totp', $label, $this->secret);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Pimple\Container;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
/**
|
||||
* Base command class
|
||||
*
|
||||
* @package console
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Kanboard\Validator\PasswordResetValidator $passwordResetValidator
|
||||
* @property \Kanboard\Export\SubtaskExport $subtaskExport
|
||||
* @property \Kanboard\Export\TaskExport $taskExport
|
||||
* @property \Kanboard\Export\TransitionExport $transitionExport
|
||||
* @property \Kanboard\Model\NotificationModel $notificationModel
|
||||
* @property \Kanboard\Model\ProjectModel $projectModel
|
||||
* @property \Kanboard\Model\ProjectPermissionModel $projectPermissionModel
|
||||
* @property \Kanboard\Model\ProjectDailyColumnStatsModel $projectDailyColumnStatsModel
|
||||
* @property \Kanboard\Model\ProjectDailyStatsModel $projectDailyStatsModel
|
||||
* @property \Kanboard\Model\TaskModel $taskModel
|
||||
* @property \Kanboard\Model\TaskFinderModel $taskFinderModel
|
||||
* @property \Kanboard\Model\UserModel $userModel
|
||||
* @property \Kanboard\Model\UserNotificationModel $userNotificationModel
|
||||
* @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel
|
||||
* @property \Kanboard\Model\ProjectUserRoleModel $projectUserRoleModel
|
||||
* @property \Kanboard\Core\Plugin\Loader $pluginLoader
|
||||
* @property \Kanboard\Core\Http\Client $httpClient
|
||||
* @property \Kanboard\Core\Queue\QueueManager $queueManager
|
||||
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
|
||||
*/
|
||||
abstract class BaseCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Container instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Pimple\Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->container[$name];
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
||||
class CronjobCommand extends BaseCommand
|
||||
{
|
||||
private $commands = array(
|
||||
'projects:daily-stats',
|
||||
'notification:overdue-tasks',
|
||||
'trigger:tasks',
|
||||
);
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('cronjob')
|
||||
->setDescription('Execute daily cronjob');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
foreach ($this->commands as $command) {
|
||||
$job = $this->getApplication()->find($command);
|
||||
$job->run(new ArrayInput(array('command' => $command)), new NullOutput());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class LocaleComparatorCommand extends BaseCommand
|
||||
{
|
||||
const REF_LOCALE = 'fr_FR';
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('locale:compare')
|
||||
->setDescription('Compare application translations with the '.self::REF_LOCALE.' locale');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$strings = array();
|
||||
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('app'));
|
||||
$it->rewind();
|
||||
|
||||
while ($it->valid()) {
|
||||
if (! $it->isDot() && substr($it->key(), -4) === '.php') {
|
||||
$strings = array_merge($strings, $this->search($it->key()));
|
||||
}
|
||||
|
||||
$it->next();
|
||||
}
|
||||
|
||||
$this->compare(array_unique($strings));
|
||||
}
|
||||
|
||||
public function show(array $strings)
|
||||
{
|
||||
foreach ($strings as $string) {
|
||||
echo " '".str_replace("'", "\'", $string)."' => '',".PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
public function compare(array $strings)
|
||||
{
|
||||
$reference_file = 'app/Locale/'.self::REF_LOCALE.'/translations.php';
|
||||
$reference = include $reference_file;
|
||||
|
||||
echo str_repeat('#', 70).PHP_EOL;
|
||||
echo 'MISSING STRINGS'.PHP_EOL;
|
||||
echo str_repeat('#', 70).PHP_EOL;
|
||||
$this->show(array_diff($strings, array_keys($reference)));
|
||||
|
||||
echo str_repeat('#', 70).PHP_EOL;
|
||||
echo 'USELESS STRINGS'.PHP_EOL;
|
||||
echo str_repeat('#', 70).PHP_EOL;
|
||||
$this->show(array_diff(array_keys($reference), $strings));
|
||||
}
|
||||
|
||||
public function search($filename)
|
||||
{
|
||||
$content = file_get_contents($filename);
|
||||
$strings = array();
|
||||
|
||||
if (preg_match_all('/\b[et]\((\'\K.*?\') *[\)\,]/', $content, $matches) && isset($matches[1])) {
|
||||
$strings = $matches[1];
|
||||
}
|
||||
|
||||
if (preg_match_all('/\bdt\((\'\K.*?\') *[\)\,]/', $content, $matches) && isset($matches[1])) {
|
||||
$strings = array_merge($strings, $matches[1]);
|
||||
}
|
||||
|
||||
array_walk($strings, function (&$value) {
|
||||
$value = trim($value, "'");
|
||||
$value = str_replace("\'", "'", $value);
|
||||
});
|
||||
|
||||
return $strings;
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class LocaleSyncCommand extends BaseCommand
|
||||
{
|
||||
const REF_LOCALE = 'fr_FR';
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('locale:sync')
|
||||
->setDescription('Synchronize all translations based on the '.self::REF_LOCALE.' locale');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$reference_file = 'app/Locale/'.self::REF_LOCALE.'/translations.php';
|
||||
$reference = include $reference_file;
|
||||
|
||||
foreach (new DirectoryIterator('app/Locale') as $fileInfo) {
|
||||
if (! $fileInfo->isDot() && $fileInfo->isDir() && $fileInfo->getFilename() !== self::REF_LOCALE) {
|
||||
$filename = 'app/Locale/'.$fileInfo->getFilename().'/translations.php';
|
||||
echo $fileInfo->getFilename().' ('.$filename.')'.PHP_EOL;
|
||||
|
||||
file_put_contents($filename, $this->updateFile($reference, $filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updateFile(array $reference, $outdated_file)
|
||||
{
|
||||
$outdated = include $outdated_file;
|
||||
|
||||
$output = '<?php'.PHP_EOL.PHP_EOL;
|
||||
$output .= 'return array('.PHP_EOL;
|
||||
|
||||
foreach ($reference as $key => $value) {
|
||||
if (! empty($outdated[$key])) {
|
||||
$output .= " '".str_replace("'", "\'", $key)."' => '".str_replace("'", "\'", $outdated[$key])."',\n";
|
||||
} else {
|
||||
$output .= " // '".str_replace("'", "\'", $key)."' => '',\n";
|
||||
}
|
||||
}
|
||||
|
||||
$output .= ");\n";
|
||||
return $output;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Core\Plugin\Installer;
|
||||
use Kanboard\Core\Plugin\PluginInstallerException;
|
||||
use LogicException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class PluginInstallCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('plugin:install')
|
||||
->setDescription('Install a plugin from a remote Zip archive')
|
||||
->addArgument('url', InputArgument::REQUIRED, 'Archive URL');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (!Installer::isConfigured()) {
|
||||
throw new LogicException('Kanboard is not configured to install plugins itself');
|
||||
}
|
||||
|
||||
try {
|
||||
$installer = new Installer($this->container);
|
||||
$installer->install($input->getArgument('url'));
|
||||
$output->writeln('<info>Plugin installed successfully</info>');
|
||||
} catch (PluginInstallerException $e) {
|
||||
$output->writeln('<error>'.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Core\Plugin\Installer;
|
||||
use Kanboard\Core\Plugin\PluginInstallerException;
|
||||
use LogicException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class PluginUninstallCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('plugin:uninstall')
|
||||
->setDescription('Remove a plugin')
|
||||
->addArgument('pluginId', InputArgument::REQUIRED, 'Plugin directory name');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (!Installer::isConfigured()) {
|
||||
throw new LogicException('Kanboard is not configured to install plugins itself');
|
||||
}
|
||||
|
||||
try {
|
||||
$installer = new Installer($this->container);
|
||||
$installer->uninstall($input->getArgument('pluginId'));
|
||||
$output->writeln('<info>Plugin removed successfully</info>');
|
||||
} catch (PluginInstallerException $e) {
|
||||
$output->writeln('<error>'.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Core\Plugin\Base as BasePlugin;
|
||||
use Kanboard\Core\Plugin\Directory;
|
||||
use Kanboard\Core\Plugin\Installer;
|
||||
use LogicException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class PluginUpgradeCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('plugin:upgrade')
|
||||
->setDescription('Update all installed plugins')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (!Installer::isConfigured()) {
|
||||
throw new LogicException('Kanboard is not configured to install plugins itself');
|
||||
}
|
||||
|
||||
$installer = new Installer($this->container);
|
||||
$availablePlugins = Directory::getInstance($this->container)->getAvailablePlugins();
|
||||
|
||||
foreach ($this->pluginLoader->getPlugins() as $installedPlugin) {
|
||||
$pluginDetails = $this->getPluginDetails($availablePlugins, $installedPlugin);
|
||||
|
||||
if ($pluginDetails === null) {
|
||||
$output->writeln('<error>* Plugin not available in the directory: '.$installedPlugin->getPluginName().'</error>');
|
||||
} elseif ($pluginDetails['version'] > $installedPlugin->getPluginVersion()) {
|
||||
$output->writeln('<comment>* Updating plugin: '.$installedPlugin->getPluginName().'</comment>');
|
||||
$installer->update($pluginDetails['download']);
|
||||
} else {
|
||||
$output->writeln('<info>* Plugin up to date: '.$installedPlugin->getPluginName().'</info>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getPluginDetails(array $availablePlugins, BasePlugin $installedPlugin)
|
||||
{
|
||||
foreach ($availablePlugins as $availablePlugin) {
|
||||
if ($availablePlugin['title'] === $installedPlugin->getPluginName()) {
|
||||
return $availablePlugin;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Core\Csv;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ProjectDailyColumnStatsExportCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('export:daily-project-column-stats')
|
||||
->setDescription('Daily project column stats CSV export (number of tasks per column and per day)')
|
||||
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
|
||||
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
|
||||
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$data = $this->projectDailyColumnStatsModel->getAggregatedMetrics(
|
||||
$input->getArgument('project_id'),
|
||||
$input->getArgument('start_date'),
|
||||
$input->getArgument('end_date')
|
||||
);
|
||||
|
||||
if (is_array($data)) {
|
||||
Csv::output($data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Model\ProjectModel;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ProjectDailyStatsCalculationCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('projects:daily-stats')
|
||||
->setDescription('Calculate daily statistics for all projects');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$projects = $this->projectModel->getAllByStatus(ProjectModel::ACTIVE);
|
||||
|
||||
foreach ($projects as $project) {
|
||||
$output->writeln('Run calculation for '.$project['name']);
|
||||
$this->projectDailyColumnStatsModel->updateTotals($project['id'], date('Y-m-d'));
|
||||
$this->projectDailyStatsModel->updateTotals($project['id'], date('Y-m-d'));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
class ResetPasswordCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('user:reset-password')
|
||||
->setDescription('Change user password')
|
||||
->addArgument('username', InputArgument::REQUIRED, 'Username')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$helper = $this->getHelper('question');
|
||||
$username = $input->getArgument('username');
|
||||
|
||||
$passwordQuestion = new Question('What is the new password for '.$username.'? (characters are not printed)'.PHP_EOL);
|
||||
$passwordQuestion->setHidden(true);
|
||||
$passwordQuestion->setHiddenFallback(false);
|
||||
|
||||
$password = $helper->ask($input, $output, $passwordQuestion);
|
||||
|
||||
$confirmationQuestion = new Question('Confirmation:'.PHP_EOL);
|
||||
$confirmationQuestion->setHidden(true);
|
||||
$confirmationQuestion->setHiddenFallback(false);
|
||||
|
||||
$confirmation = $helper->ask($input, $output, $confirmationQuestion);
|
||||
|
||||
if ($this->validatePassword($output, $password, $confirmation)) {
|
||||
$this->resetPassword($output, $username, $password);
|
||||
}
|
||||
}
|
||||
|
||||
private function validatePassword(OutputInterface $output, $password, $confirmation)
|
||||
{
|
||||
list($valid, $errors) = $this->passwordResetValidator->validateModification(array(
|
||||
'password' => $password,
|
||||
'confirmation' => $confirmation,
|
||||
));
|
||||
|
||||
if (!$valid) {
|
||||
foreach ($errors as $error_list) {
|
||||
foreach ($error_list as $error) {
|
||||
$output->writeln('<error>'.$error.'</error>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
private function resetPassword(OutputInterface $output, $username, $password)
|
||||
{
|
||||
$userId = $this->userModel->getIdByUsername($username);
|
||||
|
||||
if (empty($userId)) {
|
||||
$output->writeln('<error>User not found</error>');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->userModel->update(array('id' => $userId, 'password' => $password))) {
|
||||
$output->writeln('<error>Unable to update password</error>');
|
||||
return false;
|
||||
}
|
||||
|
||||
$output->writeln('<info>Password updated successfully</info>');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ResetTwoFactorCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('user:reset-2fa')
|
||||
->setDescription('Remove two-factor authentication for a user')
|
||||
->addArgument('username', InputArgument::REQUIRED, 'Username');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$username = $input->getArgument('username');
|
||||
$userId = $this->userModel->getIdByUsername($username);
|
||||
|
||||
if (empty($userId)) {
|
||||
$output->writeln('<error>User not found</error>');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->userModel->update(array('id' => $userId, 'twofactor_activated' => 0, 'twofactor_secret' => ''))) {
|
||||
$output->writeln('<error>Unable to update user profile</error>');
|
||||
return false;
|
||||
}
|
||||
|
||||
$output->writeln('<info>Two-factor authentication disabled</info>');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Core\Csv;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class SubtaskExportCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('export:subtasks')
|
||||
->setDescription('Subtasks CSV export')
|
||||
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
|
||||
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
|
||||
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$data = $this->subtaskExport->export(
|
||||
$input->getArgument('project_id'),
|
||||
$input->getArgument('start_date'),
|
||||
$input->getArgument('end_date')
|
||||
);
|
||||
|
||||
if (is_array($data)) {
|
||||
Csv::output($data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Core\Csv;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class TaskExportCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('export:tasks')
|
||||
->setDescription('Tasks CSV export')
|
||||
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
|
||||
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
|
||||
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$data = $this->taskExport->export(
|
||||
$input->getArgument('project_id'),
|
||||
$input->getArgument('start_date'),
|
||||
$input->getArgument('end_date')
|
||||
);
|
||||
|
||||
if (is_array($data)) {
|
||||
Csv::output($data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Model\TaskModel;
|
||||
use Kanboard\Core\Security\Role;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class TaskOverdueNotificationCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('notification:overdue-tasks')
|
||||
->setDescription('Send notifications for overdue tasks')
|
||||
->addOption('show', null, InputOption::VALUE_NONE, 'Show sent overdue tasks')
|
||||
->addOption('group', null, InputOption::VALUE_NONE, 'Group all overdue tasks for one user (from all projects) in one email')
|
||||
->addOption('manager', null, InputOption::VALUE_NONE, 'Send all overdue tasks to project manager(s) in one email');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($input->getOption('group')) {
|
||||
$tasks = $this->sendGroupOverdueTaskNotifications();
|
||||
} elseif ($input->getOption('manager')) {
|
||||
$tasks = $this->sendOverdueTaskNotificationsToManagers();
|
||||
} else {
|
||||
$tasks = $this->sendOverdueTaskNotifications();
|
||||
}
|
||||
|
||||
if ($input->getOption('show')) {
|
||||
$this->showTable($output, $tasks);
|
||||
}
|
||||
}
|
||||
|
||||
public function showTable(OutputInterface $output, array $tasks)
|
||||
{
|
||||
$rows = array();
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$rows[] = array(
|
||||
$task['id'],
|
||||
$task['title'],
|
||||
date('Y-m-d', $task['date_due']),
|
||||
$task['project_id'],
|
||||
$task['project_name'],
|
||||
$task['assignee_name'] ?: $task['assignee_username'],
|
||||
);
|
||||
}
|
||||
|
||||
$table = new Table($output);
|
||||
$table
|
||||
->setHeaders(array('Id', 'Title', 'Due date', 'Project Id', 'Project name', 'Assignee'))
|
||||
->setRows($rows)
|
||||
->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all overdue tasks for one user in one email
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function sendGroupOverdueTaskNotifications()
|
||||
{
|
||||
$tasks = $this->taskFinderModel->getOverdueTasks();
|
||||
|
||||
foreach ($this->groupByColumn($tasks, 'owner_id') as $user_tasks) {
|
||||
$users = $this->userNotificationModel->getUsersWithNotificationEnabled($user_tasks[0]['project_id']);
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->sendUserOverdueTaskNotifications($user, $user_tasks);
|
||||
}
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all overdue tasks in one email to project manager(s)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function sendOverdueTaskNotificationsToManagers()
|
||||
{
|
||||
$tasks = $this->taskFinderModel->getOverdueTasks();
|
||||
|
||||
foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) {
|
||||
$users = $this->userNotificationModel->getUsersWithNotificationEnabled($project_id);
|
||||
$managers = array();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$role = $this->projectUserRoleModel->getUserRole($project_id, $user['id']);
|
||||
if($role == Role::PROJECT_MANAGER) {
|
||||
$managers[] = $user;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($managers as $manager) {
|
||||
$this->sendUserOverdueTaskNotificationsToManagers($manager, $project_tasks);
|
||||
}
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send overdue tasks
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function sendOverdueTaskNotifications()
|
||||
{
|
||||
$tasks = $this->taskFinderModel->getOverdueTasks();
|
||||
|
||||
foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) {
|
||||
$users = $this->userNotificationModel->getUsersWithNotificationEnabled($project_id);
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->sendUserOverdueTaskNotifications($user, $project_tasks);
|
||||
}
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send overdue tasks for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param array $user
|
||||
* @param array $tasks
|
||||
*/
|
||||
public function sendUserOverdueTaskNotifications(array $user, array $tasks)
|
||||
{
|
||||
$user_tasks = array();
|
||||
$project_names = array();
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
if ($this->userNotificationFilterModel->shouldReceiveNotification($user, array('task' => $task))) {
|
||||
$user_tasks[] = $task;
|
||||
$project_names[$task['project_id']] = $task['project_name'];
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($user_tasks)) {
|
||||
$this->userNotificationModel->sendUserNotification(
|
||||
$user,
|
||||
TaskModel::EVENT_OVERDUE,
|
||||
array('tasks' => $user_tasks, 'project_name' => implode(', ', $project_names))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send overdue tasks for a project manager(s)
|
||||
*
|
||||
* @access public
|
||||
* @param array $manager
|
||||
* @param array $tasks
|
||||
*/
|
||||
public function sendUserOverdueTaskNotificationsToManagers(array $manager, array $tasks)
|
||||
{
|
||||
$this->userNotificationModel->sendUserNotification(
|
||||
$manager,
|
||||
TaskModel::EVENT_OVERDUE,
|
||||
array('tasks' => $tasks, 'project_name' => $tasks[0]['project_name'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group a collection of records by a column
|
||||
*
|
||||
* @access public
|
||||
* @param array $collection
|
||||
* @param string $column
|
||||
* @return array
|
||||
*/
|
||||
public function groupByColumn(array $collection, $column)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$result[$item[$column]][] = $item;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Kanboard\Model\TaskModel;
|
||||
use Kanboard\Event\TaskListEvent;
|
||||
|
||||
class TaskTriggerCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('trigger:tasks')
|
||||
->setDescription('Trigger scheduler event for all tasks');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
foreach ($this->getProjectIds() as $project_id) {
|
||||
$tasks = $this->taskFinderModel->getAll($project_id);
|
||||
$nb_tasks = count($tasks);
|
||||
|
||||
if ($nb_tasks > 0) {
|
||||
$output->writeln('Trigger task event: project_id='.$project_id.', nb_tasks='.$nb_tasks);
|
||||
$this->sendEvent($tasks, $project_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getProjectIds()
|
||||
{
|
||||
$listeners = $this->dispatcher->getListeners(TaskModel::EVENT_DAILY_CRONJOB);
|
||||
$project_ids = array();
|
||||
|
||||
foreach ($listeners as $listener) {
|
||||
$project_ids[] = $listener[0]->getProjectId();
|
||||
}
|
||||
|
||||
return array_unique($project_ids);
|
||||
}
|
||||
|
||||
private function sendEvent(array &$tasks, $project_id)
|
||||
{
|
||||
$event = new TaskListEvent(array('project_id' => $project_id));
|
||||
$event->setTasks($tasks);
|
||||
|
||||
$this->dispatcher->dispatch(TaskModel::EVENT_DAILY_CRONJOB, $event);
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Kanboard\Core\Csv;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class TransitionExportCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('export:transitions')
|
||||
->setDescription('Task transitions CSV export')
|
||||
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
|
||||
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
|
||||
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$data = $this->transitionExport->export(
|
||||
$input->getArgument('project_id'),
|
||||
$input->getArgument('start_date'),
|
||||
$input->getArgument('end_date')
|
||||
);
|
||||
|
||||
if (is_array($data)) {
|
||||
Csv::output($data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class WorkerCommand
|
||||
*
|
||||
* @package Kanboard\Console
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class WorkerCommand extends BaseCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('worker')
|
||||
->setDescription('Execute queue worker')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->queueManager->listen();
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Automatic Actions Controller
|
||||
*
|
||||
* @package Kanboard\Controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ActionController extends BaseController
|
||||
{
|
||||
/**
|
||||
* List of automatic actions for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$actions = $this->actionModel->getAllByProject($project['id']);
|
||||
|
||||
$this->response->html($this->helper->layout->project('action/index', array(
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'project' => $project,
|
||||
'actions' => $actions,
|
||||
'available_actions' => $this->actionManager->getAvailableActions(),
|
||||
'available_events' => $this->eventManager->getAll(),
|
||||
'available_params' => $this->actionManager->getAvailableParameters($actions),
|
||||
'columns_list' => $this->columnModel->getList($project['id']),
|
||||
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']),
|
||||
'projects_list' => $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId()),
|
||||
'colors_list' => $this->colorModel->getList(),
|
||||
'categories_list' => $this->categoryModel->getList($project['id']),
|
||||
'links_list' => $this->linkModel->getList(0, false),
|
||||
'title' => t('Automatic actions')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing an action
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->helper->layout->project('action/remove', array(
|
||||
'action' => $this->actionModel->getById($this->request->getIntegerParam('action_id')),
|
||||
'available_events' => $this->eventManager->getAll(),
|
||||
'available_actions' => $this->actionManager->getAvailableActions(),
|
||||
'project' => $project,
|
||||
'title' => t('Remove an action')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an action
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$action = $this->actionModel->getById($this->request->getIntegerParam('action_id'));
|
||||
|
||||
if (! empty($action) && $this->actionModel->remove($action['id'])) {
|
||||
$this->flash->success(t('Action removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this action.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Action Creation Controller
|
||||
*
|
||||
* @package Kanboard\Controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ActionCreationController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Show the form (step 1)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->render('action_creation/create', array(
|
||||
'project' => $project,
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'available_actions' => $this->actionManager->getAvailableActions(),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the event according to the action (step 2)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function event()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (empty($values['action_name']) || empty($values['project_id'])) {
|
||||
return $this->create();
|
||||
}
|
||||
|
||||
return $this->response->html($this->template->render('action_creation/event', array(
|
||||
'values' => $values,
|
||||
'project' => $project,
|
||||
'available_actions' => $this->actionManager->getAvailableActions(),
|
||||
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define action parameters (step 3)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function params()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) {
|
||||
return $this->create();
|
||||
}
|
||||
|
||||
$action = $this->actionManager->getAction($values['action_name']);
|
||||
$action_params = $action->getActionRequiredParameters();
|
||||
|
||||
if (empty($action_params)) {
|
||||
$this->doCreation($project, $values + array('params' => array()));
|
||||
}
|
||||
|
||||
$projects_list = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
|
||||
unset($projects_list[$project['id']]);
|
||||
|
||||
return $this->response->html($this->template->render('action_creation/params', array(
|
||||
'values' => $values,
|
||||
'action_params' => $action_params,
|
||||
'columns_list' => $this->columnModel->getList($project['id']),
|
||||
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']),
|
||||
'projects_list' => $projects_list,
|
||||
'colors_list' => $this->colorModel->getList(),
|
||||
'categories_list' => $this->categoryModel->getList($project['id']),
|
||||
'links_list' => $this->linkModel->getList(0, false),
|
||||
'priorities_list' => $this->projectTaskPriorityModel->getPriorities($project),
|
||||
'project' => $project,
|
||||
'available_actions' => $this->actionManager->getAvailableActions(),
|
||||
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the action (last step)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->doCreation($this->getProject(), $this->request->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to save the action
|
||||
*
|
||||
* @access private
|
||||
* @param array $project Project properties
|
||||
* @param array $values Form values
|
||||
*/
|
||||
private function doCreation(array $project, array $values)
|
||||
{
|
||||
list($valid, ) = $this->actionValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->actionModel->create($values) !== false) {
|
||||
$this->flash->success(t('Your automatic action have been created successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to create your automatic action.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue