看板初始化提交

This commit is contained in:
zephyr
2026-06-01 21:23:12 -07:00
commit 27411ebedc
1827 changed files with 192340 additions and 0 deletions
+36
View File
@@ -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;
}
}
+115
View File
@@ -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;
}
}
+80
View File
@@ -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();
}
}
+138
View File
@@ -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;
}
}
+101
View File
@@ -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
);
}
}
+46
View File
@@ -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;
}
}
+38
View File
@@ -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;
}
}
+34
View File
@@ -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;
}
}
+37
View File
@@ -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;
}
}
+151
View File
@@ -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;
}
}
+30
View File
@@ -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;
}
}
+38
View File
@@ -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;
}
}
+63
View File
@@ -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;
}
}