mirror of
https://github.com/YunoHost-Apps/kanboard_ynh.git
synced 2024-09-03 19:36:17 +02:00
Update sources to v1.0.26
This commit is contained in:
parent
9708947adc
commit
dcb168f23a
453 changed files with 14347 additions and 8846 deletions
|
@ -1,3 +1,83 @@
|
|||
Version 1.0.26
|
||||
--------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* API procedures:
|
||||
- "moveColumnUp" and "moveColumnDown" are replace by "changeColumnPosition"
|
||||
- "moveSwimlaneUp" and "moveSwimlaneDown" are replace 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
|
||||
--------------
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
$params[] = $key.'='.var_export($value, true);
|
||||
}
|
||||
|
||||
return $this->getName().'('.implode('|', $params).'])';
|
||||
return $this->getName().'('.implode('|', $params).')';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,7 +71,7 @@ class CommentCreationMoveTaskColumn extends Base
|
|||
return false;
|
||||
}
|
||||
|
||||
$column = $this->board->getColumn($data['column_id']);
|
||||
$column = $this->column->getById($data['column_id']);
|
||||
|
||||
return (bool) $this->comment->create(array(
|
||||
'comment' => t('Moved to column %s', $column['title']),
|
||||
|
|
95
sources/app/Action/TaskCloseNoActivity.php
Normal file
95
sources/app/Action/TaskCloseNoActivity.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\Task;
|
||||
|
||||
/**
|
||||
* 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(Task::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->taskStatus->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;
|
||||
}
|
||||
}
|
|
@ -74,7 +74,7 @@ class TaskDuplicateAnotherProject extends Base
|
|||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
$destination_column_id = $this->board->getFirstColumn($this->getParam('project_id'));
|
||||
$destination_column_id = $this->column->getFirstColumnId($this->getParam('project_id'));
|
||||
return (bool) $this->taskDuplication->duplicateToProject($data['task_id'], $this->getParam('project_id'), null, $destination_column_id);
|
||||
}
|
||||
|
||||
|
|
124
sources/app/Action/TaskEmailNoActivity.php
Normal file
124
sources/app/Action/TaskEmailNoActivity.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\Task;
|
||||
|
||||
/**
|
||||
* 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(
|
||||
Task::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->user->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->taskFinder->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->config->get('application_url')))
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ class AverageTimeSpentColumnAnalytic extends Base
|
|||
private function initialize($project_id)
|
||||
{
|
||||
$stats = array();
|
||||
$columns = $this->board->getColumnsList($project_id);
|
||||
$columns = $this->column->getList($project_id);
|
||||
|
||||
foreach ($columns as $column_id => $column_title) {
|
||||
$stats[$column_id] = array(
|
||||
|
|
|
@ -23,7 +23,7 @@ class TaskDistributionAnalytic extends Base
|
|||
{
|
||||
$metrics = array();
|
||||
$total = 0;
|
||||
$columns = $this->board->getColumns($project_id);
|
||||
$columns = $this->column->getAll($project_id);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$nb_tasks = $this->taskFinder->countByColumnId($project_id, $column['id']);
|
||||
|
|
|
@ -15,39 +15,4 @@ class Board extends Base
|
|||
$this->checkProjectPermission($project_id);
|
||||
return $this->board->getBoard($project_id);
|
||||
}
|
||||
|
||||
public function getColumns($project_id)
|
||||
{
|
||||
return $this->board->getColumns($project_id);
|
||||
}
|
||||
|
||||
public function getColumn($column_id)
|
||||
{
|
||||
return $this->board->getColumn($column_id);
|
||||
}
|
||||
|
||||
public function moveColumnUp($project_id, $column_id)
|
||||
{
|
||||
return $this->board->moveUp($project_id, $column_id);
|
||||
}
|
||||
|
||||
public function moveColumnDown($project_id, $column_id)
|
||||
{
|
||||
return $this->board->moveDown($project_id, $column_id);
|
||||
}
|
||||
|
||||
public function updateColumn($column_id, $title, $task_limit = 0, $description = '')
|
||||
{
|
||||
return $this->board->updateColumn($column_id, $title, $task_limit, $description);
|
||||
}
|
||||
|
||||
public function addColumn($project_id, $title, $task_limit = 0, $description = '')
|
||||
{
|
||||
return $this->board->addColumn($project_id, $title, $task_limit, $description);
|
||||
}
|
||||
|
||||
public function removeColumn($column_id)
|
||||
{
|
||||
return $this->board->removeColumn($column_id);
|
||||
}
|
||||
}
|
||||
|
|
42
sources/app/Api/Column.php
Normal file
42
sources/app/Api/Column.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Api;
|
||||
|
||||
/**
|
||||
* Column API controller
|
||||
*
|
||||
* @package api
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Column extends Base
|
||||
{
|
||||
public function getColumns($project_id)
|
||||
{
|
||||
return $this->column->getAll($project_id);
|
||||
}
|
||||
|
||||
public function getColumn($column_id)
|
||||
{
|
||||
return $this->column->getById($column_id);
|
||||
}
|
||||
|
||||
public function updateColumn($column_id, $title, $task_limit = 0, $description = '')
|
||||
{
|
||||
return $this->column->update($column_id, $title, $task_limit, $description);
|
||||
}
|
||||
|
||||
public function addColumn($project_id, $title, $task_limit = 0, $description = '')
|
||||
{
|
||||
return $this->column->create($project_id, $title, $task_limit, $description);
|
||||
}
|
||||
|
||||
public function removeColumn($column_id)
|
||||
{
|
||||
return $this->column->remove($column_id);
|
||||
}
|
||||
|
||||
public function changeColumnPosition($project_id, $column_id, $position)
|
||||
{
|
||||
return $this->column->changePosition($project_id, $column_id, $position);
|
||||
}
|
||||
}
|
|
@ -10,22 +10,22 @@ use Kanboard\Core\ObjectStorage\ObjectStorageException;
|
|||
* @package api
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class File extends \Kanboard\Core\Base
|
||||
class File extends Base
|
||||
{
|
||||
public function getFile($file_id)
|
||||
public function getTaskFile($file_id)
|
||||
{
|
||||
return $this->file->getById($file_id);
|
||||
return $this->taskFile->getById($file_id);
|
||||
}
|
||||
|
||||
public function getAllFiles($task_id)
|
||||
public function getAllTaskFiles($task_id)
|
||||
{
|
||||
return $this->file->getAll($task_id);
|
||||
return $this->taskFile->getAll($task_id);
|
||||
}
|
||||
|
||||
public function downloadFile($file_id)
|
||||
public function downloadTaskFile($file_id)
|
||||
{
|
||||
try {
|
||||
$file = $this->file->getById($file_id);
|
||||
$file = $this->taskFile->getById($file_id);
|
||||
|
||||
if (! empty($file)) {
|
||||
return base64_encode($this->objectStorage->get($file['path']));
|
||||
|
@ -36,23 +36,55 @@ class File extends \Kanboard\Core\Base
|
|||
}
|
||||
}
|
||||
|
||||
public function createFile($project_id, $task_id, $filename, $blob)
|
||||
public function createTaskFile($project_id, $task_id, $filename, $blob)
|
||||
{
|
||||
try {
|
||||
return $this->file->uploadContent($project_id, $task_id, $filename, $blob);
|
||||
return $this->taskFile->uploadContent($task_id, $filename, $blob);
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeTaskFile($file_id)
|
||||
{
|
||||
return $this->taskFile->remove($file_id);
|
||||
}
|
||||
|
||||
public function removeAllTaskFiles($task_id)
|
||||
{
|
||||
return $this->taskFile->removeAll($task_id);
|
||||
}
|
||||
|
||||
// Deprecated procedures
|
||||
|
||||
public function getFile($file_id)
|
||||
{
|
||||
return $this->getTaskFile($file_id);
|
||||
}
|
||||
|
||||
public function getAllFiles($task_id)
|
||||
{
|
||||
return $this->getAllTaskFiles($task_id);
|
||||
}
|
||||
|
||||
public function downloadFile($file_id)
|
||||
{
|
||||
return $this->downloadTaskFile($file_id);
|
||||
}
|
||||
|
||||
public function createFile($project_id, $task_id, $filename, $blob)
|
||||
{
|
||||
return $this->createTaskFile($project_id, $task_id, $filename, $blob);
|
||||
}
|
||||
|
||||
public function removeFile($file_id)
|
||||
{
|
||||
return $this->file->remove($file_id);
|
||||
return $this->removeTaskFile($file_id);
|
||||
}
|
||||
|
||||
public function removeAllFiles($task_id)
|
||||
{
|
||||
return $this->file->removeAll($task_id);
|
||||
return $this->removeAllTaskFiles($task_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,10 @@ class Me extends Base
|
|||
|
||||
public function createMyPrivateProject($name, $description = null)
|
||||
{
|
||||
if ($this->config->get('disable_private_project', 0) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'name' => $name,
|
||||
'description' => $description,
|
||||
|
|
|
@ -48,9 +48,11 @@ class Swimlane extends \Kanboard\Core\Base
|
|||
public function updateSwimlane($swimlane_id, $name, $description = null)
|
||||
{
|
||||
$values = array('id' => $swimlane_id, 'name' => $name);
|
||||
|
||||
if (!is_null($description)) {
|
||||
$values['description'] = $description;
|
||||
}
|
||||
|
||||
return $this->swimlane->update($values);
|
||||
}
|
||||
|
||||
|
@ -69,13 +71,8 @@ class Swimlane extends \Kanboard\Core\Base
|
|||
return $this->swimlane->enable($project_id, $swimlane_id);
|
||||
}
|
||||
|
||||
public function moveSwimlaneUp($project_id, $swimlane_id)
|
||||
public function changeSwimlanePosition($project_id, $swimlane_id, $position)
|
||||
{
|
||||
return $this->swimlane->moveUp($project_id, $swimlane_id);
|
||||
}
|
||||
|
||||
public function moveSwimlaneDown($project_id, $swimlane_id)
|
||||
{
|
||||
return $this->swimlane->moveDown($project_id, $swimlane_id);
|
||||
return $this->swimlane->changePosition($project_id, $swimlane_id, $position);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,9 +75,9 @@ class Task extends Base
|
|||
}
|
||||
|
||||
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,
|
||||
$recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
|
||||
$recurrence_basedate = 0, $reference = '')
|
||||
$date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0,
|
||||
$recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
|
||||
$recurrence_basedate = 0, $reference = '')
|
||||
{
|
||||
$this->checkProjectPermission($project_id);
|
||||
|
||||
|
@ -115,9 +115,9 @@ class Task extends Base
|
|||
}
|
||||
|
||||
public function updateTask($id, $title = null, $color_id = null, $owner_id = null,
|
||||
$date_due = null, $description = null, $category_id = null, $score = null,
|
||||
$recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
|
||||
$recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
|
||||
$date_due = null, $description = null, $category_id = null, $score = null,
|
||||
$recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
|
||||
$recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
|
||||
{
|
||||
$this->checkTaskPermission($id);
|
||||
|
||||
|
|
|
@ -21,6 +21,11 @@ class User extends \Kanboard\Core\Base
|
|||
return $this->user->getById($user_id);
|
||||
}
|
||||
|
||||
public function getUserByName($username)
|
||||
{
|
||||
return $this->user->getByUsername($username);
|
||||
}
|
||||
|
||||
public function getAllUsers()
|
||||
{
|
||||
return $this->user->getAll();
|
||||
|
@ -31,6 +36,21 @@ class User extends \Kanboard\Core\Base
|
|||
return $this->user->remove($user_id);
|
||||
}
|
||||
|
||||
public function disableUser($user_id)
|
||||
{
|
||||
return $this->user->disable($user_id);
|
||||
}
|
||||
|
||||
public function enableUser($user_id)
|
||||
{
|
||||
return $this->user->enable($user_id);
|
||||
}
|
||||
|
||||
public function isActiveUser($user_id)
|
||||
{
|
||||
return $this->user->isActive($user_id);
|
||||
}
|
||||
|
||||
public function createUser($username, $password, $name = '', $email = '', $role = Role::APP_USER)
|
||||
{
|
||||
$values = array(
|
||||
|
|
|
@ -65,6 +65,7 @@ class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterfa
|
|||
->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'])) {
|
||||
|
@ -83,7 +84,7 @@ class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterfa
|
|||
*/
|
||||
public function isValidSession()
|
||||
{
|
||||
return $this->user->exists($this->userSession->getId());
|
||||
return $this->user->isActive($this->userSession->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Security\OAuthAuthenticationProviderInterface;
|
||||
use Kanboard\User\GithubUserProvider;
|
||||
|
||||
/**
|
||||
* Github Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class GithubAuth extends Base implements OAuthAuthenticationProviderInterface
|
||||
{
|
||||
/**
|
||||
* User properties
|
||||
*
|
||||
* @access protected
|
||||
* @var \Kanboard\User\GithubUserProvider
|
||||
*/
|
||||
protected $userInfo = null;
|
||||
|
||||
/**
|
||||
* OAuth2 instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Kanboard\Core\Http\OAuth2
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* OAuth2 code
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $code = '';
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Github';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$profile = $this->getProfile();
|
||||
|
||||
if (! empty($profile)) {
|
||||
$this->userInfo = new GithubUserProvider($profile);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Code
|
||||
*
|
||||
* @access public
|
||||
* @param string $code
|
||||
* @return GithubAuth
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user object
|
||||
*
|
||||
* @access public
|
||||
* @return GithubUserProvider
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured OAuth2 service
|
||||
*
|
||||
* @access public
|
||||
* @return \Kanboard\Core\Http\OAuth2
|
||||
*/
|
||||
public function getService()
|
||||
{
|
||||
if (empty($this->service)) {
|
||||
$this->service = $this->oauth->createService(
|
||||
GITHUB_CLIENT_ID,
|
||||
GITHUB_CLIENT_SECRET,
|
||||
$this->helper->url->to('oauth', 'github', array(), '', true),
|
||||
GITHUB_OAUTH_AUTHORIZE_URL,
|
||||
GITHUB_OAUTH_TOKEN_URL,
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Github profile
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getProfile()
|
||||
{
|
||||
$this->getService()->getAccessToken($this->code);
|
||||
|
||||
return $this->httpClient->getJson(
|
||||
GITHUB_API_URL.'user',
|
||||
array($this->getService()->getAuthorizationHeader())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function unlink($userId)
|
||||
{
|
||||
return $this->user->update(array('id' => $userId, 'github_id' => ''));
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Security\OAuthAuthenticationProviderInterface;
|
||||
use Kanboard\User\GitlabUserProvider;
|
||||
|
||||
/**
|
||||
* Gitlab Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class GitlabAuth extends Base implements OAuthAuthenticationProviderInterface
|
||||
{
|
||||
/**
|
||||
* User properties
|
||||
*
|
||||
* @access private
|
||||
* @var \Kanboard\User\GitlabUserProvider
|
||||
*/
|
||||
private $userInfo = null;
|
||||
|
||||
/**
|
||||
* OAuth2 instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Kanboard\Core\Http\OAuth2
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* OAuth2 code
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $code = '';
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Gitlab';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$profile = $this->getProfile();
|
||||
|
||||
if (! empty($profile)) {
|
||||
$this->userInfo = new GitlabUserProvider($profile);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Code
|
||||
*
|
||||
* @access public
|
||||
* @param string $code
|
||||
* @return GitlabAuth
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user object
|
||||
*
|
||||
* @access public
|
||||
* @return GitlabUserProvider
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured OAuth2 service
|
||||
*
|
||||
* @access public
|
||||
* @return \Kanboard\Core\Http\OAuth2
|
||||
*/
|
||||
public function getService()
|
||||
{
|
||||
if (empty($this->service)) {
|
||||
$this->service = $this->oauth->createService(
|
||||
GITLAB_CLIENT_ID,
|
||||
GITLAB_CLIENT_SECRET,
|
||||
$this->helper->url->to('oauth', 'gitlab', array(), '', true),
|
||||
GITLAB_OAUTH_AUTHORIZE_URL,
|
||||
GITLAB_OAUTH_TOKEN_URL,
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Gitlab profile
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getProfile()
|
||||
{
|
||||
$this->getService()->getAccessToken($this->code);
|
||||
|
||||
return $this->httpClient->getJson(
|
||||
GITLAB_API_URL.'user',
|
||||
array($this->getService()->getAuthorizationHeader())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function unlink($userId)
|
||||
{
|
||||
return $this->user->update(array('id' => $userId, 'gitlab_id' => ''));
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Auth;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\Security\OAuthAuthenticationProviderInterface;
|
||||
use Kanboard\User\GoogleUserProvider;
|
||||
|
||||
/**
|
||||
* Google Authentication Provider
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class GoogleAuth extends Base implements OAuthAuthenticationProviderInterface
|
||||
{
|
||||
/**
|
||||
* User properties
|
||||
*
|
||||
* @access protected
|
||||
* @var \Kanboard\User\GoogleUserProvider
|
||||
*/
|
||||
protected $userInfo = null;
|
||||
|
||||
/**
|
||||
* OAuth2 instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Kanboard\Core\Http\OAuth2
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* OAuth2 code
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $code = '';
|
||||
|
||||
/**
|
||||
* Get authentication provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Google';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$profile = $this->getProfile();
|
||||
|
||||
if (! empty($profile)) {
|
||||
$this->userInfo = new GoogleUserProvider($profile);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Code
|
||||
*
|
||||
* @access public
|
||||
* @param string $code
|
||||
* @return GoogleAuth
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user object
|
||||
*
|
||||
* @access public
|
||||
* @return GoogleUserProvider
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured OAuth2 service
|
||||
*
|
||||
* @access public
|
||||
* @return \Kanboard\Core\Http\OAuth2
|
||||
*/
|
||||
public function getService()
|
||||
{
|
||||
if (empty($this->service)) {
|
||||
$this->service = $this->oauth->createService(
|
||||
GOOGLE_CLIENT_ID,
|
||||
GOOGLE_CLIENT_SECRET,
|
||||
$this->helper->url->to('oauth', 'google', array(), '', true),
|
||||
'https://accounts.google.com/o/oauth2/auth',
|
||||
'https://accounts.google.com/o/oauth2/token',
|
||||
array('https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile')
|
||||
);
|
||||
}
|
||||
|
||||
return $this->service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Google profile
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getProfile()
|
||||
{
|
||||
$this->getService()->getAccessToken($this->code);
|
||||
|
||||
return $this->httpClient->getJson(
|
||||
'https://www.googleapis.com/oauth2/v1/userinfo',
|
||||
array($this->getService()->getAuthorizationHeader())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function unlink($userId)
|
||||
{
|
||||
return $this->user->update(array('id' => $userId, 'google_id' => ''));
|
||||
}
|
||||
}
|
|
@ -11,18 +11,19 @@ use Symfony\Component\Console\Command\Command;
|
|||
* @package console
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Kanboard\Model\Notification $notification
|
||||
* @property \Kanboard\Model\Project $project
|
||||
* @property \Kanboard\Model\ProjectPermission $projectPermission
|
||||
* @property \Kanboard\Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
|
||||
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
|
||||
* @property \Kanboard\Model\SubtaskExport $subtaskExport
|
||||
* @property \Kanboard\Model\OverdueNotification $overdueNotification
|
||||
* @property \Kanboard\Model\Task $task
|
||||
* @property \Kanboard\Model\TaskExport $taskExport
|
||||
* @property \Kanboard\Model\TaskFinder $taskFinder
|
||||
* @property \Kanboard\Model\Transition $transition
|
||||
* @property \Kanboard\Model\Notification $notification
|
||||
* @property \Kanboard\Model\Project $project
|
||||
* @property \Kanboard\Model\ProjectPermission $projectPermission
|
||||
* @property \Kanboard\Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
|
||||
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
|
||||
* @property \Kanboard\Model\SubtaskExport $subtaskExport
|
||||
* @property \Kanboard\Model\OverdueNotification $overdueNotification
|
||||
* @property \Kanboard\Model\Task $task
|
||||
* @property \Kanboard\Model\TaskExport $taskExport
|
||||
* @property \Kanboard\Model\TaskFinder $taskFinder
|
||||
* @property \Kanboard\Model\Transition $transition
|
||||
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
|
||||
*/
|
||||
abstract class Base extends Command
|
||||
{
|
||||
|
|
32
sources/app/Console/Cronjob.php
Normal file
32
sources/app/Console/Cronjob.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?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 Cronjob extends Base
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
51
sources/app/Console/TaskTrigger.php
Normal file
51
sources/app/Console/TaskTrigger.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Kanboard\Model\Task;
|
||||
use Kanboard\Event\TaskListEvent;
|
||||
|
||||
class TaskTrigger extends Base
|
||||
{
|
||||
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->taskFinder->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(Task::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(Task::EVENT_DAILY_CRONJOB, $event);
|
||||
}
|
||||
}
|
|
@ -20,14 +20,14 @@ class Action extends Base
|
|||
$project = $this->getProject();
|
||||
$actions = $this->action->getAllByProject($project['id']);
|
||||
|
||||
$this->response->html($this->projectLayout('action/index', array(
|
||||
$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->board->getColumnsList($project['id']),
|
||||
'columns_list' => $this->column->getList($project['id']),
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']),
|
||||
'projects_list' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()),
|
||||
'colors_list' => $this->color->getList(),
|
||||
|
@ -51,7 +51,7 @@ class Action extends Base
|
|||
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('action/event', array(
|
||||
$this->response->html($this->helper->layout->project('action/event', array(
|
||||
'values' => $values,
|
||||
'project' => $project,
|
||||
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
|
||||
|
@ -83,10 +83,10 @@ class Action extends Base
|
|||
$projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
unset($projects_list[$project['id']]);
|
||||
|
||||
$this->response->html($this->projectLayout('action/params', array(
|
||||
$this->response->html($this->helper->layout->project('action/params', array(
|
||||
'values' => $values,
|
||||
'action_params' => $action_params,
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
'columns_list' => $this->column->getList($project['id']),
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']),
|
||||
'projects_list' => $projects_list,
|
||||
'colors_list' => $this->color->getList(),
|
||||
|
@ -138,7 +138,7 @@ class Action extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('action/remove', array(
|
||||
$this->response->html($this->helper->layout->project('action/remove', array(
|
||||
'action' => $this->action->getById($this->request->getIntegerParam('action_id')),
|
||||
'available_events' => $this->eventManager->getAll(),
|
||||
'available_actions' => $this->actionManager->getAvailableActions(),
|
||||
|
|
|
@ -19,8 +19,7 @@ class Activity extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->layout('activity/project', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->response->html($this->helper->layout->app('activity/project', array(
|
||||
'events' => $this->projectActivity->getProject($project['id']),
|
||||
'project' => $project,
|
||||
'title' => t('%s\'s activity', $project['name'])
|
||||
|
@ -36,7 +35,7 @@ class Activity extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('activity/task', array(
|
||||
$this->response->html($this->helper->layout->task('activity/task', array(
|
||||
'title' => $task['title'],
|
||||
'task' => $task,
|
||||
'events' => $this->projectActivity->getTask($task['id']),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Model\Task as TaskModel;
|
||||
|
||||
/**
|
||||
|
@ -11,22 +12,6 @@ use Kanboard\Model\Task as TaskModel;
|
|||
*/
|
||||
class Analytic extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for analytic views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
$params['content_for_sublayout'] = $this->template->render($template, $params);
|
||||
|
||||
return $this->template->layout('analytic/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show average Lead and Cycle time
|
||||
*
|
||||
|
@ -37,7 +22,7 @@ class Analytic extends Base
|
|||
$project = $this->getProject();
|
||||
list($from, $to) = $this->getDates();
|
||||
|
||||
$this->response->html($this->layout('analytic/lead_cycle_time', array(
|
||||
$this->response->html($this->helper->layout->analytic('analytic/lead_cycle_time', array(
|
||||
'values' => array(
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
|
@ -46,7 +31,7 @@ class Analytic extends Base
|
|||
'average' => $this->averageLeadCycleTimeAnalytic->build($project['id']),
|
||||
'metrics' => $this->projectDailyStats->getRawMetrics($project['id'], $from, $to),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
|
||||
'title' => t('Lead and Cycle time for "%s"', $project['name']),
|
||||
)));
|
||||
}
|
||||
|
@ -69,7 +54,7 @@ class Analytic extends Base
|
|||
->setQuery($query)
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->layout('analytic/compare_hours', array(
|
||||
$this->response->html($this->helper->layout->analytic('analytic/compare_hours', array(
|
||||
'project' => $project,
|
||||
'paginator' => $paginator,
|
||||
'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']),
|
||||
|
@ -86,7 +71,7 @@ class Analytic extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->layout('analytic/avg_time_columns', array(
|
||||
$this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array(
|
||||
'project' => $project,
|
||||
'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']),
|
||||
'title' => t('Average time spent into each column for "%s"', $project['name']),
|
||||
|
@ -102,7 +87,7 @@ class Analytic extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->layout('analytic/tasks', array(
|
||||
$this->response->html($this->helper->layout->analytic('analytic/tasks', array(
|
||||
'project' => $project,
|
||||
'metrics' => $this->taskDistributionAnalytic->build($project['id']),
|
||||
'title' => t('Task repartition for "%s"', $project['name']),
|
||||
|
@ -118,7 +103,7 @@ class Analytic extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->layout('analytic/users', array(
|
||||
$this->response->html($this->helper->layout->analytic('analytic/users', array(
|
||||
'project' => $project,
|
||||
'metrics' => $this->userDistributionAnalytic->build($project['id']),
|
||||
'title' => t('User repartition for "%s"', $project['name']),
|
||||
|
@ -160,7 +145,7 @@ class Analytic extends Base
|
|||
|
||||
$display_graph = $this->projectDailyColumnStats->countDays($project['id'], $from, $to) >= 2;
|
||||
|
||||
$this->response->html($this->layout($template, array(
|
||||
$this->response->html($this->helper->layout->analytic($template, array(
|
||||
'values' => array(
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
|
@ -169,7 +154,7 @@ class Analytic extends Base
|
|||
'metrics' => $display_graph ? $this->projectDailyColumnStats->getAggregatedMetrics($project['id'], $from, $to, $column) : array(),
|
||||
'project' => $project,
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
|
||||
'title' => t($title, $project['name']),
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -12,22 +12,6 @@ use Kanboard\Model\Subtask as SubtaskModel;
|
|||
*/
|
||||
class App extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for dashboard views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
$params['content_for_sublayout'] = $this->template->render($template, $params);
|
||||
|
||||
return $this->template->layout('app/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project pagination
|
||||
*
|
||||
|
@ -101,7 +85,7 @@ class App extends Base
|
|||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('app/overview', array(
|
||||
$this->response->html($this->helper->layout->dashboard('app/overview', array(
|
||||
'title' => t('Dashboard'),
|
||||
'project_paginator' => $this->getProjectPaginator($user['id'], 'index', 10),
|
||||
'task_paginator' => $this->getTaskPaginator($user['id'], 'index', 10),
|
||||
|
@ -119,7 +103,7 @@ class App extends Base
|
|||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('app/tasks', array(
|
||||
$this->response->html($this->helper->layout->dashboard('app/tasks', array(
|
||||
'title' => t('My tasks'),
|
||||
'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50),
|
||||
'user' => $user,
|
||||
|
@ -135,7 +119,7 @@ class App extends Base
|
|||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('app/subtasks', array(
|
||||
$this->response->html($this->helper->layout->dashboard('app/subtasks', array(
|
||||
'title' => t('My subtasks'),
|
||||
'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50),
|
||||
'user' => $user,
|
||||
|
@ -151,7 +135,7 @@ class App extends Base
|
|||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('app/projects', array(
|
||||
$this->response->html($this->helper->layout->dashboard('app/projects', array(
|
||||
'title' => t('My projects'),
|
||||
'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25),
|
||||
'user' => $user,
|
||||
|
@ -167,7 +151,7 @@ class App extends Base
|
|||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('app/activity', array(
|
||||
$this->response->html($this->helper->layout->dashboard('app/activity', array(
|
||||
'title' => t('My activity stream'),
|
||||
'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id']), 100),
|
||||
'user' => $user,
|
||||
|
@ -181,7 +165,7 @@ class App extends Base
|
|||
*/
|
||||
public function calendar()
|
||||
{
|
||||
$this->response->html($this->layout('app/calendar', array(
|
||||
$this->response->html($this->helper->layout->dashboard('app/calendar', array(
|
||||
'title' => t('My calendar'),
|
||||
'user' => $this->getUser(),
|
||||
)));
|
||||
|
@ -196,7 +180,7 @@ class App extends Base
|
|||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('app/notifications', array(
|
||||
$this->response->html($this->helper->layout->dashboard('app/notifications', array(
|
||||
'title' => t('My notifications'),
|
||||
'notifications' => $this->userUnreadNotification->getAll($user['id']),
|
||||
'user' => $user,
|
||||
|
|
|
@ -21,7 +21,7 @@ class Auth extends Base
|
|||
$this->response->redirect($this->helper->url->to('app', 'index'));
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('auth/index', array(
|
||||
$this->response->html($this->helper->layout->app('auth/index', array(
|
||||
'captcha' => ! empty($values['username']) && $this->userLocking->hasCaptcha($values['username']),
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
|
@ -55,8 +55,12 @@ class Auth extends Base
|
|||
*/
|
||||
public function logout()
|
||||
{
|
||||
$this->sessionManager->close();
|
||||
$this->response->redirect($this->helper->url->to('auth', 'login'));
|
||||
if (! DISABLE_LOGOUT) {
|
||||
$this->sessionManager->close();
|
||||
$this->response->redirect($this->helper->url->to('auth', 'login'));
|
||||
} else {
|
||||
$this->response->redirect($this->helper->url->to('auth', 'index'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -131,7 +131,7 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
*/
|
||||
protected function notfound($no_layout = false)
|
||||
{
|
||||
$this->response->html($this->template->layout('app/notfound', array(
|
||||
$this->response->html($this->helper->layout->app('app/notfound', array(
|
||||
'title' => t('Page not found'),
|
||||
'no_layout' => $no_layout,
|
||||
)));
|
||||
|
@ -149,7 +149,7 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
$this->response->text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('app/forbidden', array(
|
||||
$this->response->html($this->helper->layout->app('app/forbidden', array(
|
||||
'title' => t('Access Forbidden'),
|
||||
'no_layout' => $no_layout,
|
||||
)));
|
||||
|
@ -179,43 +179,6 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common layout for task views
|
||||
*
|
||||
* @access protected
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
protected function taskLayout($template, array $params)
|
||||
{
|
||||
$content = $this->template->render($template, $params);
|
||||
$params['task_content_for_layout'] = $content;
|
||||
$params['title'] = $params['task']['project_name'].' > '.$params['task']['title'];
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
|
||||
return $this->template->layout('task/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common layout for project views
|
||||
*
|
||||
* @access protected
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
protected function projectLayout($template, array $params, $sidebar_template = 'project/sidebar')
|
||||
{
|
||||
$content = $this->template->render($template, $params);
|
||||
$params['project_content_for_layout'] = $content;
|
||||
$params['title'] = $params['project']['name'] === $params['title'] ? $params['title'] : $params['project']['name'].' > '.$params['title'];
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
$params['sidebar_template'] = $sidebar_template;
|
||||
|
||||
return $this->template->layout('project/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to get a task for task views
|
||||
*
|
||||
|
@ -238,6 +201,36 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
return $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Task or Project file
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function getFile()
|
||||
{
|
||||
$task_id = $this->request->getIntegerParam('task_id');
|
||||
$file_id = $this->request->getIntegerParam('file_id');
|
||||
$model = 'projectFile';
|
||||
|
||||
if ($task_id > 0) {
|
||||
$model = 'taskFile';
|
||||
$project_id = $this->taskFinder->getProjectId($task_id);
|
||||
|
||||
if ($project_id !== $this->request->getIntegerParam('project_id')) {
|
||||
$this->forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->$model->getById($file_id);
|
||||
|
||||
if (empty($file)) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
$file['model'] = $model;
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to get a project
|
||||
*
|
||||
|
@ -248,11 +241,10 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
protected function getProject($project_id = 0)
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id', $project_id);
|
||||
$project = $this->project->getById($project_id);
|
||||
$project = $this->project->getByIdWithOwner($project_id);
|
||||
|
||||
if (empty($project)) {
|
||||
$this->flash->failure(t('Project not found.'));
|
||||
$this->response->redirect($this->helper->url->to('project', 'index'));
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
return $project;
|
||||
|
@ -279,6 +271,23 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current subtask
|
||||
*
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getSubtask()
|
||||
{
|
||||
$subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
|
||||
|
||||
if (empty($subtask)) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
return $subtask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to get project filters
|
||||
*
|
||||
|
@ -308,6 +317,30 @@ abstract class Base extends \Kanboard\Core\Base
|
|||
'board_selector' => $board_selector,
|
||||
'filters' => $filters,
|
||||
'title' => $project['name'],
|
||||
'description' => $this->getProjectDescription($project),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project description
|
||||
*
|
||||
* @access protected
|
||||
* @param array &$project
|
||||
* @return string
|
||||
*/
|
||||
protected function getProjectDescription(array &$project)
|
||||
{
|
||||
if ($project['owner_id'] > 0) {
|
||||
$description = t('Project owner: ').'**'.$this->template->e($project['owner_name'] ?: $project['owner_username']).'**'.PHP_EOL.PHP_EOL;
|
||||
|
||||
if (! empty($project['description'])) {
|
||||
$description .= '***'.PHP_EOL.PHP_EOL;
|
||||
$description .= $project['description'];
|
||||
}
|
||||
} else {
|
||||
$description = $project['description'];
|
||||
}
|
||||
|
||||
return $description;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class Board extends Base
|
|||
}
|
||||
|
||||
// Display the board with a specific layout
|
||||
$this->response->html($this->template->layout('board/view_public', array(
|
||||
$this->response->html($this->helper->layout->app('board/view_public', array(
|
||||
'project' => $project,
|
||||
'swimlanes' => $this->board->getBoard($project['id']),
|
||||
'title' => $project['name'],
|
||||
|
@ -49,12 +49,11 @@ class Board extends Base
|
|||
{
|
||||
$params = $this->getProjectFilters('board', 'show');
|
||||
|
||||
$this->response->html($this->template->layout('board/view_private', array(
|
||||
$this->response->html($this->helper->layout->app('board/view_private', array(
|
||||
'categories_list' => $this->category->getList($params['project']['id'], false),
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
|
||||
'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
|
||||
'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']),
|
||||
'description' => $params['project']['description'],
|
||||
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
|
||||
'board_highlight_period' => $this->config->get('board_highlight_period'),
|
||||
) + $params));
|
||||
|
|
|
@ -93,9 +93,8 @@ class BoardPopover extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->template->render('file/screenshot', array(
|
||||
$this->response->html($this->template->render('task_file/screenshot', array(
|
||||
'task' => $task,
|
||||
'redirect' => 'board',
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -113,7 +112,7 @@ class BoardPopover extends Base
|
|||
$this->response->html($this->template->render('board/popover_close_all_tasks_column', array(
|
||||
'project' => $project,
|
||||
'nb_tasks' => $this->taskFinder->countByColumnAndSwimlaneId($project['id'], $column_id, $swimlane_id),
|
||||
'column' => $this->board->getColumnTitleById($column_id),
|
||||
'column' => $this->column->getColumnTitleById($column_id),
|
||||
'swimlane' => $this->swimlane->getNameById($swimlane_id) ?: t($project['default_swimlane']),
|
||||
'values' => array('column_id' => $column_id, 'swimlane_id' => $swimlane_id),
|
||||
)));
|
||||
|
@ -130,7 +129,7 @@ class BoardPopover extends Base
|
|||
$values = $this->request->getValues();
|
||||
|
||||
$this->taskStatus->closeTasksBySwimlaneAndColumn($values['swimlane_id'], $values['column_id']);
|
||||
$this->flash->success(t('All tasks of the column "%s" and the swimlane "%s" have been closed successfully.', $this->board->getColumnTitleById($values['column_id']), $this->swimlane->getNameById($values['swimlane_id']) ?: t($project['default_swimlane'])));
|
||||
$this->flash->success(t('All tasks of the column "%s" and the swimlane "%s" have been closed successfully.', $this->column->getColumnTitleById($values['column_id']), $this->swimlane->getNameById($values['swimlane_id']) ?: t($project['default_swimlane'])));
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,21 @@ class BoardTooltip extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$this->response->html($this->template->render('board/tooltip_tasklinks', array(
|
||||
'links' => $this->taskLink->getAll($task['id']),
|
||||
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get links on mouseover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function externallinks()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->response->html($this->template->render('board/tooltip_external_links', array(
|
||||
'links' => $this->taskExternalLink->getAll($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
@ -48,7 +62,7 @@ class BoardTooltip extends Base
|
|||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->template->render('board/tooltip_files', array(
|
||||
'files' => $this->file->getAll($task['id']),
|
||||
'files' => $this->taskFile->getAll($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
@ -90,7 +104,7 @@ class BoardTooltip extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->template->render('task/recurring_info', array(
|
||||
$this->response->html($this->template->render('task_recurrence/info', array(
|
||||
'task' => $task,
|
||||
'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
|
||||
'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
|
||||
|
|
|
@ -20,7 +20,7 @@ class Calendar extends Base
|
|||
*/
|
||||
public function show()
|
||||
{
|
||||
$this->response->html($this->template->layout('calendar/show', array(
|
||||
$this->response->html($this->helper->layout->app('calendar/show', array(
|
||||
'check_interval' => $this->config->get('board_private_refresh_interval'),
|
||||
) + $this->getProjectFilters('calendar', 'show')));
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ class Category extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('category/index', array(
|
||||
$this->response->html($this->helper->layout->project('category/index', array(
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'errors' => $errors,
|
||||
|
@ -81,7 +81,7 @@ class Category extends Base
|
|||
$project = $this->getProject();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->projectLayout('category/edit', array(
|
||||
$this->response->html($this->helper->layout->project('category/edit', array(
|
||||
'values' => empty($values) ? $category : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
|
@ -123,7 +123,7 @@ class Category extends Base
|
|||
$project = $this->getProject();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->projectLayout('category/remove', array(
|
||||
$this->response->html($this->helper->layout->project('category/remove', array(
|
||||
'project' => $project,
|
||||
'category' => $category,
|
||||
'title' => t('Remove a category')
|
||||
|
|
|
@ -15,54 +15,61 @@ class Column extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
public function index()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$columns = $this->board->getColumns($project['id']);
|
||||
$columns = $this->column->getAll($project['id']);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$values['title['.$column['id'].']'] = $column['title'];
|
||||
$values['description['.$column['id'].']'] = $column['description'];
|
||||
$values['task_limit['.$column['id'].']'] = $column['task_limit'] ?: null;
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('column/index', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
$this->response->html($this->helper->layout->project('column/index', array(
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'title' => t('Edit board')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show form to create a new column
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
if (empty($values)) {
|
||||
$values = array('project_id' => $project['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->render('column/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'title' => t('Add a new column')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and add a new column
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
public function save()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$columns = $this->board->getColumnsList($project['id']);
|
||||
$data = $this->request->getValues();
|
||||
$values = array();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
foreach ($columns as $column_id => $column_title) {
|
||||
$values['title['.$column_id.']'] = $column_title;
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->columnValidator->validateCreation($data);
|
||||
list($valid, $errors) = $this->columnValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->board->addColumn($project['id'], $data['title'], $data['task_limit'], $data['description'])) {
|
||||
$this->flash->success(t('Board updated successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
|
||||
if ($this->column->create($project['id'], $values['title'], $values['task_limit'], $values['description'])) {
|
||||
$this->flash->success(t('Column created successfully.'));
|
||||
return $this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])), true);
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this board.'));
|
||||
$errors['title'] = array(t('Another column with the same name exists in the project'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->index($values, $errors);
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,9 +80,9 @@ class Column extends Base
|
|||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
|
||||
$column = $this->column->getById($this->request->getIntegerParam('column_id'));
|
||||
|
||||
$this->response->html($this->projectLayout('column/edit', array(
|
||||
$this->response->html($this->helper->layout->project('column/edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values ?: $column,
|
||||
'project' => $project,
|
||||
|
@ -97,7 +104,7 @@ class Column extends Base
|
|||
list($valid, $errors) = $this->columnValidator->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->board->updateColumn($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
|
||||
if ($this->column->update($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
|
||||
$this->flash->success(t('Board updated successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
|
||||
} else {
|
||||
|
@ -109,22 +116,21 @@ class Column extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Move a column up or down
|
||||
* Move column position
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function move()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$column_id = $this->request->getIntegerParam('column_id');
|
||||
$direction = $this->request->getStringParam('direction');
|
||||
$values = $this->request->getJson();
|
||||
|
||||
if ($direction === 'up' || $direction === 'down') {
|
||||
$this->board->{'move'.$direction}($project['id'], $column_id);
|
||||
if (! empty($values) && isset($values['column_id']) && isset($values['position'])) {
|
||||
$result = $this->column->changePosition($project['id'], $values['column_id'], $values['position']);
|
||||
return $this->response->json(array('result' => $result));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,8 +142,8 @@ class Column extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('column/remove', array(
|
||||
'column' => $this->board->getColumn($this->request->getIntegerParam('column_id')),
|
||||
$this->response->html($this->helper->layout->project('column/remove', array(
|
||||
'column' => $this->column->getById($this->request->getIntegerParam('column_id')),
|
||||
'project' => $project,
|
||||
'title' => t('Remove a column from a board')
|
||||
)));
|
||||
|
@ -152,9 +158,9 @@ class Column extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
$this->checkCSRFParam();
|
||||
$column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
|
||||
$column_id = $this->request->getIntegerParam('column_id');
|
||||
|
||||
if (! empty($column) && $this->board->removeColumn($column['id'])) {
|
||||
if ($this->column->remove($column_id)) {
|
||||
$this->flash->success(t('Column removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this column.'));
|
||||
|
|
|
@ -21,13 +21,11 @@ class Comment extends Base
|
|||
$comment = $this->comment->getById($this->request->getIntegerParam('comment_id'));
|
||||
|
||||
if (empty($comment)) {
|
||||
$this->notfound();
|
||||
return $this->notfound();
|
||||
}
|
||||
|
||||
if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
|
||||
$this->response->html($this->template->layout('comment/forbidden', array(
|
||||
'title' => t('Access Forbidden')
|
||||
)));
|
||||
return $this->forbidden();
|
||||
}
|
||||
|
||||
return $comment;
|
||||
|
@ -41,7 +39,6 @@ class Comment extends Base
|
|||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
if (empty($values)) {
|
||||
$values = array(
|
||||
|
@ -50,16 +47,7 @@ class Comment extends Base
|
|||
);
|
||||
}
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->html($this->template->render('comment/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'ajax' => $ajax,
|
||||
)));
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('comment/create', array(
|
||||
$this->response->html($this->helper->layout->task('comment/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
|
@ -76,7 +64,6 @@ class Comment extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
list($valid, $errors) = $this->commentValidator->validateCreation($values);
|
||||
|
||||
|
@ -87,11 +74,7 @@ class Comment extends Base
|
|||
$this->flash->failure(t('Unable to create your comment.'));
|
||||
}
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'));
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
|
@ -107,7 +90,7 @@ class Comment extends Base
|
|||
$task = $this->getTask();
|
||||
$comment = $this->getComment();
|
||||
|
||||
$this->response->html($this->taskLayout('comment/edit', array(
|
||||
$this->response->html($this->helper->layout->task('comment/edit', array(
|
||||
'values' => empty($values) ? $comment : $values,
|
||||
'errors' => $errors,
|
||||
'comment' => $comment,
|
||||
|
@ -124,7 +107,7 @@ class Comment extends Base
|
|||
public function update()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$comment = $this->getComment();
|
||||
$this->getComment();
|
||||
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->commentValidator->validateModification($values);
|
||||
|
@ -136,7 +119,7 @@ class Comment extends Base
|
|||
$this->flash->failure(t('Unable to update your comment.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comment-'.$comment['id']));
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), false);
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
|
@ -152,7 +135,7 @@ class Comment extends Base
|
|||
$task = $this->getTask();
|
||||
$comment = $this->getComment();
|
||||
|
||||
$this->response->html($this->taskLayout('comment/remove', array(
|
||||
$this->response->html($this->helper->layout->task('comment/remove', array(
|
||||
'comment' => $comment,
|
||||
'task' => $task,
|
||||
'title' => t('Remove a comment')
|
||||
|
|
|
@ -10,24 +10,6 @@ namespace Kanboard\Controller;
|
|||
*/
|
||||
class Config extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for config views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
$params['values'] = $this->config->getAll();
|
||||
$params['errors'] = array();
|
||||
$params['config_content_for_layout'] = $this->template->render($template, $params);
|
||||
|
||||
return $this->template->layout('config/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method between pages
|
||||
*
|
||||
|
@ -44,7 +26,12 @@ class Config extends Base
|
|||
$values += array('password_reset' => 0);
|
||||
break;
|
||||
case 'project':
|
||||
$values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0, 'cfd_include_closed_tasks' => 0);
|
||||
$values += array(
|
||||
'subtask_restriction' => 0,
|
||||
'subtask_time_tracking' => 0,
|
||||
'cfd_include_closed_tasks' => 0,
|
||||
'disable_private_project' => 0,
|
||||
);
|
||||
break;
|
||||
case 'integrations':
|
||||
$values += array('integration_gravatar' => 0);
|
||||
|
@ -72,7 +59,7 @@ class Config extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->response->html($this->layout('config/about', array(
|
||||
$this->response->html($this->helper->layout->config('config/about', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'title' => t('Settings').' > '.t('About'),
|
||||
)));
|
||||
|
@ -85,7 +72,7 @@ class Config extends Base
|
|||
*/
|
||||
public function plugins()
|
||||
{
|
||||
$this->response->html($this->layout('config/plugins', array(
|
||||
$this->response->html($this->helper->layout->config('config/plugins', array(
|
||||
'plugins' => $this->pluginLoader->plugins,
|
||||
'title' => t('Settings').' > '.t('Plugins'),
|
||||
)));
|
||||
|
@ -100,10 +87,12 @@ class Config extends Base
|
|||
{
|
||||
$this->common('application');
|
||||
|
||||
$this->response->html($this->layout('config/application', array(
|
||||
$this->response->html($this->helper->layout->config('config/application', array(
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'timezones' => $this->config->getTimezones(),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
|
||||
'datetime_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateTimeFormats()),
|
||||
'time_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getTimeFormats()),
|
||||
'title' => t('Settings').' > '.t('Application settings'),
|
||||
)));
|
||||
}
|
||||
|
@ -117,7 +106,7 @@ class Config extends Base
|
|||
{
|
||||
$this->common('project');
|
||||
|
||||
$this->response->html($this->layout('config/project', array(
|
||||
$this->response->html($this->helper->layout->config('config/project', array(
|
||||
'colors' => $this->color->getList(),
|
||||
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
|
||||
'title' => t('Settings').' > '.t('Project settings'),
|
||||
|
@ -133,7 +122,7 @@ class Config extends Base
|
|||
{
|
||||
$this->common('board');
|
||||
|
||||
$this->response->html($this->layout('config/board', array(
|
||||
$this->response->html($this->helper->layout->config('config/board', array(
|
||||
'title' => t('Settings').' > '.t('Board settings'),
|
||||
)));
|
||||
}
|
||||
|
@ -147,7 +136,7 @@ class Config extends Base
|
|||
{
|
||||
$this->common('calendar');
|
||||
|
||||
$this->response->html($this->layout('config/calendar', array(
|
||||
$this->response->html($this->helper->layout->config('config/calendar', array(
|
||||
'title' => t('Settings').' > '.t('Calendar settings'),
|
||||
)));
|
||||
}
|
||||
|
@ -161,7 +150,7 @@ class Config extends Base
|
|||
{
|
||||
$this->common('integrations');
|
||||
|
||||
$this->response->html($this->layout('config/integrations', array(
|
||||
$this->response->html($this->helper->layout->config('config/integrations', array(
|
||||
'title' => t('Settings').' > '.t('Integrations'),
|
||||
)));
|
||||
}
|
||||
|
@ -175,7 +164,7 @@ class Config extends Base
|
|||
{
|
||||
$this->common('webhook');
|
||||
|
||||
$this->response->html($this->layout('config/webhook', array(
|
||||
$this->response->html($this->helper->layout->config('config/webhook', array(
|
||||
'title' => t('Settings').' > '.t('Webhook settings'),
|
||||
)));
|
||||
}
|
||||
|
@ -187,7 +176,7 @@ class Config extends Base
|
|||
*/
|
||||
public function api()
|
||||
{
|
||||
$this->response->html($this->layout('config/api', array(
|
||||
$this->response->html($this->helper->layout->config('config/api', array(
|
||||
'title' => t('Settings').' > '.t('API'),
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -10,22 +10,6 @@ namespace Kanboard\Controller;
|
|||
*/
|
||||
class Currency extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for config views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
$params['config_content_for_layout'] = $this->template->render($template, $params);
|
||||
|
||||
return $this->template->layout('config/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display all currency rates and form
|
||||
*
|
||||
|
@ -33,7 +17,7 @@ class Currency extends Base
|
|||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->response->html($this->layout('currency/index', array(
|
||||
$this->response->html($this->helper->layout->config('currency/index', array(
|
||||
'config_values' => array('application_currency' => $this->config->get('application_currency')),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
|
|
|
@ -21,7 +21,7 @@ class Customfilter extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('custom_filter/index', array(
|
||||
$this->response->html($this->helper->layout->project('custom_filter/index', array(
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
|
@ -56,6 +56,23 @@ class Customfilter extends Base
|
|||
$this->index($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a custom filter
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
|
||||
|
||||
$this->response->html($this->helper->layout->project('custom_filter/remove', array(
|
||||
'project' => $project,
|
||||
'filter' => $filter,
|
||||
'title' => t('Remove a custom filter')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a custom filter
|
||||
*
|
||||
|
@ -90,7 +107,7 @@ class Customfilter extends Base
|
|||
|
||||
$this->checkPermission($project, $filter);
|
||||
|
||||
$this->response->html($this->projectLayout('custom_filter/edit', array(
|
||||
$this->response->html($this->helper->layout->project('custom_filter/edit', array(
|
||||
'values' => empty($values) ? $filter : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
|
|
|
@ -52,8 +52,6 @@ class Doc extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('doc/show', $this->readFile($filename) + array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
)));
|
||||
$this->response->html($this->helper->layout->app('doc/show', $this->readFile($filename)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class Export extends Base
|
|||
$this->response->csv($data);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('export/'.$action, array(
|
||||
$this->response->html($this->helper->layout->project('export/'.$action, array(
|
||||
'values' => array(
|
||||
'controller' => 'export',
|
||||
'action' => $action,
|
||||
|
@ -37,7 +37,7 @@ class Export extends Base
|
|||
),
|
||||
'errors' => array(),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
|
||||
'project' => $project,
|
||||
'title' => $page_title,
|
||||
), 'export/sidebar'));
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Core\ObjectStorage\ObjectStorageException;
|
||||
|
||||
/**
|
||||
* File controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class File extends Base
|
||||
{
|
||||
/**
|
||||
* Screenshot
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function screenshot()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->request->isPost() && $this->file->uploadScreenshot($task['project_id'], $task['id'], $this->request->getValue('screenshot')) !== false) {
|
||||
$this->flash->success(t('Screenshot uploaded successfully.'));
|
||||
|
||||
if ($this->request->getStringParam('redirect') === 'board') {
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('file/screenshot', array(
|
||||
'task' => $task,
|
||||
'redirect' => 'task',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('file/new', array(
|
||||
'task' => $task,
|
||||
'max_size' => ini_get('upload_max_filesize'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload (save files)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if (! $this->file->uploadFiles($task['project_id'], $task['id'], 'files')) {
|
||||
$this->flash->failure(t('Unable to upload the file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* File download
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
try {
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] != $task['id']) {
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->forceDownload($file['name']);
|
||||
$this->objectStorage->output($file['path']);
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a file (show the content in a popover)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function open()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id']) {
|
||||
$this->response->html($this->template->render('file/open', array(
|
||||
'file' => $file,
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display image
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
try {
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id']) {
|
||||
$this->response->contentType($this->file->getImageMimeType($file['name']));
|
||||
$this->objectStorage->output($file['path']);
|
||||
}
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display image thumbnails
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function thumbnail()
|
||||
{
|
||||
$this->response->contentType('image/jpeg');
|
||||
|
||||
try {
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id']) {
|
||||
$this->objectStorage->output($this->file->getThumbnailPath($file['path']));
|
||||
}
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
|
||||
// Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19
|
||||
$data = $this->objectStorage->get($file['path']);
|
||||
$this->file->generateThumbnailFromData($file['path'], $data);
|
||||
$this->objectStorage->output($this->file->getThumbnailPath($file['path']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id'] && $this->file->remove($file['id'])) {
|
||||
$this->flash->success(t('File removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
$this->response->html($this->taskLayout('file/remove', array(
|
||||
'task' => $task,
|
||||
'file' => $file,
|
||||
)));
|
||||
}
|
||||
}
|
116
sources/app/Controller/FileViewer.php
Normal file
116
sources/app/Controller/FileViewer.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Core\ObjectStorage\ObjectStorageException;
|
||||
|
||||
/**
|
||||
* File Viewer Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class FileViewer extends Base
|
||||
{
|
||||
/**
|
||||
* Get file content from object storage
|
||||
*
|
||||
* @access private
|
||||
* @param array $file
|
||||
* @return string
|
||||
*/
|
||||
private function getFileContent(array $file)
|
||||
{
|
||||
$content = '';
|
||||
|
||||
try {
|
||||
|
||||
if ($file['is_image'] == 0) {
|
||||
$content = $this->objectStorage->get($file['path']);
|
||||
}
|
||||
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show file content in a popover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$file = $this->getFile();
|
||||
$type = $this->helper->file->getPreviewType($file['name']);
|
||||
$params = array('file_id' => $file['id'], 'project_id' => $this->request->getIntegerParam('project_id'));
|
||||
|
||||
if ($file['model'] === 'taskFile') {
|
||||
$params['task_id'] = $file['task_id'];
|
||||
}
|
||||
|
||||
$this->response->html($this->template->render('file_viewer/show', array(
|
||||
'file' => $file,
|
||||
'params' => $params,
|
||||
'type' => $type,
|
||||
'content' => $this->getFileContent($file),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display image
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
try {
|
||||
$file = $this->getFile();
|
||||
$this->response->contentType($this->helper->file->getImageMimeType($file['name']));
|
||||
$this->objectStorage->output($file['path']);
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display image thumbnail
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function thumbnail()
|
||||
{
|
||||
$this->response->contentType('image/jpeg');
|
||||
|
||||
try {
|
||||
$file = $this->getFile();
|
||||
$model = $file['model'];
|
||||
$this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
|
||||
// Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19
|
||||
$data = $this->objectStorage->get($file['path']);
|
||||
$this->$model->generateThumbnailFromData($file['path'], $data);
|
||||
$this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* File download
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
try {
|
||||
$file = $this->getFile();
|
||||
$this->response->forceDownload($file['name']);
|
||||
$this->objectStorage->output($file['path']);
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,10 +23,9 @@ class Gantt extends Base
|
|||
$project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('gantt/projects', array(
|
||||
$this->response->html($this->helper->layout->app('gantt/projects', array(
|
||||
'projects' => $this->projectGanttFormatter->filter($project_ids)->format(),
|
||||
'title' => t('Gantt chart for all projects'),
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -65,7 +64,7 @@ class Gantt extends Base
|
|||
$filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('gantt/project', $params + array(
|
||||
$this->response->html($this->helper->layout->app('gantt/project', $params + array(
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
|
||||
'sorting' => $sorting,
|
||||
'tasks' => $filter->format(),
|
||||
|
@ -102,19 +101,23 @@ class Gantt extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$values = $values + array(
|
||||
'project_id' => $project['id'],
|
||||
'column_id' => $this->column->getFirstColumnId($project['id']),
|
||||
'position' => 1
|
||||
);
|
||||
|
||||
$values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
|
||||
$values = $this->hook->merge('controller:gantt:task:form:default', $values, array('default_values' => $values));
|
||||
|
||||
$this->response->html($this->template->render('gantt/task_creation', array(
|
||||
'project' => $project,
|
||||
'errors' => $errors,
|
||||
'values' => $values + array(
|
||||
'project_id' => $project['id'],
|
||||
'column_id' => $this->board->getFirstColumn($project['id']),
|
||||
'position' => 1
|
||||
),
|
||||
'values' => $values,
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], true, false, true),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($project['id']),
|
||||
'swimlanes_list' => $this->swimlane->getList($project['id'], false, true),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'title' => $project['name'].' > '.t('New task')
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@ class Group extends Base
|
|||
->setQuery($this->group->getQuery())
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->template->layout('group/index', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->response->html($this->helper->layout->app('group/index', array(
|
||||
'title' => t('Groups').' ('.$paginator->getTotal().')',
|
||||
'paginator' => $paginator,
|
||||
)));
|
||||
|
@ -48,8 +47,7 @@ class Group extends Base
|
|||
->setQuery($this->groupMember->getQuery($group_id))
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->template->layout('group/users', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->response->html($this->helper->layout->app('group/users', array(
|
||||
'title' => t('Members of %s', $group['name']).' ('.$paginator->getTotal().')',
|
||||
'paginator' => $paginator,
|
||||
'group' => $group,
|
||||
|
@ -63,8 +61,7 @@ class Group extends Base
|
|||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->response->html($this->template->layout('group/create', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->response->html($this->helper->layout->app('group/create', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'title' => t('New group')
|
||||
|
@ -104,8 +101,7 @@ class Group extends Base
|
|||
$values = $this->group->getById($this->request->getIntegerParam('group_id'));
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('group/edit', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->response->html($this->helper->layout->app('group/edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'title' => t('Edit group')
|
||||
|
@ -148,8 +144,7 @@ class Group extends Base
|
|||
$values['group_id'] = $group_id;
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('group/associate', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->response->html($this->helper->layout->app('group/associate', array(
|
||||
'users' => $this->user->prepareList($this->groupMember->getNotMembers($group_id)),
|
||||
'group' => $group,
|
||||
'errors' => $errors,
|
||||
|
@ -191,7 +186,7 @@ class Group extends Base
|
|||
$group = $this->group->getById($group_id);
|
||||
$user = $this->user->getById($user_id);
|
||||
|
||||
$this->response->html($this->template->layout('group/dissociate', array(
|
||||
$this->response->html($this->helper->layout->app('group/dissociate', array(
|
||||
'group' => $group,
|
||||
'user' => $user,
|
||||
'title' => t('Remove user from group "%s"', $group['name']),
|
||||
|
@ -228,7 +223,7 @@ class Group extends Base
|
|||
$group_id = $this->request->getIntegerParam('group_id');
|
||||
$group = $this->group->getById($group_id);
|
||||
|
||||
$this->response->html($this->template->layout('group/remove', array(
|
||||
$this->response->html($this->helper->layout->app('group/remove', array(
|
||||
'group' => $group,
|
||||
'title' => t('Remove group'),
|
||||
)));
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Model\TaskFilter;
|
||||
use Kanboard\Model\Task as TaskModel;
|
||||
use Eluceo\iCal\Component\Calendar as iCalendar;
|
||||
|
||||
/**
|
||||
|
@ -31,6 +32,7 @@ class Ical extends Base
|
|||
// Common filter
|
||||
$filter = $this->taskFilterICalendarFormatter
|
||||
->create()
|
||||
->filterByStatus(TaskModel::STATUS_OPEN)
|
||||
->filterByOwner($user['id']);
|
||||
|
||||
// Calendar properties
|
||||
|
@ -60,6 +62,7 @@ class Ical extends Base
|
|||
// Common filter
|
||||
$filter = $this->taskFilterICalendarFormatter
|
||||
->create()
|
||||
->filterByStatus(TaskModel::STATUS_OPEN)
|
||||
->filterByProject($project['id']);
|
||||
|
||||
// Calendar properties
|
||||
|
|
|
@ -11,22 +11,6 @@ namespace Kanboard\Controller;
|
|||
*/
|
||||
class Link extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for config views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
$params['config_content_for_layout'] = $this->template->render($template, $params);
|
||||
|
||||
return $this->template->layout('config/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current link
|
||||
*
|
||||
|
@ -51,7 +35,7 @@ class Link extends Base
|
|||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->response->html($this->layout('link/index', array(
|
||||
$this->response->html($this->helper->layout->config('link/index', array(
|
||||
'links' => $this->link->getMergedList(),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
|
@ -91,7 +75,7 @@ class Link extends Base
|
|||
$link = $this->getLink();
|
||||
$link['label'] = t($link['label']);
|
||||
|
||||
$this->response->html($this->layout('link/edit', array(
|
||||
$this->response->html($this->helper->layout->config('link/edit', array(
|
||||
'values' => $values ?: $link,
|
||||
'errors' => $errors,
|
||||
'labels' => $this->link->getList($link['id']),
|
||||
|
@ -131,7 +115,7 @@ class Link extends Base
|
|||
{
|
||||
$link = $this->getLink();
|
||||
|
||||
$this->response->html($this->layout('link/remove', array(
|
||||
$this->response->html($this->helper->layout->config('link/remove', array(
|
||||
'link' => $link,
|
||||
'title' => t('Remove a link')
|
||||
)));
|
||||
|
|
|
@ -30,8 +30,11 @@ class Listing extends Base
|
|||
->setQuery($query)
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->template->layout('listing/show', $params + array(
|
||||
$this->response->html($this->helper->layout->app('listing/show', $params + array(
|
||||
'paginator' => $paginator,
|
||||
'categories_list' => $this->category->getList($params['project']['id'], false),
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
|
||||
'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,36 +10,6 @@ namespace Kanboard\Controller;
|
|||
*/
|
||||
class Oauth extends Base
|
||||
{
|
||||
/**
|
||||
* Link or authenticate a Google account
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function google()
|
||||
{
|
||||
$this->step1('Google');
|
||||
}
|
||||
|
||||
/**
|
||||
* Link or authenticate a Github account
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function github()
|
||||
{
|
||||
$this->step1('Github');
|
||||
}
|
||||
|
||||
/**
|
||||
* Link or authenticate a Gitlab account
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function gitlab()
|
||||
{
|
||||
$this->step1('Gitlab');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink external account
|
||||
*
|
||||
|
@ -65,7 +35,7 @@ class Oauth extends Base
|
|||
* @access private
|
||||
* @param string $provider
|
||||
*/
|
||||
private function step1($provider)
|
||||
protected function step1($provider)
|
||||
{
|
||||
$code = $this->request->getStringParam('code');
|
||||
|
||||
|
@ -79,11 +49,11 @@ class Oauth extends Base
|
|||
/**
|
||||
* Link or authenticate the user
|
||||
*
|
||||
* @access private
|
||||
* @access protected
|
||||
* @param string $provider
|
||||
* @param string $code
|
||||
*/
|
||||
private function step2($provider, $code)
|
||||
protected function step2($provider, $code)
|
||||
{
|
||||
$this->authenticationManager->getProvider($provider)->setCode($code);
|
||||
|
||||
|
@ -97,10 +67,10 @@ class Oauth extends Base
|
|||
/**
|
||||
* Link the account
|
||||
*
|
||||
* @access private
|
||||
* @access protected
|
||||
* @param string $provider
|
||||
*/
|
||||
private function link($provider)
|
||||
protected function link($provider)
|
||||
{
|
||||
$authProvider = $this->authenticationManager->getProvider($provider);
|
||||
|
||||
|
@ -117,15 +87,15 @@ class Oauth extends Base
|
|||
/**
|
||||
* Authenticate the account
|
||||
*
|
||||
* @access private
|
||||
* @access protected
|
||||
* @param string $provider
|
||||
*/
|
||||
private function authenticate($provider)
|
||||
protected function authenticate($provider)
|
||||
{
|
||||
if ($this->authenticationManager->oauthAuthentication($provider)) {
|
||||
$this->response->redirect($this->helper->url->to('app', 'index'));
|
||||
} else {
|
||||
$this->response->html($this->template->layout('auth/index', array(
|
||||
$this->response->html($this->helper->layout->app('auth/index', array(
|
||||
'errors' => array('login' => t('External authentication failed')),
|
||||
'values' => array(),
|
||||
'no_layout' => true,
|
||||
|
|
|
@ -17,7 +17,7 @@ class PasswordReset extends Base
|
|||
{
|
||||
$this->checkActivation();
|
||||
|
||||
$this->response->html($this->template->layout('password_reset/create', array(
|
||||
$this->response->html($this->helper->layout->app('password_reset/create', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'no_layout' => true,
|
||||
|
@ -53,7 +53,7 @@ class PasswordReset extends Base
|
|||
$user_id = $this->passwordReset->getUserIdByToken($token);
|
||||
|
||||
if ($user_id !== false) {
|
||||
$this->response->html($this->template->layout('password_reset/change', array(
|
||||
$this->response->html($this->helper->layout->app('password_reset/change', array(
|
||||
'token' => $token,
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
|
|
|
@ -29,11 +29,10 @@ class Project extends Base
|
|||
->setUrl('project', 'index')
|
||||
->setMax(20)
|
||||
->setOrder('name')
|
||||
->setQuery($this->project->getQueryProjectDetails($project_ids))
|
||||
->setQuery($this->project->getQueryColumnStats($project_ids))
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->template->layout('project/index', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->response->html($this->helper->layout->app('project/index', array(
|
||||
'paginator' => $paginator,
|
||||
'nb_projects' => $nb_projects,
|
||||
'title' => t('Projects').' ('.$nb_projects.')'
|
||||
|
@ -49,7 +48,7 @@ class Project extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project/show', array(
|
||||
$this->response->html($this->helper->layout->project('project/show', array(
|
||||
'project' => $project,
|
||||
'stats' => $this->project->getTaskStats($project['id']),
|
||||
'title' => $project['name'],
|
||||
|
@ -78,7 +77,7 @@ class Project extends Base
|
|||
$this->response->redirect($this->helper->url->to('project', 'share', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/share', array(
|
||||
$this->response->html($this->helper->layout->project('project/share', array(
|
||||
'project' => $project,
|
||||
'title' => t('Public access'),
|
||||
)));
|
||||
|
@ -99,7 +98,7 @@ class Project extends Base
|
|||
$this->response->redirect($this->helper->url->to('project', 'integrations', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/integrations', array(
|
||||
$this->response->html($this->helper->layout->project('project/integrations', array(
|
||||
'project' => $project,
|
||||
'title' => t('Integrations'),
|
||||
'webhook_token' => $this->config->get('webhook_token'),
|
||||
|
@ -124,7 +123,7 @@ class Project extends Base
|
|||
$this->response->redirect($this->helper->url->to('project', 'notifications', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/notifications', array(
|
||||
$this->response->html($this->helper->layout->project('project/notifications', array(
|
||||
'notifications' => $this->projectNotification->readSettings($project['id']),
|
||||
'types' => $this->projectNotificationType->getTypes(),
|
||||
'project' => $project,
|
||||
|
@ -132,57 +131,6 @@ class Project extends Base
|
|||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to edit a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project/edit', array(
|
||||
'values' => empty($values) ? $project : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'title' => t('Edit project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and update a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (isset($values['is_private'])) {
|
||||
if (! $this->helper->user->hasProjectAccess('project', 'create', $project['id'])) {
|
||||
unset($values['is_private']);
|
||||
}
|
||||
} elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) {
|
||||
if ($this->helper->user->hasProjectAccess('project', 'create', $project['id'])) {
|
||||
$values += array('is_private' => 0);
|
||||
}
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->projectValidator->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->project->update($values)) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('project', 'edit', array('project_id' => $project['id'])));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a project
|
||||
*
|
||||
|
@ -204,7 +152,7 @@ class Project extends Base
|
|||
$this->response->redirect($this->helper->url->to('project', 'index'));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/remove', array(
|
||||
$this->response->html($this->helper->layout->project('project/remove', array(
|
||||
'project' => $project,
|
||||
'title' => t('Remove project')
|
||||
)));
|
||||
|
@ -222,17 +170,18 @@ class Project extends Base
|
|||
$project = $this->getProject();
|
||||
|
||||
if ($this->request->getStringParam('duplicate') === 'yes') {
|
||||
$values = array_keys($this->request->getValues());
|
||||
if ($this->projectDuplication->duplicate($project['id'], $values) !== false) {
|
||||
$project_id = $this->projectDuplication->duplicate($project['id'], array_keys($this->request->getValues()), $this->userSession->getId());
|
||||
|
||||
if ($project_id !== false) {
|
||||
$this->flash->success(t('Project cloned successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to clone this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('project', 'index'));
|
||||
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/duplicate', array(
|
||||
$this->response->html($this->helper->layout->project('project/duplicate', array(
|
||||
'project' => $project,
|
||||
'title' => t('Clone this project')
|
||||
)));
|
||||
|
@ -259,7 +208,7 @@ class Project extends Base
|
|||
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/disable', array(
|
||||
$this->response->html($this->helper->layout->project('project/disable', array(
|
||||
'project' => $project,
|
||||
'title' => t('Project activation')
|
||||
)));
|
||||
|
@ -286,62 +235,9 @@ class Project extends Base
|
|||
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/enable', array(
|
||||
$this->response->html($this->helper->layout->project('project/enable', array(
|
||||
'project' => $project,
|
||||
'title' => t('Project activation')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$is_private = isset($values['is_private']) && $values['is_private'] == 1;
|
||||
|
||||
$this->response->html($this->template->layout('project/new', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'is_private' => $is_private,
|
||||
'title' => $is_private ? t('New private project') : t('New project'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a private project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function createPrivate(array $values = array(), array $errors = array())
|
||||
{
|
||||
$values['is_private'] = 1;
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->projectValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
$project_id = $this->project->create($values, $this->userSession->getId(), true);
|
||||
|
||||
if ($project_id > 0) {
|
||||
$this->flash->success(t('Your project have been created successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
|
||||
}
|
||||
|
||||
$this->flash->failure(t('Unable to create your project.'));
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
}
|
||||
|
|
125
sources/app/Controller/ProjectCreation.php
Normal file
125
sources/app/Controller/ProjectCreation.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Project Creation Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectCreation extends Base
|
||||
{
|
||||
/**
|
||||
* Display a form to create a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$is_private = isset($values['is_private']) && $values['is_private'] == 1;
|
||||
$projects_list = array(0 => t('Do not duplicate anything')) + $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
|
||||
$this->response->html($this->helper->layout->app('project_creation/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'is_private' => $is_private,
|
||||
'projects_list' => $projects_list,
|
||||
'title' => $is_private ? t('New private project') : t('New project'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a private project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function createPrivate(array $values = array(), array $errors = array())
|
||||
{
|
||||
$values['is_private'] = 1;
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->projectValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
$project_id = $this->createOrDuplicate($values);
|
||||
|
||||
if ($project_id > 0) {
|
||||
$this->flash->success(t('Your project have been created successfully.'));
|
||||
return $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
|
||||
}
|
||||
|
||||
$this->flash->failure(t('Unable to create your project.'));
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or duplicate a project
|
||||
*
|
||||
* @access private
|
||||
* @param array $values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function createOrDuplicate(array $values)
|
||||
{
|
||||
if (empty($values['src_project_id'])) {
|
||||
return $this->createNewProject($values);
|
||||
}
|
||||
|
||||
return $this->duplicateNewProject($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new project
|
||||
*
|
||||
* @access private
|
||||
* @param array $values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function createNewProject(array $values)
|
||||
{
|
||||
$project = array(
|
||||
'name' => $values['name'],
|
||||
'is_private' => $values['is_private'],
|
||||
);
|
||||
|
||||
return $this->project->create($project, $this->userSession->getId(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creatte from another project
|
||||
*
|
||||
* @access private
|
||||
* @param array $values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function duplicateNewProject(array $values)
|
||||
{
|
||||
$selection = array();
|
||||
|
||||
foreach ($this->projectDuplication->getOptionalSelection() as $item) {
|
||||
if (isset($values[$item]) && $values[$item] == 1) {
|
||||
$selection[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->projectDuplication->duplicate(
|
||||
$values['src_project_id'],
|
||||
$selection,
|
||||
$this->userSession->getId(),
|
||||
$values['name'],
|
||||
$values['is_private'] == 1
|
||||
);
|
||||
}
|
||||
}
|
125
sources/app/Controller/ProjectEdit.php
Normal file
125
sources/app/Controller/ProjectEdit.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Project Edit Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectEdit extends Base
|
||||
{
|
||||
/**
|
||||
* General edition (most common operations)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->renderView('project_edit/general', $values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change start and end dates
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function dates(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->renderView('project_edit/dates', $values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change project description
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function description(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->renderView('project_edit/description', $values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change task priority
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function priority(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->renderView('project_edit/task_priority', $values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and update a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
$redirect = $this->request->getStringParam('redirect', 'edit');
|
||||
|
||||
$values = $this->prepareValues($redirect, $project, $values);
|
||||
list($valid, $errors) = $this->projectValidator->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->project->update($values)) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('ProjectEdit', $redirect, array('project_id' => $project['id'])));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->$redirect($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare form values
|
||||
*
|
||||
* @access private
|
||||
* @param string $redirect
|
||||
* @param array $project
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
private function prepareValues($redirect, array $project, array $values)
|
||||
{
|
||||
if ($redirect === 'edit') {
|
||||
if (isset($values['is_private'])) {
|
||||
if (! $this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) {
|
||||
unset($values['is_private']);
|
||||
}
|
||||
} elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) {
|
||||
if ($this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) {
|
||||
$values += array('is_private' => 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common metthod to render different views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template
|
||||
* @param array $values
|
||||
* @param array $errors
|
||||
*/
|
||||
private function renderView($template, array $values, array $errors)
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->helper->layout->project($template, array(
|
||||
'owners' => $this->projectUserRole->getAssignableUsersList($project['id'], true),
|
||||
'values' => empty($values) ? $project : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'title' => t('Edit project')
|
||||
)));
|
||||
}
|
||||
}
|
79
sources/app/Controller/ProjectFile.php
Normal file
79
sources/app/Controller/ProjectFile.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Project File Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectFile extends Base
|
||||
{
|
||||
/**
|
||||
* File upload form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->render('project_file/create', array(
|
||||
'project' => $project,
|
||||
'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save uploaded files
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
if (! $this->projectFile->uploadFiles($project['id'], $this->request->getFileInfo('files'))) {
|
||||
$this->flash->failure(t('Unable to upload the file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectOverview', 'show', array('project_id' => $project['id'])), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$file = $this->projectFile->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($this->projectFile->remove($file['id'])) {
|
||||
$this->flash->success(t('File removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectOverview', 'show', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$file = $this->projectFile->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
$this->response->html($this->template->render('project_file/remove', array(
|
||||
'project' => $project,
|
||||
'file' => $file,
|
||||
)));
|
||||
}
|
||||
}
|
29
sources/app/Controller/ProjectOverview.php
Normal file
29
sources/app/Controller/ProjectOverview.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Project Overview Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectOverview extends Base
|
||||
{
|
||||
/**
|
||||
* Show project overview
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$params = $this->getProjectFilters('ProjectOverview', 'show');
|
||||
$params['users'] = $this->projectUserRole->getAllUsersGroupedByRole($params['project']['id']);
|
||||
$params['roles'] = $this->role->getProjectRoles();
|
||||
$params['events'] = $this->projectActivity->getProject($params['project']['id'], 10);
|
||||
$params['images'] = $this->projectFile->getAllImages($params['project']['id']);
|
||||
$params['files'] = $this->projectFile->getAllDocuments($params['project']['id']);
|
||||
|
||||
$this->project->getColumnStats($params['project']);
|
||||
|
||||
$this->response->html($this->helper->layout->app('project_overview/show', $params));
|
||||
}
|
||||
}
|
|
@ -12,6 +12,24 @@ use Kanboard\Core\Security\Role;
|
|||
*/
|
||||
class ProjectPermission extends Base
|
||||
{
|
||||
/**
|
||||
* Permissions are only available for team projects
|
||||
*
|
||||
* @access protected
|
||||
* @param integer $project_id Default project id
|
||||
* @return array
|
||||
*/
|
||||
protected function getProject($project_id = 0)
|
||||
{
|
||||
$project = parent::getProject($project_id);
|
||||
|
||||
if ($project['is_private'] == 1) {
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all permissions
|
||||
*
|
||||
|
@ -25,7 +43,7 @@ class ProjectPermission extends Base
|
|||
$values['role'] = Role::PROJECT_MEMBER;
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_permission/index', array(
|
||||
$this->response->html($this->helper->layout->project('project_permission/index', array(
|
||||
'project' => $project,
|
||||
'users' => $this->projectUserRole->getUsers($project['id']),
|
||||
'groups' => $this->projectGroupRole->getGroups($project['id']),
|
||||
|
@ -62,6 +80,7 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function addUser()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) {
|
||||
|
@ -70,7 +89,7 @@ class ProjectPermission extends Base
|
|||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,19 +100,16 @@ class ProjectPermission extends Base
|
|||
public function removeUser()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$user_id = $this->request->getIntegerParam('user_id');
|
||||
|
||||
$values = array(
|
||||
'project_id' => $this->request->getIntegerParam('project_id'),
|
||||
'user_id' => $this->request->getIntegerParam('user_id'),
|
||||
);
|
||||
|
||||
if ($this->projectUserRole->removeUser($values['project_id'], $values['user_id'])) {
|
||||
if ($this->projectUserRole->removeUser($project['id'], $user_id)) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,10 +119,10 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function changeUserRole()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getJson();
|
||||
|
||||
if (! empty($project_id) && ! empty($values) && $this->projectUserRole->changeUserRole($project_id, $values['id'], $values['role'])) {
|
||||
if (! empty($project) && ! empty($values) && $this->projectUserRole->changeUserRole($project['id'], $values['id'], $values['role'])) {
|
||||
$this->response->json(array('status' => 'ok'));
|
||||
} else {
|
||||
$this->response->json(array('status' => 'error'));
|
||||
|
@ -120,19 +136,20 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function addGroup()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (empty($values['group_id']) && ! empty($values['external_id'])) {
|
||||
$values['group_id'] = $this->group->create($values['name'], $values['external_id']);
|
||||
}
|
||||
|
||||
if ($this->projectGroupRole->addGroup($values['project_id'], $values['group_id'], $values['role'])) {
|
||||
if ($this->projectGroupRole->addGroup($project['id'], $values['group_id'], $values['role'])) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,19 +160,16 @@ class ProjectPermission extends Base
|
|||
public function removeGroup()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$group_id = $this->request->getIntegerParam('group_id');
|
||||
|
||||
$values = array(
|
||||
'project_id' => $this->request->getIntegerParam('project_id'),
|
||||
'group_id' => $this->request->getIntegerParam('group_id'),
|
||||
);
|
||||
|
||||
if ($this->projectGroupRole->removeGroup($values['project_id'], $values['group_id'])) {
|
||||
if ($this->projectGroupRole->removeGroup($project['id'], $group_id)) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,10 +179,10 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function changeGroupRole()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getJson();
|
||||
|
||||
if (! empty($project_id) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project_id, $values['id'], $values['role'])) {
|
||||
if (! empty($project) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project['id'], $values['id'], $values['role'])) {
|
||||
$this->response->json(array('status' => 'ok'));
|
||||
} else {
|
||||
$this->response->json(array('status' => 'error'));
|
||||
|
|
|
@ -14,23 +14,6 @@ use Kanboard\Core\Security\Role;
|
|||
*/
|
||||
class Projectuser extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for users overview views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
$params['content_for_sublayout'] = $this->template->render($template, $params);
|
||||
$params['filter'] = array('user_id' => $params['user_id']);
|
||||
|
||||
return $this->template->layout('project_user/layout', $params);
|
||||
}
|
||||
|
||||
private function common()
|
||||
{
|
||||
$user_id = $this->request->getIntegerParam('user_id', UserModel::EVERYBODY_ID);
|
||||
|
@ -41,7 +24,7 @@ class Projectuser extends Base
|
|||
$project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
|
||||
}
|
||||
|
||||
return array($user_id, $project_ids, $this->user->getList(true));
|
||||
return array($user_id, $project_ids, $this->user->getActiveUsersList(true));
|
||||
}
|
||||
|
||||
private function role($role, $action, $title, $title_user)
|
||||
|
@ -50,7 +33,7 @@ class Projectuser extends Base
|
|||
|
||||
$query = $this->projectPermission->getQueryByRole($project_ids, $role)->callback(array($this->project, 'applyColumnStats'));
|
||||
|
||||
if ($user_id !== UserModel::EVERYBODY_ID) {
|
||||
if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) {
|
||||
$query->eq(UserModel::TABLE.'.id', $user_id);
|
||||
$title = t($title_user, $users[$user_id]);
|
||||
}
|
||||
|
@ -62,7 +45,7 @@ class Projectuser extends Base
|
|||
->setQuery($query)
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->layout('project_user/roles', array(
|
||||
$this->response->html($this->helper->layout->projectUser('project_user/roles', array(
|
||||
'paginator' => $paginator,
|
||||
'title' => $title,
|
||||
'user_id' => $user_id,
|
||||
|
@ -76,7 +59,7 @@ class Projectuser extends Base
|
|||
|
||||
$query = $this->taskFinder->getProjectUserOverviewQuery($project_ids, $is_active);
|
||||
|
||||
if ($user_id !== UserModel::EVERYBODY_ID) {
|
||||
if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) {
|
||||
$query->eq(TaskModel::TABLE.'.owner_id', $user_id);
|
||||
$title = t($title_user, $users[$user_id]);
|
||||
}
|
||||
|
@ -88,7 +71,7 @@ class Projectuser extends Base
|
|||
->setQuery($query)
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->layout('project_user/tasks', array(
|
||||
$this->response->html($this->helper->layout->projectUser('project_user/tasks', array(
|
||||
'paginator' => $paginator,
|
||||
'title' => $title,
|
||||
'user_id' => $user_id,
|
||||
|
@ -131,4 +114,17 @@ class Projectuser extends Base
|
|||
{
|
||||
$this->tasks(TaskModel::STATUS_CLOSED, 'closed', t('Closed tasks'), 'Closed tasks assigned to "%s"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Users tooltip
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
return $this->response->html($this->template->render('project_user/tooltip_users', array(
|
||||
'users' => $this->projectUserRole->getAllUsersGroupedByRole($project['id']),
|
||||
'roles' => $this->role->getProjectRoles(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,8 +36,7 @@ class Search extends Base
|
|||
$nb_tasks = $paginator->getTotal();
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('search/index', array(
|
||||
'board_selector' => $projects,
|
||||
$this->response->html($this->helper->layout->app('search/index', array(
|
||||
'values' => array(
|
||||
'search' => $search,
|
||||
'controller' => 'search',
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Model\Subtask as SubtaskModel;
|
||||
|
||||
/**
|
||||
* Subtask controller
|
||||
*
|
||||
|
@ -13,20 +11,19 @@ use Kanboard\Model\Subtask as SubtaskModel;
|
|||
class Subtask extends Base
|
||||
{
|
||||
/**
|
||||
* Get the current subtask
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
* Show list of subtasks
|
||||
*/
|
||||
private function getSubtask()
|
||||
public function show()
|
||||
{
|
||||
$subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
|
||||
$task = $this->getTask();
|
||||
|
||||
if (empty($subtask)) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
return $subtask;
|
||||
$this->response->html($this->helper->layout->task('subtask/show', array(
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
|
||||
'task' => $task,
|
||||
'project' => $this->getProject(),
|
||||
'subtasks' => $this->subtask->getAll($task['id']),
|
||||
'editable' => true,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,7 +42,7 @@ class Subtask extends Base
|
|||
);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('subtask/create', array(
|
||||
$this->response->html($this->helper->layout->task('subtask/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
|
||||
|
@ -73,10 +70,10 @@ class Subtask extends Base
|
|||
}
|
||||
|
||||
if (isset($values['another_subtask']) && $values['another_subtask'] == 1) {
|
||||
$this->response->redirect($this->helper->url->to('subtask', 'create', array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1)));
|
||||
return $this->create(array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true);
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
|
@ -92,7 +89,7 @@ class Subtask extends Base
|
|||
$task = $this->getTask();
|
||||
$subtask = $this->getSubTask();
|
||||
|
||||
$this->response->html($this->taskLayout('subtask/edit', array(
|
||||
$this->response->html($this->helper->layout->task('subtask/edit', array(
|
||||
'values' => empty($values) ? $subtask : $values,
|
||||
'errors' => $errors,
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
|
||||
|
@ -122,7 +119,7 @@ class Subtask extends Base
|
|||
$this->flash->failure(t('Unable to update your sub-task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
|
@ -138,7 +135,7 @@ class Subtask extends Base
|
|||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
|
||||
$this->response->html($this->taskLayout('subtask/remove', array(
|
||||
$this->response->html($this->helper->layout->task('subtask/remove', array(
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
)));
|
||||
|
@ -161,97 +158,7 @@ class Subtask extends Base
|
|||
$this->flash->failure(t('Unable to remove this sub-task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change status to the next status: Toto -> In Progress -> Done
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function toggleStatus()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
$redirect = $this->request->getStringParam('redirect', 'task');
|
||||
|
||||
$this->subtask->toggleStatus($subtask['id']);
|
||||
|
||||
if ($redirect === 'board') {
|
||||
$this->sessionStorage->hasSubtaskInProgress = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
|
||||
|
||||
$this->response->html($this->template->render('board/tooltip_subtasks', array(
|
||||
'subtasks' => $this->subtask->getAll($task['id']),
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
$this->toggleRedirect($task, $redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle subtask restriction (popover)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function subtaskRestriction()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
|
||||
$this->response->html($this->template->render('subtask/restriction_change_status', array(
|
||||
'status_list' => array(
|
||||
SubtaskModel::STATUS_TODO => t('Todo'),
|
||||
SubtaskModel::STATUS_DONE => t('Done'),
|
||||
),
|
||||
'subtask_inprogress' => $this->subtask->getSubtaskInProgress($this->userSession->getId()),
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
'redirect' => $this->request->getStringParam('redirect'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change status of the in progress subtask and the other subtask
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function changeRestrictionStatus()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
// Change status of the previous in progress subtask
|
||||
$this->subtask->update(array(
|
||||
'id' => $values['id'],
|
||||
'status' => $values['status'],
|
||||
));
|
||||
|
||||
// Set the current subtask to in pogress
|
||||
$this->subtask->update(array(
|
||||
'id' => $subtask['id'],
|
||||
'status' => SubtaskModel::STATUS_INPROGRESS,
|
||||
));
|
||||
|
||||
$this->toggleRedirect($task, $values['redirect']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to the right page
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function toggleRedirect(array $task, $redirect)
|
||||
{
|
||||
switch ($redirect) {
|
||||
case 'board':
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
case 'dashboard':
|
||||
$this->response->redirect($this->helper->url->to('app', 'index'));
|
||||
default:
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'subtasks'));
|
||||
}
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,14 +168,15 @@ class Subtask extends Base
|
|||
*/
|
||||
public function movePosition()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$task_id = $this->request->getIntegerParam('task_id');
|
||||
$subtask_id = $this->request->getIntegerParam('subtask_id');
|
||||
$direction = $this->request->getStringParam('direction');
|
||||
$method = $direction === 'up' ? 'moveUp' : 'moveDown';
|
||||
$values = $this->request->getJson();
|
||||
|
||||
$this->subtask->$method($task_id, $subtask_id);
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id), 'subtasks'));
|
||||
if (! empty($values) && $this->helper->user->hasProjectAccess('Subtask', 'movePosition', $project_id)) {
|
||||
$result = $this->subtask->changePosition($task_id, $values['subtask_id'], $values['position']);
|
||||
return $this->response->json(array('result' => $result));
|
||||
}
|
||||
|
||||
$this->forbidden();
|
||||
}
|
||||
}
|
||||
|
|
61
sources/app/Controller/SubtaskRestriction.php
Normal file
61
sources/app/Controller/SubtaskRestriction.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Model\Subtask as SubtaskModel;
|
||||
|
||||
/**
|
||||
* Subtask Restriction
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskRestriction extends Base
|
||||
{
|
||||
/**
|
||||
* Show popup
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function popover()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
|
||||
$this->response->html($this->template->render('subtask_restriction/popover', array(
|
||||
'status_list' => array(
|
||||
SubtaskModel::STATUS_TODO => t('Todo'),
|
||||
SubtaskModel::STATUS_DONE => t('Done'),
|
||||
),
|
||||
'subtask_inprogress' => $this->subtask->getSubtaskInProgress($this->userSession->getId()),
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change status of the in progress subtask and the other subtask
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
// Change status of the previous "in progress" subtask
|
||||
$this->subtask->update(array(
|
||||
'id' => $values['id'],
|
||||
'status' => $values['status'],
|
||||
));
|
||||
|
||||
// Set the current subtask to "in progress"
|
||||
$this->subtask->update(array(
|
||||
'id' => $subtask['id'],
|
||||
'status' => SubtaskModel::STATUS_INPROGRESS,
|
||||
));
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
|
||||
}
|
||||
}
|
72
sources/app/Controller/SubtaskStatus.php
Normal file
72
sources/app/Controller/SubtaskStatus.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Subtask Status
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskStatus extends Base
|
||||
{
|
||||
/**
|
||||
* Change status to the next status: Toto -> In Progress -> Done
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function change()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
|
||||
$status = $this->subtask->toggleStatus($subtask['id']);
|
||||
|
||||
if ($this->request->getIntegerParam('refresh-table') === 0) {
|
||||
$subtask['status'] = $status;
|
||||
$html = $this->helper->subtask->toggleStatus($subtask, $task['project_id']);
|
||||
} else {
|
||||
$html = $this->renderTable($task);
|
||||
}
|
||||
|
||||
$this->response->html($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start/stop timer for subtasks
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function timer()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask_id = $this->request->getIntegerParam('subtask_id');
|
||||
$timer = $this->request->getStringParam('timer');
|
||||
|
||||
if ($timer === 'start') {
|
||||
$this->subtaskTimeTracking->logStartTime($subtask_id, $this->userSession->getId());
|
||||
} elseif ($timer === 'stop') {
|
||||
$this->subtaskTimeTracking->logEndTime($subtask_id, $this->userSession->getId());
|
||||
$this->subtaskTimeTracking->updateTaskTimeTracking($task['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->renderTable($task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render table
|
||||
*
|
||||
* @access private
|
||||
* @param array $task
|
||||
* @return string
|
||||
*/
|
||||
private function renderTable(array $task)
|
||||
{
|
||||
return $this->template->render('subtask/table', array(
|
||||
'task' => $task,
|
||||
'subtasks' => $this->subtask->getAll($task['id']),
|
||||
'editable' => true,
|
||||
'redirect' => 'task',
|
||||
));
|
||||
}
|
||||
}
|
|
@ -36,18 +36,32 @@ class Swimlane extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
public function index()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('swimlane/index', array(
|
||||
$this->response->html($this->helper->layout->project('swimlane/index', array(
|
||||
'default_swimlane' => $this->swimlane->getDefault($project['id']),
|
||||
'active_swimlanes' => $this->swimlane->getAllByStatus($project['id'], SwimlaneModel::ACTIVE),
|
||||
'inactive_swimlanes' => $this->swimlane->getAllByStatus($project['id'], SwimlaneModel::INACTIVE),
|
||||
'project' => $project,
|
||||
'title' => t('Swimlanes')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new swimlane
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->render('swimlane/create', array(
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'title' => t('Swimlanes')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -67,11 +81,28 @@ class Swimlane extends Base
|
|||
$this->flash->success(t('Your swimlane have been created successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to create your swimlane.'));
|
||||
$errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
|
||||
}
|
||||
}
|
||||
|
||||
$this->index($values, $errors);
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit default swimlane (display the form)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function editDefault(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$swimlane = $this->swimlane->getDefault($project['id']);
|
||||
|
||||
$this->response->html($this->helper->layout->project('swimlane/edit_default', array(
|
||||
'values' => empty($values) ? $swimlane : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,23 +110,23 @@ class Swimlane extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function change()
|
||||
public function updateDefault()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$values = $this->request->getValues() + array('show_default_swimlane' => 0);
|
||||
list($valid, ) = $this->swimlaneValidator->validateDefaultModification($values);
|
||||
list($valid, $errors) = $this->swimlaneValidator->validateDefaultModification($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->swimlane->updateDefault($values)) {
|
||||
$this->flash->success(t('The default swimlane have been updated successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
|
||||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])), true);
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this swimlane.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->index();
|
||||
$this->editDefault($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,11 +139,10 @@ class Swimlane extends Base
|
|||
$project = $this->getProject();
|
||||
$swimlane = $this->getSwimlane($project['id']);
|
||||
|
||||
$this->response->html($this->projectLayout('swimlane/edit', array(
|
||||
$this->response->html($this->helper->layout->project('swimlane/edit', array(
|
||||
'values' => empty($values) ? $swimlane : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'title' => t('Swimlanes')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -133,7 +163,7 @@ class Swimlane extends Base
|
|||
$this->flash->success(t('Swimlane updated successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this swimlane.'));
|
||||
$errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,10 +180,9 @@ class Swimlane extends Base
|
|||
$project = $this->getProject();
|
||||
$swimlane = $this->getSwimlane($project['id']);
|
||||
|
||||
$this->response->html($this->projectLayout('swimlane/remove', array(
|
||||
$this->response->html($this->helper->layout->project('swimlane/remove', array(
|
||||
'project' => $project,
|
||||
'swimlane' => $swimlane,
|
||||
'title' => t('Remove a swimlane')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -197,6 +226,25 @@ class Swimlane extends Base
|
|||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable default swimlane
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disableDefault()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
|
||||
if ($this->swimlane->disableDefault($project['id'])) {
|
||||
$this->flash->success(t('Swimlane updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this swimlane.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a swimlane
|
||||
*
|
||||
|
@ -218,32 +266,39 @@ class Swimlane extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Move up a swimlane
|
||||
* Enable default swimlane
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function moveup()
|
||||
public function enableDefault()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
|
||||
|
||||
$this->swimlane->moveUp($project['id'], $swimlane_id);
|
||||
if ($this->swimlane->enableDefault($project['id'])) {
|
||||
$this->flash->success(t('Swimlane updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this swimlane.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Move down a swimlane
|
||||
* Move swimlane position
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function movedown()
|
||||
public function move()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
|
||||
$values = $this->request->getJson();
|
||||
|
||||
$this->swimlane->moveDown($project['id'], $swimlane_id);
|
||||
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
|
||||
if (! empty($values) && isset($values['swimlane_id']) && isset($values['position'])) {
|
||||
$result = $this->swimlane->changePosition($project['id'], $values['swimlane_id'], $values['position']);
|
||||
return $this->response->json(array('result' => $result));
|
||||
}
|
||||
|
||||
$this->forbidden();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,13 +30,13 @@ class Task extends Base
|
|||
$this->notfound(true);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('task/public', array(
|
||||
$this->response->html($this->helper->layout->app('task/public', array(
|
||||
'project' => $project,
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
'subtasks' => $this->subtask->getAll($task['id']),
|
||||
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
|
||||
'task' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'columns_list' => $this->column->getList($task['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'title' => $task['title'],
|
||||
'no_layout' => true,
|
||||
|
@ -62,23 +62,21 @@ class Task extends Base
|
|||
'time_spent' => $task['time_spent'] ?: '',
|
||||
);
|
||||
|
||||
$this->dateParser->format($values, array('date_started'), 'Y-m-d H:i');
|
||||
$values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', 'm/d/Y H:i'));
|
||||
|
||||
$this->response->html($this->taskLayout('task/show', array(
|
||||
$this->response->html($this->helper->layout->task('task/show', array(
|
||||
'project' => $this->project->getById($task['project_id']),
|
||||
'files' => $this->file->getAllDocuments($task['id']),
|
||||
'images' => $this->file->getAllImages($task['id']),
|
||||
'files' => $this->taskFile->getAllDocuments($task['id']),
|
||||
'images' => $this->taskFile->getAllImages($task['id']),
|
||||
'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()),
|
||||
'subtasks' => $subtasks,
|
||||
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
|
||||
'task' => $task,
|
||||
'values' => $values,
|
||||
'link_label_list' => $this->link->getList(0, false),
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'columns_list' => $this->column->getList($task['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id'], true, false, false),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'title' => $task['project_name'].' > '.$task['title'],
|
||||
'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
|
||||
'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
|
||||
|
@ -95,7 +93,7 @@ class Task extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task/analytics', array(
|
||||
$this->response->html($this->helper->layout->task('task/analytics', array(
|
||||
'title' => $task['title'],
|
||||
'task' => $task,
|
||||
'lead_time' => $this->taskAnalytic->getLeadTime($task),
|
||||
|
@ -114,14 +112,14 @@ class Task extends Base
|
|||
$task = $this->getTask();
|
||||
|
||||
$subtask_paginator = $this->paginator
|
||||
->setUrl('task', 'timesheet', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'pagination' => 'subtasks'))
|
||||
->setUrl('task', 'timetracking', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'pagination' => 'subtasks'))
|
||||
->setMax(15)
|
||||
->setOrder('start')
|
||||
->setDirection('DESC')
|
||||
->setQuery($this->subtaskTimeTracking->getTaskQuery($task['id']))
|
||||
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
|
||||
|
||||
$this->response->html($this->taskLayout('task/time_tracking_details', array(
|
||||
$this->response->html($this->helper->layout->task('task/time_tracking_details', array(
|
||||
'task' => $task,
|
||||
'subtask_paginator' => $subtask_paginator,
|
||||
)));
|
||||
|
@ -136,7 +134,7 @@ class Task extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task/transitions', array(
|
||||
$this->response->html($this->helper->layout->task('task/transitions', array(
|
||||
'task' => $task,
|
||||
'transitions' => $this->transition->getAllByTask($task['id']),
|
||||
)));
|
||||
|
@ -167,7 +165,7 @@ class Task extends Base
|
|||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task/remove', array(
|
||||
$this->response->html($this->helper->layout->task('task/remove', array(
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
|
185
sources/app/Controller/TaskExternalLink.php
Normal file
185
sources/app/Controller/TaskExternalLink.php
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
|
||||
|
||||
/**
|
||||
* Task External Link Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskExternalLink extends Base
|
||||
{
|
||||
/**
|
||||
* Creation form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->helper->layout->task('task_external_link/show', array(
|
||||
'links' => $this->taskExternalLink->getAll($task['id']),
|
||||
'task' => $task,
|
||||
'title' => t('List of external links'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* First creation form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function find(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->helper->layout->task('task_external_link/find', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'types' => $this->externalLinkManager->getTypes(),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Second creation form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
try {
|
||||
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
$provider = $this->externalLinkManager->setUserInput($values)->find();
|
||||
$link = $provider->getLink();
|
||||
|
||||
$this->response->html($this->helper->layout->task('task_external_link/create', array(
|
||||
'values' => array(
|
||||
'title' => $link->getTitle(),
|
||||
'url' => $link->getUrl(),
|
||||
'link_type' => $provider->getType(),
|
||||
),
|
||||
'dependencies' => $provider->getDependencies(),
|
||||
'errors' => array(),
|
||||
'task' => $task,
|
||||
)));
|
||||
|
||||
} catch (ExternalLinkProviderNotFound $e) {
|
||||
$errors = array('text' => array(t('Unable to fetch link information.')));
|
||||
$this->find($values, $errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
|
||||
|
||||
if ($valid && $this->taskExternalLink->create($values)) {
|
||||
$this->flash->success(t('Link added successfully.'));
|
||||
return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$link_id = $this->request->getIntegerParam('link_id');
|
||||
|
||||
if ($link_id > 0) {
|
||||
$values = $this->taskExternalLink->getById($link_id);
|
||||
}
|
||||
|
||||
if (empty($values)) {
|
||||
return $this->notfound();
|
||||
}
|
||||
|
||||
$provider = $this->externalLinkManager->getProvider($values['link_type']);
|
||||
|
||||
$this->response->html($this->helper->layout->task('task_external_link/edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'dependencies' => $provider->getDependencies(),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
|
||||
|
||||
if ($valid && $this->taskExternalLink->update($values)) {
|
||||
$this->flash->success(t('Link updated successfully.'));
|
||||
return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$link_id = $this->request->getIntegerParam('link_id');
|
||||
$link = $this->taskExternalLink->getById($link_id);
|
||||
|
||||
if (empty($link)) {
|
||||
return $this->notfound();
|
||||
}
|
||||
|
||||
$this->response->html($this->helper->layout->task('task_external_link/remove', array(
|
||||
'link' => $link,
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->taskExternalLink->remove($this->request->getIntegerParam('link_id'))) {
|
||||
$this->flash->success(t('Link removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this link.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
}
|
98
sources/app/Controller/TaskFile.php
Normal file
98
sources/app/Controller/TaskFile.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Task File Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskFile extends Base
|
||||
{
|
||||
/**
|
||||
* Screenshot
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function screenshot()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->request->isPost() && $this->taskFile->uploadScreenshot($task['id'], $this->request->getValue('screenshot')) !== false) {
|
||||
$this->flash->success(t('Screenshot uploaded successfully.'));
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->render('task_file/screenshot', array(
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->template->render('task_file/create', array(
|
||||
'task' => $task,
|
||||
'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload (save files)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if (! $this->taskFile->uploadFiles($task['id'], $this->request->getFileInfo('files'))) {
|
||||
$this->flash->failure(t('Unable to upload the file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$task = $this->getTask();
|
||||
$file = $this->taskFile->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id'] && $this->taskFile->remove($file['id'])) {
|
||||
$this->flash->success(t('File removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a file
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$file = $this->taskFile->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
$this->response->html($this->template->render('task_file/remove', array(
|
||||
'task' => $task,
|
||||
'file' => $file,
|
||||
)));
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ class TaskImport extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('task_import/step1', array(
|
||||
$this->response->html($this->helper->layout->project('task_import/step1', array(
|
||||
'project' => $project,
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
|
|
61
sources/app/Controller/TaskRecurrence.php
Normal file
61
sources/app/Controller/TaskRecurrence.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Task Recurrence controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskRecurrence extends Base
|
||||
{
|
||||
/**
|
||||
* Edit recurrence form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if (empty($values)) {
|
||||
$values = $task;
|
||||
}
|
||||
|
||||
$this->response->html($this->helper->layout->task('task_recurrence/edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'recurrence_status_list' => $this->task->getRecurrenceStatusList(),
|
||||
'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
|
||||
'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
|
||||
'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update recurrence form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->taskModification->update($values)) {
|
||||
$this->flash->success(t('Task updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
|
||||
}
|
||||
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
}
|
|
@ -18,30 +18,29 @@ class Taskcreation extends Base
|
|||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$method = $this->request->isAjax() ? 'render' : 'layout';
|
||||
$swimlanes_list = $this->swimlane->getList($project['id'], false, true);
|
||||
|
||||
if (empty($values)) {
|
||||
$values = array(
|
||||
'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)),
|
||||
'column_id' => $this->request->getIntegerParam('column_id'),
|
||||
'color_id' => $this->request->getStringParam('color_id', $this->color->getDefaultColor()),
|
||||
'owner_id' => $this->request->getIntegerParam('owner_id'),
|
||||
'another_task' => $this->request->getIntegerParam('another_task'),
|
||||
'color_id' => $this->color->getDefaultColor(),
|
||||
'owner_id' => $this->userSession->getId(),
|
||||
);
|
||||
|
||||
$values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
|
||||
$values = $this->hook->merge('controller:task-creation:form:default', $values, array('default_values' => $values));
|
||||
}
|
||||
|
||||
$this->response->html($this->template->$method('task_creation/form', array(
|
||||
'ajax' => $this->request->isAjax(),
|
||||
$this->response->html($this->template->render('task_creation/form', array(
|
||||
'project' => $project,
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
'columns_list' => $this->column->getList($project['id']),
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], true, false, true),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($project['id']),
|
||||
'swimlanes_list' => $swimlanes_list,
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'title' => $project['name'].' > '.t('New task')
|
||||
)));
|
||||
}
|
||||
|
@ -60,25 +59,26 @@ class Taskcreation extends Base
|
|||
|
||||
if ($valid && $this->taskCreation->create($values)) {
|
||||
$this->flash->success(t('Task created successfully.'));
|
||||
$this->afterSave($project, $values);
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to create your task.'));
|
||||
return $this->afterSave($project, $values);
|
||||
}
|
||||
|
||||
$this->flash->failure(t('Unable to create your task.'));
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
private function afterSave(array $project, array &$values)
|
||||
{
|
||||
if (isset($values['another_task']) && $values['another_task'] == 1) {
|
||||
unset($values['title']);
|
||||
unset($values['description']);
|
||||
|
||||
if (! $this->request->isAjax()) {
|
||||
$this->response->redirect($this->helper->url->to('taskcreation', 'create', $values));
|
||||
}
|
||||
} else {
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])));
|
||||
return $this->create(array(
|
||||
'owner_id' => $values['owner_id'],
|
||||
'color_id' => $values['color_id'],
|
||||
'category_id' => isset($values['category_id']) ? $values['category_id'] : 0,
|
||||
'column_id' => $values['column_id'],
|
||||
'swimlane_id' => isset($values['swimlane_id']) ? $values['swimlane_id'] : 0,
|
||||
'another_task' => 1,
|
||||
));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Model\Project as ProjectModel;
|
||||
|
||||
/**
|
||||
* Task Duplication controller
|
||||
*
|
||||
|
@ -30,11 +28,11 @@ class Taskduplication extends Base
|
|||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task_id)));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to create this task.'));
|
||||
$this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
|
||||
$this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_duplication/duplicate', array(
|
||||
$this->response->html($this->helper->layout->task('task_duplication/duplicate', array(
|
||||
'task' => $task,
|
||||
)));
|
||||
}
|
||||
|
@ -109,7 +107,7 @@ class Taskduplication extends Base
|
|||
private function chooseDestination(array $task, $template)
|
||||
{
|
||||
$values = array();
|
||||
$projects_list = $this->projectUserRole->getProjectsByUser($this->userSession->getId(), array(ProjectModel::ACTIVE));
|
||||
$projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
|
||||
unset($projects_list[$task['project_id']]);
|
||||
|
||||
|
@ -117,7 +115,7 @@ class Taskduplication extends Base
|
|||
$dst_project_id = $this->request->getIntegerParam('dst_project_id', key($projects_list));
|
||||
|
||||
$swimlanes_list = $this->swimlane->getList($dst_project_id, false, true);
|
||||
$columns_list = $this->board->getColumnsList($dst_project_id);
|
||||
$columns_list = $this->column->getList($dst_project_id);
|
||||
$categories_list = $this->category->getList($dst_project_id);
|
||||
$users_list = $this->projectUserRole->getAssignableUsersList($dst_project_id);
|
||||
|
||||
|
@ -130,7 +128,7 @@ class Taskduplication extends Base
|
|||
$users_list = array();
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout($template, array(
|
||||
$this->response->html($this->helper->layout->task($template, array(
|
||||
'values' => $values,
|
||||
'task' => $task,
|
||||
'projects_list' => $projects_list,
|
||||
|
|
|
@ -22,12 +22,31 @@ class Tasklink extends Base
|
|||
$link = $this->taskLink->getById($this->request->getIntegerParam('link_id'));
|
||||
|
||||
if (empty($link)) {
|
||||
$this->notfound();
|
||||
return $this->notfound();
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show links
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$project = $this->project->getById($task['project_id']);
|
||||
|
||||
$this->response->html($this->helper->layout->task('tasklink/show', array(
|
||||
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
|
||||
'task' => $task,
|
||||
'project' => $project,
|
||||
'editable' => true,
|
||||
'is_public' => false,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creation form
|
||||
*
|
||||
|
@ -36,20 +55,8 @@ class Tasklink extends Base
|
|||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
if ($ajax && empty($errors)) {
|
||||
$this->response->html($this->template->render('tasklink/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'labels' => $this->link->getList(0, false),
|
||||
'title' => t('Add a new link'),
|
||||
'ajax' => $ajax,
|
||||
)));
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('tasklink/create', array(
|
||||
$this->response->html($this->helper->layout->task('tasklink/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
|
@ -67,19 +74,13 @@ class Tasklink extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
list($valid, $errors) = $this->taskLinkValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
|
||||
$this->flash->success(t('Link added successfully.'));
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links', true);
|
||||
}
|
||||
|
||||
$errors = array('title' => array(t('The exact same link already exists')));
|
||||
|
@ -105,7 +106,7 @@ class Tasklink extends Base
|
|||
$values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title'];
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('tasklink/edit', array(
|
||||
$this->response->html($this->helper->layout->task('tasklink/edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task_link' => $task_link,
|
||||
|
@ -130,7 +131,7 @@ class Tasklink extends Base
|
|||
if ($valid) {
|
||||
if ($this->taskLink->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
|
||||
$this->flash->success(t('Link updated successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
|
||||
}
|
||||
|
||||
$this->flash->failure(t('Unable to update your link.'));
|
||||
|
@ -149,7 +150,7 @@ class Tasklink extends Base
|
|||
$task = $this->getTask();
|
||||
$link = $this->getTaskLink();
|
||||
|
||||
$this->response->html($this->taskLayout('tasklink/remove', array(
|
||||
$this->response->html($this->helper->layout->task('tasklink/remove', array(
|
||||
'link' => $link,
|
||||
'task' => $task,
|
||||
)));
|
||||
|
|
|
@ -22,72 +22,49 @@ class Taskmodification extends Base
|
|||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update time tracking information
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function time()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, ) = $this->taskValidator->validateTimeModification($values);
|
||||
|
||||
if ($valid && $this->taskModification->update($values)) {
|
||||
$this->flash->success(t('Task updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit description form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function description()
|
||||
public function description(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->taskModification->update($values)) {
|
||||
$this->flash->success(t('Task updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
} else {
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
if (empty($values)) {
|
||||
$values = array('id' => $task['id'], 'description' => $task['description']);
|
||||
}
|
||||
|
||||
$params = array(
|
||||
$this->response->html($this->helper->layout->task('task_modification/edit_description', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'ajax' => $ajax,
|
||||
);
|
||||
)));
|
||||
}
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->html($this->template->render('task_modification/edit_description', $params));
|
||||
} else {
|
||||
$this->response->html($this->taskLayout('task_modification/edit_description', $params));
|
||||
/**
|
||||
* Update description
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function updateDescription()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->taskModification->update($values)) {
|
||||
$this->flash->success(t('Task updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
|
||||
}
|
||||
|
||||
$this->description($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,33 +75,26 @@ class Taskmodification extends Base
|
|||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$ajax = $this->request->isAjax();
|
||||
$project = $this->project->getById($task['project_id']);
|
||||
|
||||
if (empty($values)) {
|
||||
$values = $task;
|
||||
$values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
|
||||
$values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values));
|
||||
}
|
||||
|
||||
$this->dateParser->format($values, array('date_due'));
|
||||
$values = $this->dateParser->format($values, array('date_due'), $this->config->get('application_date_format', 'm/d/Y'));
|
||||
$values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', 'm/d/Y H:i'));
|
||||
|
||||
$params = array(
|
||||
$this->response->html($this->helper->layout->task('task_modification/edit_task', array(
|
||||
'project' => $project,
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($task['project_id']),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'ajax' => $ajax,
|
||||
);
|
||||
|
||||
if ($ajax) {
|
||||
$html = $this->template->render('task_modification/edit_task', $params);
|
||||
} else {
|
||||
$html = $this->taskLayout('task_modification/edit_task', $params);
|
||||
}
|
||||
|
||||
$this->response->html($html);
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,56 +111,10 @@ class Taskmodification extends Base
|
|||
|
||||
if ($valid && $this->taskModification->update($values)) {
|
||||
$this->flash->success(t('Task updated successfully.'));
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
} else {
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
|
||||
}
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update your task.'));
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit recurrence form
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function recurrence()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->taskModification->update($values)) {
|
||||
$this->flash->success(t('Task updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
|
||||
}
|
||||
} else {
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'recurrence_status_list' => $this->task->getRecurrenceStatusList(),
|
||||
'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
|
||||
'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
|
||||
'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(),
|
||||
);
|
||||
|
||||
$this->response->html($this->taskLayout('task_modification/edit_recurrence', $params));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,7 @@ class Taskstatus extends Base
|
|||
*/
|
||||
public function close()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->changeStatus($task, 'close', t('Task closed successfully.'), t('Unable to close this task.'));
|
||||
$this->renderTemplate($task, 'task_status/close');
|
||||
$this->changeStatus('close', 'task_status/close', t('Task closed successfully.'), t('Unable to close this task.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,13 +27,22 @@ class Taskstatus extends Base
|
|||
*/
|
||||
public function open()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->changeStatus($task, 'open', t('Task opened successfully.'), t('Unable to open this task.'));
|
||||
$this->renderTemplate($task, 'task_status/open');
|
||||
$this->changeStatus('open', 'task_status/open', t('Task opened successfully.'), t('Unable to open this task.'));
|
||||
}
|
||||
|
||||
private function changeStatus(array $task, $method, $success_message, $failure_message)
|
||||
/**
|
||||
* Common method to change status
|
||||
*
|
||||
* @access private
|
||||
* @param string $method
|
||||
* @param string $template
|
||||
* @param string $success_message
|
||||
* @param string $failure_message
|
||||
*/
|
||||
private function changeStatus($method, $template, $success_message, $failure_message)
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
$this->checkCSRFParam();
|
||||
|
||||
|
@ -45,28 +52,11 @@ class Taskstatus extends Base
|
|||
$this->flash->failure($failure_message);
|
||||
}
|
||||
|
||||
if ($this->request->getStringParam('redirect') === 'board') {
|
||||
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
|
||||
}
|
||||
}
|
||||
|
||||
private function renderTemplate(array $task, $template)
|
||||
{
|
||||
$redirect = $this->request->getStringParam('redirect');
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->html($this->template->render($template, array(
|
||||
'task' => $task,
|
||||
'redirect' => $redirect,
|
||||
)));
|
||||
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout($template, array(
|
||||
$this->response->html($this->helper->layout->task($template, array(
|
||||
'task' => $task,
|
||||
'redirect' => $redirect,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Time Tracking controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Timer extends Base
|
||||
{
|
||||
/**
|
||||
* Start/stop timer for subtasks
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function subtask()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$task_id = $this->request->getIntegerParam('task_id');
|
||||
$subtask_id = $this->request->getIntegerParam('subtask_id');
|
||||
$timer = $this->request->getStringParam('timer');
|
||||
|
||||
if ($timer === 'start') {
|
||||
$this->subtaskTimeTracking->logStartTime($subtask_id, $this->userSession->getId());
|
||||
} elseif ($timer === 'stop') {
|
||||
$this->subtaskTimeTracking->logEndTime($subtask_id, $this->userSession->getId());
|
||||
$this->subtaskTimeTracking->updateTaskTimeTracking($task_id);
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks');
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ class Twofactor extends User
|
|||
$this->checkCurrentUser($user);
|
||||
unset($this->sessionStorage->twoFactorSecret);
|
||||
|
||||
$this->response->html($this->layout('twofactor/index', array(
|
||||
$this->response->html($this->helper->layout->user('twofactor/index', array(
|
||||
'user' => $user,
|
||||
'provider' => $this->authenticationManager->getPostAuthenticationProvider()->getName(),
|
||||
)));
|
||||
|
@ -60,7 +60,7 @@ class Twofactor extends User
|
|||
$provider->setSecret($this->sessionStorage->twoFactorSecret);
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('twofactor/show', array(
|
||||
$this->response->html($this->helper->layout->user('twofactor/show', array(
|
||||
'user' => $user,
|
||||
'secret' => $this->sessionStorage->twoFactorSecret,
|
||||
'qrcode_url' => $provider->getQrCodeUrl($label),
|
||||
|
@ -165,7 +165,7 @@ class Twofactor extends User
|
|||
$this->sessionStorage->twoFactorBeforeCodeCalled = true;
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('twofactor/check', array(
|
||||
$this->response->html($this->helper->layout->app('twofactor/check', array(
|
||||
'title' => t('Check two factor authentication code'),
|
||||
)));
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ class Twofactor extends User
|
|||
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('twofactor/disable', array(
|
||||
$this->response->html($this->helper->layout->user('twofactor/disable', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -14,27 +14,6 @@ use Kanboard\Core\Security\Role;
|
|||
*/
|
||||
class User extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for user views
|
||||
*
|
||||
* @access protected
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
protected function layout($template, array $params)
|
||||
{
|
||||
$content = $this->template->render($template, $params);
|
||||
$params['user_content_for_layout'] = $content;
|
||||
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
|
||||
if (isset($params['user'])) {
|
||||
$params['title'] = ($params['user']['name'] ?: $params['user']['username']).' (#'.$params['user']['id'].')';
|
||||
}
|
||||
|
||||
return $this->template->layout('user/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all users
|
||||
*
|
||||
|
@ -50,11 +29,11 @@ class User extends Base
|
|||
->calculate();
|
||||
|
||||
$this->response->html(
|
||||
$this->template->layout('user/index', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->helper->layout->app('user/index', array(
|
||||
'title' => t('Users').' ('.$paginator->getTotal().')',
|
||||
'paginator' => $paginator,
|
||||
)));
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,8 +50,7 @@ class User extends Base
|
|||
}
|
||||
|
||||
$this->response->html(
|
||||
$this->template->layout('user/profile', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
$this->helper->layout->app('user/profile', array(
|
||||
'title' => $user['name'] ?: $user['username'],
|
||||
'user' => $user,
|
||||
)
|
||||
|
@ -88,11 +66,10 @@ class User extends Base
|
|||
{
|
||||
$is_remote = $this->request->getIntegerParam('remote') == 1 || (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1);
|
||||
|
||||
$this->response->html($this->template->layout($is_remote ? 'user/create_remote' : 'user/create_local', array(
|
||||
$this->response->html($this->helper->layout->app($is_remote ? 'user/create_remote' : 'user/create_local', array(
|
||||
'timezones' => $this->config->getTimezones(true),
|
||||
'languages' => $this->config->getLanguages(true),
|
||||
'roles' => $this->role->getApplicationRoles(),
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
'projects' => $this->project->getList(),
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('role' => Role::APP_USER),
|
||||
|
@ -142,7 +119,7 @@ class User extends Base
|
|||
public function show()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user/show', array(
|
||||
$this->response->html($this->helper->layout->user('user/show', array(
|
||||
'user' => $user,
|
||||
'timezones' => $this->config->getTimezones(true),
|
||||
'languages' => $this->config->getLanguages(true),
|
||||
|
@ -166,7 +143,7 @@ class User extends Base
|
|||
->setQuery($this->subtaskTimeTracking->getUserQuery($user['id']))
|
||||
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
|
||||
|
||||
$this->response->html($this->layout('user/timesheet', array(
|
||||
$this->response->html($this->helper->layout->user('user/timesheet', array(
|
||||
'subtask_paginator' => $subtask_paginator,
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -180,7 +157,7 @@ class User extends Base
|
|||
public function passwordReset()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user/password_reset', array(
|
||||
$this->response->html($this->helper->layout->user('user/password_reset', array(
|
||||
'tokens' => $this->passwordReset->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -194,7 +171,7 @@ class User extends Base
|
|||
public function last()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user/last', array(
|
||||
$this->response->html($this->helper->layout->user('user/last', array(
|
||||
'last_logins' => $this->lastLogin->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -208,7 +185,7 @@ class User extends Base
|
|||
public function sessions()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user/sessions', array(
|
||||
$this->response->html($this->helper->layout->user('user/sessions', array(
|
||||
'sessions' => $this->rememberMeSession->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -243,7 +220,7 @@ class User extends Base
|
|||
$this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user/notifications', array(
|
||||
$this->response->html($this->helper->layout->user('user/notifications', array(
|
||||
'projects' => $this->projectUserRole->getProjectsByUser($user['id'], array(ProjectModel::ACTIVE)),
|
||||
'notifications' => $this->userNotification->readSettings($user['id']),
|
||||
'types' => $this->userNotificationType->getTypes(),
|
||||
|
@ -268,7 +245,7 @@ class User extends Base
|
|||
$this->response->redirect($this->helper->url->to('user', 'integrations', array('user_id' => $user['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user/integrations', array(
|
||||
$this->response->html($this->helper->layout->user('user/integrations', array(
|
||||
'user' => $user,
|
||||
'values' => $this->userMetadata->getall($user['id']),
|
||||
)));
|
||||
|
@ -282,7 +259,7 @@ class User extends Base
|
|||
public function external()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user/external', array(
|
||||
$this->response->html($this->helper->layout->user('user/external', array(
|
||||
'last_logins' => $this->lastLogin->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -310,7 +287,7 @@ class User extends Base
|
|||
$this->response->redirect($this->helper->url->to('user', 'share', array('user_id' => $user['id'])));
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user/share', array(
|
||||
$this->response->html($this->helper->layout->user('user/share', array(
|
||||
'user' => $user,
|
||||
'title' => t('Public access'),
|
||||
)));
|
||||
|
@ -342,7 +319,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user/password', array(
|
||||
$this->response->html($this->helper->layout->user('user/password', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'user' => $user,
|
||||
|
@ -384,7 +361,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user/edit', array(
|
||||
$this->response->html($this->helper->layout->user('user/edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'user' => $user,
|
||||
|
@ -422,36 +399,10 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user/authentication', array(
|
||||
$this->response->html($this->helper->layout->user('user/authentication', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->user->remove($user['id'])) {
|
||||
$this->flash->success(t('User removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this user.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('user', 'index'));
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user/remove', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class UserImport extends Base
|
|||
*/
|
||||
public function step1(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->response->html($this->template->layout('user_import/step1', array(
|
||||
$this->response->html($this->helper->layout->app('user_import/step1', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'max_size' => ini_get('upload_max_filesize'),
|
||||
|
|
111
sources/app/Controller/UserStatus.php
Normal file
111
sources/app/Controller/UserStatus.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* User Status Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserStatus extends Base
|
||||
{
|
||||
/**
|
||||
* Confirm remove a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmRemove()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->helper->layout->user('user_status/remove', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->user->remove($user['id'])) {
|
||||
$this->flash->success(t('User removed successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to remove this user.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('user', 'index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm enable a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmEnable()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->helper->layout->user('user_status/enable', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enable()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->user->enable($user['id'])) {
|
||||
$this->flash->success(t('User activated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to enable this user.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('user', 'index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm disable a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmDisable()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->helper->layout->user('user_status/disable', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disable()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->user->disable($user['id'])) {
|
||||
$this->flash->success(t('User disabled successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to disable this user.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('user', 'index'));
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ use Pimple\Container;
|
|||
* @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic
|
||||
* @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic
|
||||
* @property \Kanboard\Core\Action\ActionManager $actionManager
|
||||
* @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager
|
||||
* @property \Kanboard\Core\Cache\MemoryCache $memoryCache
|
||||
* @property \Kanboard\Core\Event\EventManager $eventManager
|
||||
* @property \Kanboard\Core\Group\GroupManager $groupManager
|
||||
|
@ -62,11 +63,13 @@ use Pimple\Container;
|
|||
* @property \Kanboard\Model\Board $board
|
||||
* @property \Kanboard\Model\Category $category
|
||||
* @property \Kanboard\Model\Color $color
|
||||
* @property \Kanboard\Model\Column $column
|
||||
* @property \Kanboard\Model\Comment $comment
|
||||
* @property \Kanboard\Model\Config $config
|
||||
* @property \Kanboard\Model\Currency $currency
|
||||
* @property \Kanboard\Model\CustomFilter $customFilter
|
||||
* @property \Kanboard\Model\File $file
|
||||
* @property \Kanboard\Model\TaskFile $taskFile
|
||||
* @property \Kanboard\Model\ProjectFile $projectFile
|
||||
* @property \Kanboard\Model\Group $group
|
||||
* @property \Kanboard\Model\GroupMember $groupMember
|
||||
* @property \Kanboard\Model\LastLogin $lastLogin
|
||||
|
@ -97,6 +100,7 @@ use Pimple\Container;
|
|||
* @property \Kanboard\Model\TaskCreation $taskCreation
|
||||
* @property \Kanboard\Model\TaskDuplication $taskDuplication
|
||||
* @property \Kanboard\Model\TaskExport $taskExport
|
||||
* @property \Kanboard\Model\TaskExternalLink $taskExternalLink
|
||||
* @property \Kanboard\Model\TaskImport $taskImport
|
||||
* @property \Kanboard\Model\TaskFinder $taskFinder
|
||||
* @property \Kanboard\Model\TaskFilter $taskFilter
|
||||
|
@ -132,6 +136,7 @@ use Pimple\Container;
|
|||
* @property \Kanboard\Validator\SubtaskValidator $subtaskValidator
|
||||
* @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator
|
||||
* @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator
|
||||
* @property \Kanboard\Validator\TaskExternalLinkValidator $taskExternalLinkValidator
|
||||
* @property \Kanboard\Validator\TaskValidator $taskValidator
|
||||
* @property \Kanboard\Validator\UserValidator $userValidator
|
||||
* @property \Psr\Log\LoggerInterface $logger
|
||||
|
|
|
@ -12,6 +12,141 @@ use DateTime;
|
|||
*/
|
||||
class DateParser extends Base
|
||||
{
|
||||
/**
|
||||
* List of time formats
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTimeFormats()
|
||||
{
|
||||
return array(
|
||||
'H:i',
|
||||
'g:i a',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of date formats
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $iso
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDateFormats($iso = false)
|
||||
{
|
||||
$iso_formats = array(
|
||||
'Y-m-d',
|
||||
'Y_m_d',
|
||||
);
|
||||
|
||||
$user_formats = array(
|
||||
'm/d/Y',
|
||||
'd/m/Y',
|
||||
'Y/m/d',
|
||||
'd.m.Y',
|
||||
);
|
||||
|
||||
return $iso ? array_merge($iso_formats, $user_formats) : $user_formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of datetime formats
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $iso
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDateTimeFormats($iso = false)
|
||||
{
|
||||
$formats = array();
|
||||
|
||||
foreach ($this->getDateFormats($iso) as $date) {
|
||||
foreach ($this->getTimeFormats() as $time) {
|
||||
$formats[] = $date.' '.$time;
|
||||
}
|
||||
}
|
||||
|
||||
return $formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of all date formats
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $iso
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllDateFormats($iso = false)
|
||||
{
|
||||
return array_merge($this->getDateFormats($iso), $this->getDateTimeFormats($iso));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available formats (visible in settings)
|
||||
*
|
||||
* @access public
|
||||
* @param array $formats
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableFormats(array $formats)
|
||||
{
|
||||
$values = array();
|
||||
|
||||
foreach ($formats as $format) {
|
||||
$values[$format] = date($format);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a date and return a unix timestamp, try different date formats
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Date to parse
|
||||
* @return integer
|
||||
*/
|
||||
public function getTimestamp($value)
|
||||
{
|
||||
if (ctype_digit($value)) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
foreach ($this->getAllDateFormats(true) as $format) {
|
||||
$timestamp = $this->getValidDate($value, $format);
|
||||
|
||||
if ($timestamp !== 0) {
|
||||
return $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a timestamp if the given date format is correct otherwise return 0
|
||||
*
|
||||
* @access private
|
||||
* @param string $value Date to parse
|
||||
* @param string $format Date format
|
||||
* @return integer
|
||||
*/
|
||||
private function getValidDate($value, $format)
|
||||
{
|
||||
$date = DateTime::createFromFormat($format, $value);
|
||||
|
||||
if ($date !== false) {
|
||||
$errors = DateTime::getLastErrors();
|
||||
if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
|
||||
$timestamp = $date->getTimestamp();
|
||||
return $timestamp > 0 ? $timestamp : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the date is within the date range
|
||||
*
|
||||
|
@ -54,50 +189,7 @@ class DateParser extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a timestamp if the given date format is correct otherwise return 0
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Date to parse
|
||||
* @param string $format Date format
|
||||
* @return integer
|
||||
*/
|
||||
public function getValidDate($value, $format)
|
||||
{
|
||||
$date = DateTime::createFromFormat($format, $value);
|
||||
|
||||
if ($date !== false) {
|
||||
$errors = DateTime::getLastErrors();
|
||||
if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
|
||||
$timestamp = $date->getTimestamp();
|
||||
return $timestamp > 0 ? $timestamp : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a date and return a unix timestamp, try different date formats
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Date to parse
|
||||
* @return integer
|
||||
*/
|
||||
public function getTimestamp($value)
|
||||
{
|
||||
foreach ($this->getAllFormats() as $format) {
|
||||
$timestamp = $this->getValidDate($value, $format);
|
||||
|
||||
if ($timestamp !== 0) {
|
||||
return $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ISO8601 date from user input
|
||||
* Get ISO-8601 date from user input
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Date to parse
|
||||
|
@ -105,96 +197,31 @@ class DateParser extends Base
|
|||
*/
|
||||
public function getIsoDate($value)
|
||||
{
|
||||
return date('Y-m-d', ctype_digit($value) ? $value : $this->getTimestamp($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all combinations of date/time formats
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllFormats()
|
||||
{
|
||||
$formats = array();
|
||||
|
||||
foreach ($this->getDateFormats() as $date) {
|
||||
foreach ($this->getTimeFormats() as $time) {
|
||||
$formats[] = $date.' '.$time;
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($formats, $this->getDateFormats());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of supported date formats (for the parser)
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDateFormats()
|
||||
{
|
||||
return array(
|
||||
$this->config->get('application_date_format', 'm/d/Y'),
|
||||
'Y-m-d',
|
||||
'Y_m_d',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of supported time formats (for the parser)
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTimeFormats()
|
||||
{
|
||||
return array(
|
||||
'H:i',
|
||||
'g:i A',
|
||||
'g:iA',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of available date formats (for the config page)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableFormats()
|
||||
{
|
||||
return array(
|
||||
'm/d/Y' => date('m/d/Y'),
|
||||
'd/m/Y' => date('d/m/Y'),
|
||||
'Y/m/d' => date('Y/m/d'),
|
||||
'd.m.Y' => date('d.m.Y'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the time from a timestamp
|
||||
*
|
||||
* @access public
|
||||
* @param integer $timestamp Timestamp
|
||||
* @return integer
|
||||
*/
|
||||
public function removeTimeFromTimestamp($timestamp)
|
||||
{
|
||||
return mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp), date('Y', $timestamp));
|
||||
return date('Y-m-d', $this->getTimestamp($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a timetstamp from an ISO date format
|
||||
*
|
||||
* @access public
|
||||
* @param string $date
|
||||
* @param string $value
|
||||
* @return integer
|
||||
*/
|
||||
public function getTimestampFromIsoFormat($date)
|
||||
public function getTimestampFromIsoFormat($value)
|
||||
{
|
||||
return $this->removeTimeFromTimestamp(ctype_digit($date) ? $date : strtotime($date));
|
||||
return $this->removeTimeFromTimestamp(ctype_digit($value) ? $value : strtotime($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the time from a timestamp
|
||||
*
|
||||
* @access public
|
||||
* @param integer $timestamp
|
||||
* @return integer
|
||||
*/
|
||||
public function removeTimeFromTimestamp($timestamp)
|
||||
{
|
||||
return mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp), date('Y', $timestamp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,13 +231,10 @@ class DateParser extends Base
|
|||
* @param array $values Database values
|
||||
* @param string[] $fields Date fields
|
||||
* @param string $format Date format
|
||||
* @return array
|
||||
*/
|
||||
public function format(array &$values, array $fields, $format = '')
|
||||
public function format(array $values, array $fields, $format)
|
||||
{
|
||||
if ($format === '') {
|
||||
$format = $this->config->get('application_date_format');
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (! empty($values[$field])) {
|
||||
$values[$field] = date($format, $values[$field]);
|
||||
|
@ -218,23 +242,28 @@ class DateParser extends Base
|
|||
$values[$field] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert date (form input data)
|
||||
* Convert date to timestamp
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Database values
|
||||
* @param string[] $fields Date fields
|
||||
* @param boolean $keep_time Keep time or not
|
||||
* @return array
|
||||
*/
|
||||
public function convert(array &$values, array $fields, $keep_time = false)
|
||||
public function convert(array $values, array $fields, $keep_time = false)
|
||||
{
|
||||
foreach ($fields as $field) {
|
||||
if (! empty($values[$field]) && ! is_numeric($values[$field])) {
|
||||
if (! empty($values[$field])) {
|
||||
$timestamp = $this->getTimestamp($values[$field]);
|
||||
$values[$field] = $keep_time ? $timestamp : $this->removeTimeFromTimestamp($timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ class EventManager
|
|||
Task::EVENT_CLOSE => t('Closing a task'),
|
||||
Task::EVENT_CREATE_UPDATE => t('Task creation or modification'),
|
||||
Task::EVENT_ASSIGNEE_CHANGE => t('Task assignee change'),
|
||||
Task::EVENT_DAILY_CRONJOB => t('Daily background job for tasks'),
|
||||
);
|
||||
|
||||
$events = array_merge($events, $this->events);
|
||||
|
|
36
sources/app/Core/ExternalLink/ExternalLinkInterface.php
Normal file
36
sources/app/Core/ExternalLink/ExternalLinkInterface.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Core\ExternalLink;
|
||||
|
||||
/**
|
||||
* External Link Interface
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
interface ExternalLinkInterface
|
||||
{
|
||||
/**
|
||||
* Get link title
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Get link URL
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl();
|
||||
|
||||
/**
|
||||
* Set link URL
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
*/
|
||||
public function setUrl($url);
|
||||
}
|
173
sources/app/Core/ExternalLink/ExternalLinkManager.php
Normal file
173
sources/app/Core/ExternalLink/ExternalLinkManager.php
Normal file
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Core\ExternalLink;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* External Link Manager
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ExternalLinkManager extends Base
|
||||
{
|
||||
/**
|
||||
* Automatic type value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TYPE_AUTO = 'auto';
|
||||
|
||||
/**
|
||||
* Registered providers
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $providers = array();
|
||||
|
||||
/**
|
||||
* Type chosen by the user
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $userInputType = '';
|
||||
|
||||
/**
|
||||
* Text entered by the user
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $userInputText = '';
|
||||
|
||||
/**
|
||||
* Register a new provider
|
||||
*
|
||||
* Providers are registered in a LIFO queue
|
||||
*
|
||||
* @access public
|
||||
* @param ExternalLinkProviderInterface $provider
|
||||
* @return ExternalLinkManager
|
||||
*/
|
||||
public function register(ExternalLinkProviderInterface $provider)
|
||||
{
|
||||
array_unshift($this->providers, $provider);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get provider
|
||||
*
|
||||
* @access public
|
||||
* @param string $type
|
||||
* @throws ExternalLinkProviderNotFound
|
||||
* @return ExternalLinkProviderInterface
|
||||
*/
|
||||
public function getProvider($type)
|
||||
{
|
||||
foreach ($this->providers as $provider) {
|
||||
if ($provider->getType() === $type) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ExternalLinkProviderNotFound('Unable to find link provider: '.$type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get link types
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getTypes()
|
||||
{
|
||||
$types = array();
|
||||
|
||||
foreach ($this->providers as $provider) {
|
||||
$types[$provider->getType()] = $provider->getName();
|
||||
}
|
||||
|
||||
asort($types);
|
||||
|
||||
return array(self::TYPE_AUTO => t('Auto')) + $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dependency label from a provider
|
||||
*
|
||||
* @access public
|
||||
* @param string $type
|
||||
* @param string $dependency
|
||||
* @return string
|
||||
*/
|
||||
public function getDependencyLabel($type, $dependency)
|
||||
{
|
||||
$provider = $this->getProvider($type);
|
||||
$dependencies = $provider->getDependencies();
|
||||
return isset($dependencies[$dependency]) ? $dependencies[$dependency] : $dependency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a provider that match
|
||||
*
|
||||
* @access public
|
||||
* @throws ExternalLinkProviderNotFound
|
||||
* @return ExternalLinkProviderInterface
|
||||
*/
|
||||
public function find()
|
||||
{
|
||||
if ($this->userInputType === self::TYPE_AUTO) {
|
||||
$provider = $this->findProvider();
|
||||
} else {
|
||||
$provider = $this->getProvider($this->userInputType);
|
||||
$provider->setUserTextInput($this->userInputText);
|
||||
|
||||
if (! $provider->match()) {
|
||||
throw new ExternalLinkProviderNotFound('Unable to parse URL with selected provider');
|
||||
}
|
||||
}
|
||||
|
||||
if ($provider === null) {
|
||||
throw new ExternalLinkProviderNotFound('Unable to find link information from provided information');
|
||||
}
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set form values
|
||||
*
|
||||
* @access public
|
||||
* @param array $values
|
||||
* @return ExternalLinkManager
|
||||
*/
|
||||
public function setUserInput(array $values)
|
||||
{
|
||||
$this->userInputType = empty($values['type']) ? self::TYPE_AUTO : $values['type'];
|
||||
$this->userInputText = empty($values['text']) ? '' : trim($values['text']);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a provider that user input
|
||||
*
|
||||
* @access private
|
||||
* @return ExternalLinkProviderInterface
|
||||
*/
|
||||
private function findProvider()
|
||||
{
|
||||
foreach ($this->providers as $provider) {
|
||||
$provider->setUserTextInput($this->userInputText);
|
||||
|
||||
if ($provider->match()) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Core\ExternalLink;
|
||||
|
||||
/**
|
||||
* External Link Provider Interface
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
interface ExternalLinkProviderInterface
|
||||
{
|
||||
/**
|
||||
* Get provider name (label)
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Get link type (will be saved in the database)
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Get a dictionary of supported dependency types by the provider
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* [
|
||||
* 'related' => t('Related'),
|
||||
* 'child' => t('Child'),
|
||||
* 'parent' => t('Parent'),
|
||||
* 'self' => t('Self'),
|
||||
* ]
|
||||
*
|
||||
* The dictionary key is saved in the database.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencies();
|
||||
|
||||
/**
|
||||
* Set text entered by the user
|
||||
*
|
||||
* @access public
|
||||
* @param string $input
|
||||
*/
|
||||
public function setUserTextInput($input);
|
||||
|
||||
/**
|
||||
* Return true if the provider can parse correctly the user input
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function match();
|
||||
|
||||
/**
|
||||
* Get the link found with the properties
|
||||
*
|
||||
* @access public
|
||||
* @return ExternalLinkInterface
|
||||
*/
|
||||
public function getLink();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Core\ExternalLink;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* External Link Provider Not Found Exception
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ExternalLinkProviderNotFound extends Exception
|
||||
{
|
||||
}
|
|
@ -20,6 +20,7 @@ use Pimple\Container;
|
|||
* @property \Helper\Text $text
|
||||
* @property \Helper\Url $url
|
||||
* @property \Helper\User $user
|
||||
* @property \Helper\Layout $layout
|
||||
*/
|
||||
class Helper
|
||||
{
|
||||
|
|
|
@ -33,6 +33,19 @@ class Client extends Base
|
|||
*/
|
||||
const HTTP_USER_AGENT = 'Kanboard';
|
||||
|
||||
/**
|
||||
* Send a GET HTTP request
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
* @param string[] $headers
|
||||
* @return string
|
||||
*/
|
||||
public function get($url, array $headers = array())
|
||||
{
|
||||
return $this->doRequest('GET', $url, '', $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a GET HTTP request and parse JSON response
|
||||
*
|
||||
|
|
|
@ -68,11 +68,12 @@ class Response extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param string $url Redirection URL
|
||||
* @param boolean $self If Ajax request and true: refresh the current page
|
||||
*/
|
||||
public function redirect($url)
|
||||
public function redirect($url, $self = false)
|
||||
{
|
||||
if ($this->request->getServerVariable('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest') {
|
||||
header('X-Ajax-Redirect: '.$url);
|
||||
if ($this->request->isAjax()) {
|
||||
header('X-Ajax-Redirect: '.($self ? 'self' : $url));
|
||||
} else {
|
||||
header('Location: '.$url);
|
||||
}
|
||||
|
|
|
@ -146,32 +146,6 @@ class Translator
|
|||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a formatted datetime
|
||||
*
|
||||
* $translator->datetime('%Y-%m-%d', time());
|
||||
*
|
||||
* @access public
|
||||
* @param string $format Format defined by the strftime function
|
||||
* @param integer $timestamp Unix timestamp
|
||||
* @return string
|
||||
*/
|
||||
public function datetime($format, $timestamp)
|
||||
{
|
||||
if (! $timestamp) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$format = $this->get($format, $format);
|
||||
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||
$format = str_replace('%e', '%d', $format);
|
||||
$format = str_replace('%k', '%H', $format);
|
||||
}
|
||||
|
||||
return strftime($format, (int) $timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an identifier from the translations or return the default
|
||||
*
|
||||
|
@ -199,8 +173,6 @@ class Translator
|
|||
*/
|
||||
public static function load($language, $path = self::PATH)
|
||||
{
|
||||
setlocale(LC_TIME, $language.'.UTF-8', $language);
|
||||
|
||||
$filename = $path.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'translations.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
|
|
|
@ -52,7 +52,7 @@ class UserProfile extends Base
|
|||
$this->groupSync->synchronize($profile['id'], $user->getExternalGroupIds());
|
||||
}
|
||||
|
||||
if (! empty($profile)) {
|
||||
if (! empty($profile) && $profile['is_active'] == 1) {
|
||||
$this->userSession->initialize($profile);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use Symfony\Component\EventDispatcher\Event as BaseEvent;
|
|||
|
||||
class GenericEvent extends BaseEvent implements ArrayAccess
|
||||
{
|
||||
private $container = array();
|
||||
protected $container = array();
|
||||
|
||||
public function __construct(array $values = array())
|
||||
{
|
||||
|
|
11
sources/app/Event/TaskListEvent.php
Normal file
11
sources/app/Event/TaskListEvent.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Event;
|
||||
|
||||
class TaskListEvent extends GenericEvent
|
||||
{
|
||||
public function setTasks(array &$tasks)
|
||||
{
|
||||
$this->container['tasks'] =& $tasks;
|
||||
}
|
||||
}
|
26
sources/app/ExternalLink/AttachmentLink.php
Normal file
26
sources/app/ExternalLink/AttachmentLink.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\ExternalLink;
|
||||
|
||||
use Kanboard\Core\ExternalLink\ExternalLinkInterface;
|
||||
|
||||
/**
|
||||
* Attachment Link
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AttachmentLink extends BaseLink implements ExternalLinkInterface
|
||||
{
|
||||
/**
|
||||
* Get link title
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
$path = parse_url($this->url, PHP_URL_PATH);
|
||||
return basename($path);
|
||||
}
|
||||
}
|
117
sources/app/ExternalLink/AttachmentLinkProvider.php
Normal file
117
sources/app/ExternalLink/AttachmentLinkProvider.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\ExternalLink;
|
||||
|
||||
use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface;
|
||||
|
||||
/**
|
||||
* Attachment Link Provider
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AttachmentLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface
|
||||
{
|
||||
/**
|
||||
* File extensions that are not attachments
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $extensions = array(
|
||||
'html',
|
||||
'htm',
|
||||
'xhtml',
|
||||
'php',
|
||||
'jsp',
|
||||
'do',
|
||||
'action',
|
||||
'asp',
|
||||
'aspx',
|
||||
'cgi',
|
||||
);
|
||||
|
||||
/**
|
||||
* Get provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return t('Attachment');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get link type
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return 'attachment';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a dictionary of supported dependency types by the provider
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencies()
|
||||
{
|
||||
return array(
|
||||
'related' => t('Related'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the provider can parse correctly the user input
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function match()
|
||||
{
|
||||
if (preg_match('/^https?:\/\/.*\.([^\/]+)$/', $this->userInput, $matches)) {
|
||||
return $this->isValidExtension($matches[1]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the link found with the properties
|
||||
*
|
||||
* @access public
|
||||
* @return ExternalLinkInterface
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
$link = new AttachmentLink($this->container);
|
||||
$link->setUrl($this->userInput);
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check file extension
|
||||
*
|
||||
* @access protected
|
||||
* @param string $extension
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isValidExtension($extension)
|
||||
{
|
||||
$extension = strtolower($extension);
|
||||
|
||||
foreach ($this->extensions as $ext) {
|
||||
if ($extension === $ext) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
44
sources/app/ExternalLink/BaseLink.php
Normal file
44
sources/app/ExternalLink/BaseLink.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\ExternalLink;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Base Link
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseLink extends Base
|
||||
{
|
||||
/**
|
||||
* URL
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $url = '';
|
||||
|
||||
/**
|
||||
* Get link URL
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set link URL
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
}
|
33
sources/app/ExternalLink/BaseLinkProvider.php
Normal file
33
sources/app/ExternalLink/BaseLinkProvider.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\ExternalLink;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Base Link Provider
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseLinkProvider extends Base
|
||||
{
|
||||
/**
|
||||
* User input
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $userInput = '';
|
||||
|
||||
/**
|
||||
* Set text entered by the user
|
||||
*
|
||||
* @access public
|
||||
* @param string $input
|
||||
*/
|
||||
public function setUserTextInput($input)
|
||||
{
|
||||
$this->userInput = trim($input);
|
||||
}
|
||||
}
|
37
sources/app/ExternalLink/WebLink.php
Normal file
37
sources/app/ExternalLink/WebLink.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\ExternalLink;
|
||||
|
||||
use Kanboard\Core\ExternalLink\ExternalLinkInterface;
|
||||
|
||||
/**
|
||||
* Web Link
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class WebLink extends BaseLink implements ExternalLinkInterface
|
||||
{
|
||||
/**
|
||||
* Get link title
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
$html = $this->httpClient->get($this->url);
|
||||
|
||||
if (preg_match('/<title>(.*)<\/title>/siU', $html, $matches)) {
|
||||
return trim($matches[1]);
|
||||
}
|
||||
|
||||
$components = parse_url($this->url);
|
||||
|
||||
if (! empty($components['host']) && ! empty($components['path'])) {
|
||||
return $components['host'].$components['path'];
|
||||
}
|
||||
|
||||
return t('Title not found');
|
||||
}
|
||||
}
|
77
sources/app/ExternalLink/WebLinkProvider.php
Normal file
77
sources/app/ExternalLink/WebLinkProvider.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\ExternalLink;
|
||||
|
||||
use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface;
|
||||
|
||||
/**
|
||||
* Web Link Provider
|
||||
*
|
||||
* @package externalLink
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class WebLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface
|
||||
{
|
||||
/**
|
||||
* Get provider name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return t('Web Link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get link type
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return 'weblink';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a dictionary of supported dependency types by the provider
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencies()
|
||||
{
|
||||
return array(
|
||||
'related' => t('Related'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the provider can parse correctly the user input
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function match()
|
||||
{
|
||||
$startWithHttp = strpos($this->userInput, 'http://') === 0 || strpos($this->userInput, 'https://') === 0;
|
||||
$validUrl = filter_var($this->userInput, FILTER_VALIDATE_URL);
|
||||
|
||||
return $startWithHttp && $validUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the link found with the properties
|
||||
*
|
||||
* @access public
|
||||
* @return ExternalLinkInterface
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
$link = new WebLink($this->container);
|
||||
$link->setUrl($this->userInput);
|
||||
|
||||
return $link;
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ class TaskFilterGanttFormatter extends TaskFilter implements FormatterInterface
|
|||
private function formatTask(array $task)
|
||||
{
|
||||
if (! isset($this->columns[$task['project_id']])) {
|
||||
$this->columns[$task['project_id']] = $this->board->getColumnsList($task['project_id']);
|
||||
$this->columns[$task['project_id']] = $this->column->getList($task['project_id']);
|
||||
}
|
||||
|
||||
$start = $task['date_started'] ?: time();
|
||||
|
|
|
@ -17,11 +17,12 @@ class App extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param string $param
|
||||
* @param mixed $default_value
|
||||
* @return mixed
|
||||
*/
|
||||
public function config($param)
|
||||
public function config($param, $default_value = '')
|
||||
{
|
||||
return $this->config->get($param);
|
||||
return $this->config->get($param, $default_value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,50 @@ use DateTime;
|
|||
*/
|
||||
class Dt extends \Kanboard\Core\Base
|
||||
{
|
||||
/**
|
||||
* Get formatted time
|
||||
*
|
||||
* @access public
|
||||
* @param integer $value
|
||||
* @return string
|
||||
*/
|
||||
public function time($value)
|
||||
{
|
||||
return date($this->config->get('application_time_format', 'H:i'), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted date
|
||||
*
|
||||
* @access public
|
||||
* @param integer $value
|
||||
* @return string
|
||||
*/
|
||||
public function date($value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (! ctype_digit($value)) {
|
||||
$value = strtotime($value);
|
||||
}
|
||||
|
||||
return date($this->config->get('application_date_format', 'm/d/Y'), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted datetime
|
||||
*
|
||||
* @access public
|
||||
* @param integer $value
|
||||
* @return string
|
||||
*/
|
||||
public function datetime($value)
|
||||
{
|
||||
return date($this->config->get('application_datetime_format', 'm/d/Y H:i'), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get duration in seconds into human format
|
||||
*
|
||||
|
@ -107,6 +151,6 @@ class Dt extends \Kanboard\Core\Base
|
|||
*/
|
||||
public function getWeekDay($day)
|
||||
{
|
||||
return dt('%A', strtotime('next Monday +'.($day - 1).' days'));
|
||||
return date('l', strtotime('next Monday +'.($day - 1).' days'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,19 +38,70 @@ class File extends \Kanboard\Core\Base
|
|||
return 'fa-file-powerpoint-o';
|
||||
case 'zip':
|
||||
case 'rar':
|
||||
case 'tar':
|
||||
case 'bz2':
|
||||
case 'xz':
|
||||
case 'gz':
|
||||
return 'fa-file-archive-o';
|
||||
case 'mp3':
|
||||
return 'fa-audio-o';
|
||||
return 'fa-file-audio-o';
|
||||
case 'avi':
|
||||
return 'fa-video-o';
|
||||
case 'mov':
|
||||
return 'fa-file-video-o';
|
||||
case 'php':
|
||||
case 'html':
|
||||
case 'css':
|
||||
return 'fa-code-o';
|
||||
return 'fa-file-code-o';
|
||||
case 'pdf':
|
||||
return 'fa-file-pdf-o';
|
||||
}
|
||||
|
||||
return 'fa-file-o';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image mimetype based on the file extension
|
||||
*
|
||||
* @access public
|
||||
* @param $filename
|
||||
* @return string
|
||||
*/
|
||||
public function getImageMimeType($filename)
|
||||
{
|
||||
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
|
||||
switch ($extension) {
|
||||
case 'jpeg':
|
||||
case 'jpg':
|
||||
return 'image/jpeg';
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
default:
|
||||
return 'image/jpeg';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preview type
|
||||
*
|
||||
* @access public
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public function getPreviewType($filename)
|
||||
{
|
||||
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
|
||||
switch ($extension) {
|
||||
case 'md':
|
||||
case 'markdown':
|
||||
return 'markdown';
|
||||
case 'txt':
|
||||
return 'text';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue