看板初始化提交
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Class BaseFormatter
|
||||
*
|
||||
* @package formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseFormatter extends Base
|
||||
{
|
||||
/**
|
||||
* Query object
|
||||
*
|
||||
* @access protected
|
||||
* @var Table
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Set query
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return $this
|
||||
*/
|
||||
public function withQuery(Table $query)
|
||||
{
|
||||
$this->query = $query;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Board Column Formatter
|
||||
*
|
||||
* @package formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class BoardColumnFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $swimlaneId = 0;
|
||||
protected $columns = array();
|
||||
protected $tasks = array();
|
||||
protected $tags = array();
|
||||
protected $taskCountBySwimlaneAndColumn = array();
|
||||
|
||||
/**
|
||||
* Set swimlaneId
|
||||
*
|
||||
* @access public
|
||||
* @param integer $swimlaneId
|
||||
* @return $this
|
||||
*/
|
||||
public function withSwimlaneId($swimlaneId)
|
||||
{
|
||||
$this->swimlaneId = $swimlaneId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set columns
|
||||
*
|
||||
* @access public
|
||||
* @param array $columns
|
||||
* @return $this
|
||||
*/
|
||||
public function withColumns(array $columns)
|
||||
{
|
||||
$this->columns = $columns;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tasks
|
||||
*
|
||||
* @access public
|
||||
* @param array $tasks
|
||||
* @return $this
|
||||
*/
|
||||
public function withTasks(array $tasks)
|
||||
{
|
||||
$this->tasks = $tasks;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tags
|
||||
*
|
||||
* @access public
|
||||
* @param array $tags
|
||||
* @return $this
|
||||
*/
|
||||
public function withTags(array $tags)
|
||||
{
|
||||
$this->tags = $tags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set task count by swimlane and column
|
||||
*
|
||||
* @access public
|
||||
* @param array $taskCountBySwimlaneAndColumn
|
||||
* @return $this
|
||||
*/
|
||||
public function withTaskCountBySwimlaneAndColumn(array $taskCountBySwimlaneAndColumn)
|
||||
{
|
||||
$this->taskCountBySwimlaneAndColumn = $taskCountBySwimlaneAndColumn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
foreach ($this->columns as &$column) {
|
||||
$column['id'] = (int) $column['id'];
|
||||
$column['tasks'] = $this->boardTaskFormatter
|
||||
->withTasks($this->tasks)
|
||||
->withTags($this->tags)
|
||||
->withSwimlaneId($this->swimlaneId)
|
||||
->withColumnId($column['id'])
|
||||
->format();
|
||||
|
||||
$column['nb_tasks'] = count($column['tasks']);
|
||||
$column['score'] = (int) array_column_sum($column['tasks'], 'score');
|
||||
|
||||
if (empty($this->taskCountBySwimlaneAndColumn)) {
|
||||
$column['column_nb_open_tasks'] = $column['nb_open_tasks'];
|
||||
} else {
|
||||
$column['column_nb_open_tasks'] = isset($this->taskCountBySwimlaneAndColumn[$this->swimlaneId][$column['id']]) ? $this->taskCountBySwimlaneAndColumn[$this->swimlaneId][$column['id']] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->columns;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
use Kanboard\Model\ProjectModel;
|
||||
use Kanboard\Model\SwimlaneModel;
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Board Formatter
|
||||
*
|
||||
* @package formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class BoardFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Project id
|
||||
*
|
||||
* @access protected
|
||||
* @var integer
|
||||
*/
|
||||
protected $projectId;
|
||||
|
||||
/**
|
||||
* Set ProjectId
|
||||
*
|
||||
* @access public
|
||||
* @param integer $projectId
|
||||
* @return $this
|
||||
*/
|
||||
public function withProjectId($projectId)
|
||||
{
|
||||
$this->projectId = $projectId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$project = $this->projectModel->getById($this->projectId);
|
||||
$swimlanes = $this->swimlaneModel->getAllByStatus($this->projectId, SwimlaneModel::ACTIVE);
|
||||
$columns = $this->columnModel->getAllWithOpenedTaskCount($this->projectId);
|
||||
$task_count_by_swimlanes_and_columns = [];
|
||||
|
||||
if ($project['per_swimlane_task_limits']) {
|
||||
foreach ($this->taskModel->getOpenTaskCountBySwimlaneAndColumn($this->projectId) as $task_count) {
|
||||
$task_count_by_swimlanes_and_columns[$task_count['swimlane_id']][$task_count['column_id']] = $task_count['nb_open_tasks'];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($swimlanes) || empty($columns)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$this->hook->reference('formatter:board:query', $this->query);
|
||||
|
||||
$tasks = $this->query
|
||||
->eq(TaskModel::TABLE.'.project_id', $this->projectId)
|
||||
->asc(TaskModel::TABLE.'.position')
|
||||
->findAll();
|
||||
|
||||
$task_ids = array_column($tasks, 'id');
|
||||
$tags = $this->taskTagModel->getTagsByTaskIds($task_ids);
|
||||
|
||||
return $this->boardSwimlaneFormatter
|
||||
->withSwimlanes($swimlanes)
|
||||
->withColumns($columns)
|
||||
->withTasks($tasks)
|
||||
->withTags($tags)
|
||||
->withTaskCountBySwimlaneAndColumn($task_count_by_swimlanes_and_columns)
|
||||
->format();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Board Swimlane Formatter
|
||||
*
|
||||
* @package formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $swimlanes = array();
|
||||
protected $columns = array();
|
||||
protected $tasks = array();
|
||||
protected $tags = array();
|
||||
protected $taskCountBySwimlaneAndColumn = array();
|
||||
|
||||
/**
|
||||
* Set swimlanes
|
||||
*
|
||||
* @access public
|
||||
* @param array $swimlanes
|
||||
* @return $this
|
||||
*/
|
||||
public function withSwimlanes(array $swimlanes)
|
||||
{
|
||||
$this->swimlanes = $swimlanes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set columns
|
||||
*
|
||||
* @access public
|
||||
* @param array $columns
|
||||
* @return $this
|
||||
*/
|
||||
public function withColumns(array $columns)
|
||||
{
|
||||
$this->columns = $columns;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tasks
|
||||
*
|
||||
* @access public
|
||||
* @param array $tasks
|
||||
* @return $this
|
||||
*/
|
||||
public function withTasks(array $tasks)
|
||||
{
|
||||
$this->tasks = $tasks;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tags
|
||||
*
|
||||
* @access public
|
||||
* @param array $tags
|
||||
* @return $this
|
||||
*/
|
||||
public function withTags(array $tags)
|
||||
{
|
||||
$this->tags = $tags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set task count by swimlane and column
|
||||
*
|
||||
* @access public
|
||||
* @param array $taskCountBySwimlaneAndColumn
|
||||
* @return $this
|
||||
*/
|
||||
public function withTaskCountBySwimlaneAndColumn(array $taskCountBySwimlaneAndColumn)
|
||||
{
|
||||
$this->taskCountBySwimlaneAndColumn = $taskCountBySwimlaneAndColumn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$nb_swimlanes = count($this->swimlanes);
|
||||
$nb_columns = count($this->columns);
|
||||
$tasks_stats_by_column_across_swimlanes = [];
|
||||
|
||||
foreach ($this->columns as $column) {
|
||||
$tasks_stats_by_column_across_swimlanes[$column['id']] = [
|
||||
'nb_visible_tasks_across_swimlane' => 0,
|
||||
'nb_unfiltered_tasks_across_swimlane' => 0,
|
||||
'cumulative_score_across_swimlane' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($this->swimlanes as &$swimlane) {
|
||||
$swimlane['id'] = (int) $swimlane['id'];
|
||||
$swimlane['columns'] = $this->boardColumnFormatter
|
||||
->withSwimlaneId($swimlane['id'])
|
||||
->withColumns($this->columns)
|
||||
->withTasks($this->tasks)
|
||||
->withTags($this->tags)
|
||||
->withTaskCountBySwimlaneAndColumn($this->taskCountBySwimlaneAndColumn)
|
||||
->format();
|
||||
|
||||
$swimlane['nb_swimlanes'] = $nb_swimlanes;
|
||||
$swimlane['nb_columns'] = $nb_columns;
|
||||
$swimlane['nb_tasks'] = array_column_sum($swimlane['columns'], 'nb_tasks');
|
||||
$swimlane['score'] = array_column_sum($swimlane['columns'], 'score');
|
||||
|
||||
foreach ($swimlane['columns'] as &$column) {
|
||||
$tasks_stats_by_column_across_swimlanes[$column['id']]['nb_visible_tasks_across_swimlane'] += count($column['tasks']);
|
||||
$tasks_stats_by_column_across_swimlanes[$column['id']]['nb_unfiltered_tasks_across_swimlane'] = $column['nb_open_tasks'];
|
||||
$tasks_stats_by_column_across_swimlanes[$column['id']]['cumulative_score_across_swimlane'] += $column['score'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->swimlanes as &$swimlane) {
|
||||
foreach ($swimlane['columns'] as &$column) {
|
||||
$column['nb_visible_tasks_across_swimlane'] = $tasks_stats_by_column_across_swimlanes[$column['id']]['nb_visible_tasks_across_swimlane'];
|
||||
$column['nb_unfiltered_tasks_across_swimlane'] = $tasks_stats_by_column_across_swimlanes[$column['id']]['nb_unfiltered_tasks_across_swimlane'];
|
||||
$column['cumulative_score_across_swimlane'] = $tasks_stats_by_column_across_swimlanes[$column['id']]['cumulative_score_across_swimlane'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->swimlanes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Board Task Formatter
|
||||
*
|
||||
* @package formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class BoardTaskFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $tasks = array();
|
||||
protected $tags = array();
|
||||
protected $columnId = 0;
|
||||
protected $swimlaneId = 0;
|
||||
|
||||
/**
|
||||
* Set tags
|
||||
*
|
||||
* @access public
|
||||
* @param array $tags
|
||||
* @return $this
|
||||
*/
|
||||
public function withTags(array $tags)
|
||||
{
|
||||
$this->tags = $tags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tasks
|
||||
*
|
||||
* @access public
|
||||
* @param array $tasks
|
||||
* @return $this
|
||||
*/
|
||||
public function withTasks(array $tasks)
|
||||
{
|
||||
$this->tasks = $tasks;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set columnId
|
||||
*
|
||||
* @access public
|
||||
* @param integer $columnId
|
||||
* @return $this
|
||||
*/
|
||||
public function withColumnId($columnId)
|
||||
{
|
||||
$this->columnId = $columnId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set swimlaneId
|
||||
*
|
||||
* @access public
|
||||
* @param integer $swimlaneId
|
||||
* @return $this
|
||||
*/
|
||||
public function withSwimlaneId($swimlaneId)
|
||||
{
|
||||
$this->swimlaneId = $swimlaneId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$tasks = array_values(array_filter($this->tasks, array($this, 'filterTasks')));
|
||||
array_merge_relation($tasks, $this->tags, 'tags', 'id');
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
$task['is_draggable'] = $this->helper->projectRole->isDraggable($task);
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep only tasks of the given column and swimlane
|
||||
*
|
||||
* @access protected
|
||||
* @param array $task
|
||||
* @return bool
|
||||
*/
|
||||
protected function filterTasks(array $task)
|
||||
{
|
||||
return $task['column_id'] == $this->columnId && $task['swimlane_id'] == $this->swimlaneId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
use Kanboard\Core\Group\GroupProviderInterface;
|
||||
|
||||
/**
|
||||
* Auto-complete formatter for groups
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class GroupAutoCompleteFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Groups found
|
||||
*
|
||||
* @access protected
|
||||
* @var GroupProviderInterface[]
|
||||
*/
|
||||
protected $groups;
|
||||
|
||||
/**
|
||||
* Set groups
|
||||
*
|
||||
* @access public
|
||||
* @param GroupProviderInterface[] $groups
|
||||
* @return $this
|
||||
*/
|
||||
public function withGroups(array $groups)
|
||||
{
|
||||
$this->groups = $groups;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format groups for the ajax auto-completion
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($this->groups as $group) {
|
||||
$result[] = array(
|
||||
'id' => $group->getInternalId(),
|
||||
'external_id' => $group->getExternalId(),
|
||||
'value' => $group->getName(),
|
||||
'label' => $group->getName(),
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
class ProjectActivityEventFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$events = $this->query->findAll();
|
||||
$res = array();
|
||||
|
||||
foreach ($events as &$event) {
|
||||
$eventData = $this->unserializeEvent($event['data']);
|
||||
if (empty($eventData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$event += $eventData;
|
||||
unset($event['data']);
|
||||
|
||||
if (isset($event['comment'])) {
|
||||
if ($this->userSession->getRole() === Role::APP_USER && $event['comment']['visibility'] !== Role::APP_USER) {
|
||||
continue;
|
||||
}
|
||||
if ($this->userSession->getRole() === Role::APP_MANAGER && $event['comment']['visibility'] === Role::APP_ADMIN) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$event['author'] = $event['author_name'] ?: $event['author_username'];
|
||||
$event['event_title'] = $this->notificationModel->getTitleWithAuthor($event['author'], $event['event_name'], $event);
|
||||
$event['event_content'] = $this->renderEvent($event);
|
||||
$res[] = $event;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode event data, supports unserialize() and json_decode()
|
||||
*
|
||||
* @access protected
|
||||
* @param string $data Serialized data
|
||||
* @return array
|
||||
*/
|
||||
protected function unserializeEvent($data)
|
||||
{
|
||||
// Ignore legacy events serialized with PHP due potential security issues.
|
||||
if ($data[0] === 'a') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return json_decode($data, true) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event html content
|
||||
*
|
||||
* @access protected
|
||||
* @param array $params Event properties
|
||||
* @return string
|
||||
*/
|
||||
protected function renderEvent(array $params)
|
||||
{
|
||||
return $this->template->render(
|
||||
'event/'.str_replace('.', '_', $params['event_name']),
|
||||
$params
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Class ProjectApiFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
*/
|
||||
class ProjectApiFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $project = null;
|
||||
|
||||
public function withProject($project)
|
||||
{
|
||||
$this->project = $project;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
if (! empty($this->project)) {
|
||||
$this->project['url'] = array(
|
||||
'board' => $this->helper->url->to('BoardViewController', 'show', array('project_id' => $this->project['id']), '', true),
|
||||
'list' => $this->helper->url->to('TaskListController', 'show', array('project_id' => $this->project['id']), '', true),
|
||||
);
|
||||
|
||||
// Add public board URL if public access is enabled
|
||||
if (!empty($this->project['is_public']) && !empty($this->project['token'])) {
|
||||
$this->project['url']['public_board'] = $this->helper->url->to('BoardViewController', 'readonly', array('token' => $this->project['token']), '', true);
|
||||
$this->project['url']['rss_feed'] = $this->helper->url->to('FeedController', 'project', array('token' => $this->project['token']), '', true);
|
||||
$this->project['url']['ical_feed'] = $this->helper->url->to('ICalendarController', 'project', array('token' => $this->project['token']), '', true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->project;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Class ProjectsApiFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
*/
|
||||
class ProjectsApiFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $projects = array();
|
||||
|
||||
public function withProjects($projects)
|
||||
{
|
||||
$this->projects = $projects;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
if (! empty($this->projects)) {
|
||||
foreach ($this->projects as &$project) {
|
||||
$project = $this->projectApiFormatter->withProject($project)->format();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->projects;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Class SubtaskListFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskListFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$status = $this->subtaskModel->getStatusList();
|
||||
$subtasks = $this->query->findAll();
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$subtask['status_name'] = $status[$subtask['status']];
|
||||
$subtask['timer_start_date'] = isset($subtask['timer_start_date']) ? $subtask['timer_start_date'] : 0;
|
||||
$subtask['is_timer_started'] = ! empty($subtask['timer_start_date']);
|
||||
}
|
||||
|
||||
return $subtasks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
class SubtaskTimeTrackingCalendarFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Format calendar events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$events = array();
|
||||
|
||||
foreach ($this->query->findAll() as $row) {
|
||||
$user = isset($row['username']) ? ' ('.($row['user_fullname'] ?: $row['username']).')' : '';
|
||||
|
||||
$events[] = array(
|
||||
'id' => $row['id'],
|
||||
'subtask_id' => $row['subtask_id'],
|
||||
'title' => t('#%d', $row['task_id']).' '.$row['subtask_title'].$user,
|
||||
'start' => date('Y-m-d\TH:i:s', $row['start']),
|
||||
'end' => date('Y-m-d\TH:i:s', $row['end'] ?: time()),
|
||||
'backgroundColor' => $this->colorModel->getBackgroundColor($row['color_id']),
|
||||
'borderColor' => $this->colorModel->getBorderColor($row['color_id']),
|
||||
'textColor' => 'black',
|
||||
'url' => $this->helper->url->to('TaskViewController', 'show', array('task_id' => $row['task_id'])),
|
||||
'editable' => false,
|
||||
);
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Class TaskApiFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
*/
|
||||
class TaskApiFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $task = null;
|
||||
|
||||
public function withTask($task)
|
||||
{
|
||||
$this->task = $task;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
if (! empty($this->task)) {
|
||||
$this->task['url'] = $this->helper->url->to('TaskViewController', 'show', array('task_id' => $this->task['id']), '', true);
|
||||
$this->task['color'] = $this->colorModel->getColorProperties($this->task['color_id']);
|
||||
}
|
||||
|
||||
return $this->task;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
use Kanboard\Model\ProjectModel;
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Task AutoComplete Formatter
|
||||
*
|
||||
* @package formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAutoCompleteFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $limit = 25;
|
||||
|
||||
/**
|
||||
* Limit number of results
|
||||
*
|
||||
* @param $limit
|
||||
* @return $this
|
||||
*/
|
||||
public function withLimit($limit)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$tasks = $this->query
|
||||
->columns(
|
||||
TaskModel::TABLE.'.id',
|
||||
TaskModel::TABLE.'.title',
|
||||
ProjectModel::TABLE.'.name AS project_name'
|
||||
)
|
||||
->asc(TaskModel::TABLE.'.id')
|
||||
->limit($this->limit)
|
||||
->findAll();
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
$task['value'] = $task['title'];
|
||||
$task['label'] = $task['project_name'].' > #'.$task['id'].' '.$task['title'];
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use DateTime;
|
||||
use Eluceo\iCal\Component\Calendar;
|
||||
use Eluceo\iCal\Component\Event;
|
||||
use Eluceo\iCal\Property\Event\Attendees;
|
||||
use Eluceo\iCal\Property\Event\Organizer;
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* iCal event formatter for tasks
|
||||
*
|
||||
* @package formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskICalFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Calendar object
|
||||
*
|
||||
* @access protected
|
||||
* @var \Eluceo\iCal\Component\Calendar
|
||||
*/
|
||||
protected $vCalendar;
|
||||
|
||||
/**
|
||||
* Get Ical events
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
return $this->vCalendar->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set calendar object
|
||||
*
|
||||
* @access public
|
||||
* @param \Eluceo\iCal\Component\Calendar $vCalendar
|
||||
* @return $this
|
||||
*/
|
||||
public function setCalendar(Calendar $vCalendar)
|
||||
{
|
||||
$this->vCalendar = $vCalendar;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform results to iCal events
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @param string $startColumn
|
||||
* @param string $endColumn
|
||||
* @return $this
|
||||
*/
|
||||
public function addTasksWithStartAndDueDate(Table $query, $startColumn, $endColumn)
|
||||
{
|
||||
foreach ($query->findAll() as $task) {
|
||||
$start = new DateTime;
|
||||
$start->setTimestamp($task[$startColumn]);
|
||||
|
||||
$end = new DateTime;
|
||||
$end->setTimestamp($task[$endColumn] ?: time());
|
||||
|
||||
$vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$startColumn.'-'.$endColumn);
|
||||
$vEvent->setDtStart($start);
|
||||
$vEvent->setDtEnd($end);
|
||||
|
||||
$this->vCalendar->addComponent($vEvent);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform results to all day iCal events
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return $this
|
||||
*/
|
||||
public function addTasksWithDueDateOnly(Table $query)
|
||||
{
|
||||
foreach ($query->findAll() as $task) {
|
||||
$date = new DateTime;
|
||||
$date->setTimestamp($task['date_due']);
|
||||
|
||||
$vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-date_due');
|
||||
$vEvent->setDtStart($date);
|
||||
$vEvent->setDtEnd($date);
|
||||
|
||||
if ($date->format('Hi') === '0000') {
|
||||
$vEvent->setNoTime(true);
|
||||
}
|
||||
|
||||
$this->vCalendar->addComponent($vEvent);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get common events for task iCal events
|
||||
*
|
||||
* @access protected
|
||||
* @param array $task
|
||||
* @param string $uid
|
||||
* @return Event
|
||||
*/
|
||||
protected function getTaskIcalEvent(array &$task, $uid)
|
||||
{
|
||||
$dateCreation = new DateTime;
|
||||
$dateCreation->setTimestamp($task['date_creation']);
|
||||
|
||||
$dateModif = new DateTime;
|
||||
$dateModif->setTimestamp($task['date_modification']);
|
||||
|
||||
$vEvent = new Event($uid);
|
||||
$vEvent->setCreated($dateCreation);
|
||||
$vEvent->setModified($dateModif);
|
||||
$vEvent->setUseTimezone(true);
|
||||
$vEvent->setSummary(t('#%d', $task['id']).' '.$task['title']);
|
||||
$vEvent->setDescription($task['description']);
|
||||
$vEvent->setDescriptionHTML($this->helper->text->markdown($task['description']));
|
||||
$vEvent->setUrl($this->helper->url->base().$this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])));
|
||||
|
||||
if (! empty($task['owner_id'])) {
|
||||
$attendees = new Attendees;
|
||||
$attendees->add(
|
||||
'MAILTO:'.($task['assignee_email'] ?: $task['assignee_username'].'@kanboard.local'),
|
||||
array('CN' => $task['assignee_name'] ?: $task['assignee_username'])
|
||||
);
|
||||
$vEvent->setAttendees($attendees);
|
||||
}
|
||||
|
||||
if (! empty($task['creator_id'])) {
|
||||
$vEvent->setOrganizer(new Organizer(
|
||||
'MAILTO:' . $task['creator_email'] ?: $task['creator_username'].'@kanboard.local',
|
||||
array('CN' => $task['creator_name'] ?: $task['creator_username'])
|
||||
));
|
||||
}
|
||||
|
||||
return $vEvent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Class TaskListFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskListFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$tasks = $this->query->findAll();
|
||||
$taskIds = array_column($tasks, 'id');
|
||||
$tags = $this->taskTagModel->getTagsByTaskIds($taskIds);
|
||||
array_merge_relation($tasks, $tags, 'tags', 'id');
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
/**
|
||||
* Class TaskListSubtaskAssigneeFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskListSubtaskAssigneeFormatter extends TaskListFormatter
|
||||
{
|
||||
protected $userId = 0;
|
||||
protected $withoutEmptyTasks = false;
|
||||
|
||||
/**
|
||||
* Set assignee
|
||||
*
|
||||
* @param integer $userId
|
||||
* @return $this
|
||||
*/
|
||||
public function withUserId($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withoutEmptyTasks()
|
||||
{
|
||||
$this->withoutEmptyTasks = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$tasks = parent::format();
|
||||
$taskIds = array_column($tasks, 'id');
|
||||
$subtasks = $this->subtaskModel->getAllByTaskIdsAndAssignee($taskIds, $this->userId);
|
||||
$subtasks = array_column_index($subtasks, 'task_id');
|
||||
array_merge_relation($tasks, $subtasks, 'subtasks', 'id');
|
||||
|
||||
if ($this->withoutEmptyTasks) {
|
||||
$tasks = array_filter($tasks, function (array $task) {
|
||||
return count($task['subtasks']) > 0;
|
||||
});
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
/**
|
||||
* Class TaskListSubtaskFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskListSubtaskFormatter extends TaskListFormatter
|
||||
{
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$tasks = parent::format();
|
||||
$taskIds = array_column($tasks, 'id');
|
||||
$subtasks = $this->subtaskModel->getAllByTaskIds($taskIds);
|
||||
$subtasks = array_column_index($subtasks, 'task_id');
|
||||
array_merge_relation($tasks, $subtasks, 'subtasks', 'id');
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
use Kanboard\Model\ProjectModel;
|
||||
use Kanboard\Model\TaskModel;
|
||||
|
||||
/**
|
||||
* Class TaskSuggestMenuFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskSuggestMenuFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $limit = 25;
|
||||
|
||||
/**
|
||||
* Limit number of results
|
||||
*
|
||||
* @param $limit
|
||||
* @return $this
|
||||
*/
|
||||
public function withLimit($limit)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$result = array();
|
||||
$tasks = $this->query
|
||||
->columns(
|
||||
TaskModel::TABLE.'.id',
|
||||
TaskModel::TABLE.'.title',
|
||||
ProjectModel::TABLE.'.name AS project_name'
|
||||
)
|
||||
->asc(TaskModel::TABLE.'.id')
|
||||
->limit($this->limit)
|
||||
->findAll();
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$html = '#'.$task['id'].' ';
|
||||
$html .= $this->helper->text->e($task['title']).' ';
|
||||
$html .= '<small>'.$this->helper->text->e($task['project_name']).'</small>';
|
||||
|
||||
$result[] = array(
|
||||
'value' => (string) $task['id'],
|
||||
'html' => $html,
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Class TasksApiFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
*/
|
||||
class TasksApiFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $tasks = array();
|
||||
|
||||
public function withTasks($tasks)
|
||||
{
|
||||
$this->tasks = $tasks;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
if (! empty($this->tasks)) {
|
||||
foreach ($this->tasks as &$task) {
|
||||
$task = $this->taskApiFormatter->withTask($task)->format();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->tasks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\User\UserProviderInterface;
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Auto-complete formatter for users
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserAutoCompleteFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Users found
|
||||
*
|
||||
* @access protected
|
||||
* @var UserProviderInterface[]
|
||||
*/
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* Set users
|
||||
*
|
||||
* @access public
|
||||
* @param UserProviderInterface[] $users
|
||||
* @return $this
|
||||
*/
|
||||
public function withUsers(array $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the users for the ajax auto-completion
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($this->users as $user) {
|
||||
$result[] = array(
|
||||
'id' => $user->getInternalId(),
|
||||
'username' => $user->getUsername(),
|
||||
'external_id' => $user->getExternalId(),
|
||||
'external_id_column' => $user->getExternalIdColumn(),
|
||||
'value' => $user->getName() === '' ? $user->getUsername() : $user->getName(),
|
||||
'label' => $user->getName() === '' ? $user->getUsername() : $user->getName().' ('.$user->getUsername().')',
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Formatter;
|
||||
|
||||
use Kanboard\Core\Filter\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Class UserMentionFormatter
|
||||
*
|
||||
* @package Kanboard\Formatter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserMentionFormatter extends BaseFormatter implements FormatterInterface
|
||||
{
|
||||
protected $users = array();
|
||||
|
||||
/**
|
||||
* Set users
|
||||
*
|
||||
* @param array $users
|
||||
* @return $this
|
||||
*/
|
||||
public function withUsers(array $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function format()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($this->users as $user) {
|
||||
$html = $this->helper->avatar->small(
|
||||
$user['id'],
|
||||
$user['username'],
|
||||
$user['name'],
|
||||
$user['email'],
|
||||
$user['avatar_path'],
|
||||
'avatar-inline'
|
||||
);
|
||||
|
||||
$html .= ' '.$this->helper->text->e($user['username']);
|
||||
|
||||
if (! empty($user['name'])) {
|
||||
$html .= ' <small aria-hidden="true">'.$this->helper->text->e($user['name']).'</small>';
|
||||
}
|
||||
|
||||
$result[] = array(
|
||||
'value' => $user['username'],
|
||||
'html' => $html,
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user