OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
60
app/Domain/Tickets/Controllers/DelMilestone.php
Normal file
60
app/Domain/Tickets/Controllers/DelMilestone.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Leantime\Core\Auth\Permissions\RequiresPermission;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Domain\Tickets\Permissions\TicketsPermissions;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class DelMilestone extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
public function init(TicketService $ticketService): void
|
||||
{
|
||||
$this->ticketService = $ticketService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::DELETE)]
|
||||
public function get(): Response
|
||||
{
|
||||
if (! isset($_GET['id'])) {
|
||||
return $this->tpl->displayPartial('errors.error404', responseCode: 404);
|
||||
}
|
||||
|
||||
$id = (int) $_GET['id'];
|
||||
|
||||
$this->tpl->assign('ticket', $this->ticketService->getTicket($id));
|
||||
|
||||
return $this->tpl->displayPartial('tickets.delMilestone');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::DELETE)]
|
||||
public function post($params): Response
|
||||
{
|
||||
if (! isset($_GET['id'], $params['del'])) {
|
||||
return $this->tpl->displayPartial('errors.error404', responseCode: 404);
|
||||
}
|
||||
|
||||
if (($result = $this->ticketService->deleteMilestone($id = (int) ($_GET['id']))) === true) {
|
||||
$this->tpl->setNotification($this->language->__('notification.milestone_deleted'), 'success');
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/roadmap');
|
||||
}
|
||||
|
||||
$this->tpl->setNotification($this->language->__($result['msg']), 'error');
|
||||
$this->tpl->assign('ticket', $this->ticketService->getTicket($id));
|
||||
|
||||
return $this->tpl->displayPartial('tickets.delMilestone');
|
||||
}
|
||||
}
|
||||
77
app/Domain/Tickets/Controllers/DelTicket.php
Normal file
77
app/Domain/Tickets/Controllers/DelTicket.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Auth\Permissions\RequiresPermission;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Domain\Tickets\Permissions\TicketsPermissions;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class DelTicket extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
public function init(TicketService $ticketService): void
|
||||
{
|
||||
$this->ticketService = $ticketService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::DELETE)]
|
||||
public function get(): Response
|
||||
{
|
||||
if (! isset($_GET['id'])) {
|
||||
return $this->tpl->display('errors.error404', responseCode: 404);
|
||||
}
|
||||
|
||||
$id = (int) $_GET['id'];
|
||||
|
||||
try {
|
||||
$this->ticketService->canDelete($id);
|
||||
} catch (\Exception $e) {
|
||||
$this->tpl->assign('error', $e->getMessage());
|
||||
|
||||
return $this->tpl->displayPartial('tickets.delTicket');
|
||||
}
|
||||
|
||||
$this->tpl->assign('error', '');
|
||||
$this->tpl->assign('ticket', $this->ticketService->getTicket($id));
|
||||
|
||||
return $this->tpl->displayPartial('tickets.delTicket');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::DELETE)]
|
||||
public function post($params): Response
|
||||
{
|
||||
if (! isset($params['del'])) {
|
||||
return $this->tpl->display('errors.error403', responseCode: 403);
|
||||
}
|
||||
|
||||
if (! isset($_GET['id'])) {
|
||||
return $this->tpl->display('errors.error404', responseCode: 404);
|
||||
}
|
||||
|
||||
$id = (int) $_GET['id'];
|
||||
|
||||
$result = $this->ticketService->delete($id);
|
||||
|
||||
if ($result === true) {
|
||||
$this->tpl->setNotification($this->language->__('notification.todo_deleted'), 'success');
|
||||
$redirect = session('lastPage') ?? BASE_URL.'/';
|
||||
|
||||
return Frontcontroller::redirect($redirect);
|
||||
}
|
||||
|
||||
$this->tpl->setNotification($this->language->__($result['msg']), 'error');
|
||||
$this->tpl->assign('ticket', $this->ticketService->getTicket($id));
|
||||
|
||||
return $this->tpl->displayPartial('tickets.delTicket');
|
||||
}
|
||||
}
|
||||
139
app/Domain/Tickets/Controllers/EditMilestone.php
Normal file
139
app/Domain/Tickets/Controllers/EditMilestone.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Domain\Comments\Services\Comments as CommentService;
|
||||
use Leantime\Domain\Projects\Services\Projects as ProjectService;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
|
||||
class EditMilestone extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
private CommentService $commentsService;
|
||||
|
||||
private ProjectService $projectService;
|
||||
|
||||
/**
|
||||
* init - initialize private variables
|
||||
*/
|
||||
public function init(
|
||||
TicketService $ticketService,
|
||||
CommentService $commentsService,
|
||||
ProjectService $projectService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
$this->commentsService = $commentsService;
|
||||
$this->projectService = $projectService;
|
||||
}
|
||||
|
||||
/**
|
||||
* get - handle get requests
|
||||
*/
|
||||
public function get($params)
|
||||
{
|
||||
if (isset($params['id'])) {
|
||||
// Delete comment
|
||||
if (isset($params['delComment']) === true) {
|
||||
$commentId = (int) ($params['delComment']);
|
||||
$this->commentsService->deleteComment($commentId);
|
||||
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_deleted'), 'success');
|
||||
}
|
||||
|
||||
$milestone = $this->ticketService->getMilestone((int) $params['id']);
|
||||
|
||||
if ($milestone === false || ! isset($milestone->id)) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.could_not_find_milestone'), 'error');
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/roadmap/');
|
||||
}
|
||||
|
||||
// Ensure this ticket belongs to the current project
|
||||
if (session('currentProject') != $milestone->projectId) {
|
||||
$this->projectService->changeCurrentSessionProject($milestone->projectId);
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/editMilestone/'.$milestone->id);
|
||||
}
|
||||
|
||||
$comments = $this->commentsService->getComments('ticket', $params['id']);
|
||||
} else {
|
||||
$milestone = $this->ticketService->getNewMilestone();
|
||||
$comments = [];
|
||||
}
|
||||
|
||||
$allAssignedprojects = $this->projectService->getProjectsAssignedToUser(session('userdata.id'), 'open');
|
||||
$this->tpl->assign('allAssignedprojects', $allAssignedprojects);
|
||||
|
||||
$this->tpl->assign('statusLabels', $this->ticketService->getStatusLabels());
|
||||
$this->tpl->assign('comments', $comments);
|
||||
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestones(['sprint' => '', 'type' => 'milestone', 'currentProject' => session('currentProject')]);
|
||||
$this->tpl->assign('milestones', $allProjectMilestones);
|
||||
$this->tpl->assign('users', $this->projectService->getUsersWithAccessToProject((int) session('currentProject')));
|
||||
$this->tpl->assign('milestone', $milestone);
|
||||
|
||||
return $this->tpl->displayPartial('tickets.milestoneDialog');
|
||||
}
|
||||
|
||||
/**
|
||||
* post - handle post requests
|
||||
*/
|
||||
public function post($params)
|
||||
{
|
||||
// If ID is set its an update
|
||||
if (isset($_GET['id']) && (int) $_GET['id'] > 0) {
|
||||
$params['id'] = (int) $_GET['id'];
|
||||
|
||||
if (isset($params['comment']) === true) {
|
||||
$milestone = $this->ticketService->getMilestone($params['id']);
|
||||
|
||||
if ($this->ticketService->addMilestoneComment($params, $milestone)) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_added_successfully'), 'success');
|
||||
} else {
|
||||
$this->tpl->setNotification($this->language->__('notifications.problem_saving_your_comment'), 'error');
|
||||
}
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/editMilestone/'.$params['id']);
|
||||
}
|
||||
|
||||
if (isset($params['headline']) === true) {
|
||||
if ($this->ticketService->updateMilestoneFromDialog($params)) {
|
||||
$this->tpl->setNotification($this->language->__('notification.milestone_edited_successfully'), 'success');
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/editMilestone/'.$params['id'].'?closeModal=1');
|
||||
}
|
||||
|
||||
$this->tpl->setNotification($this->language->__('notification.saving_milestone_error'), 'error');
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/editMilestone/'.$params['id']);
|
||||
}
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/editMilestone/'.$params['id']);
|
||||
}
|
||||
|
||||
$result = $this->ticketService->createMilestoneFromDialog($params);
|
||||
|
||||
if (is_numeric($result)) {
|
||||
$this->tpl->setNotification($this->language->__('notification.milestone_created_successfully'), 'success');
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/editMilestone/'.$result.'?closeModal=1');
|
||||
}
|
||||
|
||||
$this->tpl->setNotification($this->language->__('notification.saving_milestone_error'), 'error');
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/editMilestone/');
|
||||
}
|
||||
|
||||
/**
|
||||
* put - handle put requests
|
||||
*/
|
||||
public function put($params) {}
|
||||
|
||||
/**
|
||||
* delete - handle delete requests
|
||||
*/
|
||||
public function delete($params) {}
|
||||
}
|
||||
66
app/Domain/Tickets/Controllers/MoveTicket.php
Normal file
66
app/Domain/Tickets/Controllers/MoveTicket.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Leantime\Core\Auth\Permissions\RequiresPermission;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller as FrontcontrollerCore;
|
||||
use Leantime\Domain\Projects\Services\Projects as ProjectService;
|
||||
use Leantime\Domain\Tickets\Permissions\TicketsPermissions;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class MoveTicket extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
private ProjectService $projectService;
|
||||
|
||||
public function init(
|
||||
TicketService $ticketService,
|
||||
ProjectService $projectService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
$this->projectService = $projectService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::EDIT)]
|
||||
public function get($params): Response
|
||||
{
|
||||
$ticketId = $params['id'] ?? '';
|
||||
|
||||
$ticket = $this->ticketService->getTicket($ticketId);
|
||||
|
||||
if (! $ticket) {
|
||||
return $this->tpl->displayPartial('errors.error404', responseCode: 404);
|
||||
}
|
||||
|
||||
$projects = $this->projectService->getProjectsAssignedToUser(session('userdata.id'));
|
||||
|
||||
$this->tpl->assign('ticket', $ticket);
|
||||
$this->tpl->assign('projects', $projects);
|
||||
|
||||
return $this->tpl->displayPartial('tickets.moveTicket');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::EDIT)]
|
||||
public function post($params): Response
|
||||
{
|
||||
if (! empty($ticketId = (int) $_GET['id']) && ! empty($projectId = (int) $params['projectId'])) {
|
||||
if ($this->ticketService->moveTicket($ticketId, $projectId)) {
|
||||
$this->tpl->setNotification($this->language->__('text.ticket_moved'), 'success');
|
||||
} else {
|
||||
$this->tpl->setNotification($this->language->__('text.move_problem'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
return FrontcontrollerCore::redirect(BASE_URL.'/tickets/moveTicket/'.$ticketId.'?closeModal=true');
|
||||
}
|
||||
}
|
||||
150
app/Domain/Tickets/Controllers/NewTicket.php
Normal file
150
app/Domain/Tickets/Controllers/NewTicket.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Leantime\Core\Auth\Permissions\RequiresPermission;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Core\Support\FromFormat;
|
||||
use Leantime\Domain\Projects\Services\Projects as ProjectService;
|
||||
use Leantime\Domain\Sprints\Services\Sprints as SprintService;
|
||||
use Leantime\Domain\Tickets\Models\Tickets as TicketModel;
|
||||
use Leantime\Domain\Tickets\Permissions\TicketsPermissions;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Leantime\Domain\Timesheets\Services\Timesheets as TimesheetService;
|
||||
use Leantime\Domain\Users\Services\Users as UserService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class NewTicket extends Controller
|
||||
{
|
||||
private ProjectService $projectService;
|
||||
|
||||
private TicketService $ticketService;
|
||||
|
||||
private SprintService $sprintService;
|
||||
|
||||
private TimesheetService $timesheetService;
|
||||
|
||||
private UserService $userService;
|
||||
|
||||
public function init(
|
||||
ProjectService $projectService,
|
||||
TicketService $ticketService,
|
||||
SprintService $sprintService,
|
||||
TimesheetService $timesheetService,
|
||||
UserService $userService
|
||||
): void {
|
||||
$this->projectService = $projectService;
|
||||
$this->ticketService = $ticketService;
|
||||
$this->sprintService = $sprintService;
|
||||
$this->timesheetService = $timesheetService;
|
||||
$this->userService = $userService;
|
||||
|
||||
if (! session()->exists('lastPage')) {
|
||||
session(['lastPage' => BASE_URL.'/tickets/showKanban/']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::CREATE)]
|
||||
public function get(): Response
|
||||
{
|
||||
$ticket = app()->make(TicketModel::class, [
|
||||
'values' => [
|
||||
'userLastname' => session('userdata.name'),
|
||||
'status' => 3,
|
||||
'projectId' => session('currentProject'),
|
||||
'sprint' => session('currentSprint') ?? '',
|
||||
'editorId' => session('userdata.id'),
|
||||
],
|
||||
]);
|
||||
|
||||
$ticket->date = dtHelper()->userNow();
|
||||
|
||||
$this->tpl->assign('ticket', $ticket);
|
||||
$this->tpl->assign('ticketParents', $this->ticketService->getAllPossibleParents($ticket));
|
||||
$this->tpl->assign('statusLabels', $this->ticketService->getStatusLabels());
|
||||
$this->tpl->assign('ticketTypes', $this->ticketService->getTicketTypes());
|
||||
$this->tpl->assign('efforts', $this->ticketService->getEffortLabels());
|
||||
$this->tpl->assign('priorities', $this->ticketService->getPriorityLabels());
|
||||
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestones(['sprint' => '', 'type' => 'milestone', 'currentProject' => session('currentProject')]);
|
||||
$this->tpl->assign('milestones', $allProjectMilestones);
|
||||
$this->tpl->assign('sprints', $this->sprintService->getAllSprints(session('currentProject')));
|
||||
|
||||
$this->tpl->assign('kind', $this->timesheetService->getLoggableHourTypes());
|
||||
$this->tpl->assign('ticketHours', 0);
|
||||
$this->tpl->assign('userHours', 0);
|
||||
|
||||
$this->tpl->assign('timesheetsAllHours', 0);
|
||||
$this->tpl->assign('remainingHours', 0);
|
||||
|
||||
$this->tpl->assign('userInfo', $this->userService->getUser(session('userdata.id')));
|
||||
$this->tpl->assign('users', $this->projectService->getUsersWithAccessToProject((int) session('currentProject')));
|
||||
|
||||
$allAssignedprojects = $this->projectService->getProjectsUserHasAccessTo(session('userdata.id'));
|
||||
$this->tpl->assign('allAssignedprojects', $allAssignedprojects);
|
||||
|
||||
return $this->tpl->displayPartial('tickets.newTicketModal');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
#[RequiresPermission(TicketsPermissions::CREATE)]
|
||||
public function post($params): Response
|
||||
{
|
||||
if (isset($params['saveTicket']) || isset($params['saveAndCloseTicket'])) {
|
||||
|
||||
$params['timeToFinish'] = format(value: $params['timeToFinish'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
|
||||
$params['timeFrom'] = format(value: $params['timeFrom'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
|
||||
$params['timeTo'] = format(value: $params['timeTo'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
|
||||
|
||||
$result = $this->ticketService->addTicket($params);
|
||||
|
||||
if (is_array($result) === false) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.ticket_saved'), 'success');
|
||||
|
||||
if (isset($params['saveAndCloseTicket']) === true && $params['saveAndCloseTicket'] == 1) {
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$result.'?closeModal=1');
|
||||
} else {
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$result);
|
||||
}
|
||||
} else {
|
||||
$this->tpl->setNotification($this->language->__($result['msg']), 'error');
|
||||
|
||||
$ticket = app()->makeWith(TicketModel::class, ['values' => $params]);
|
||||
$ticket->userLastname = session('userdata.name');
|
||||
|
||||
$this->tpl->assign('ticket', $ticket);
|
||||
$this->tpl->assign('ticketParents', $this->ticketService->getAllPossibleParents($ticket));
|
||||
$this->tpl->assign('statusLabels', $this->ticketService->getStatusLabels());
|
||||
$this->tpl->assign('ticketTypes', $this->ticketService->getTicketTypes());
|
||||
$this->tpl->assign('efforts', $this->ticketService->getEffortLabels());
|
||||
$this->tpl->assign('priorities', $this->ticketService->getPriorityLabels());
|
||||
$this->tpl->assign('milestones', $this->ticketService->getAllMilestones(['sprint' => '', 'type' => 'milestone', 'currentProject' => session('currentProject')]));
|
||||
$this->tpl->assign('sprints', $this->sprintService->getAllSprints(session('currentProject')));
|
||||
|
||||
$this->tpl->assign('kind', $this->timesheetService->getLoggableHourTypes());
|
||||
$this->tpl->assign('ticketHours', 0);
|
||||
$this->tpl->assign('userHours', 0);
|
||||
|
||||
$this->tpl->assign('timesheetsAllHours', 0);
|
||||
$this->tpl->assign('remainingHours', 0);
|
||||
|
||||
$this->tpl->assign('userInfo', $this->userService->getUser(session('userdata.id')));
|
||||
$this->tpl->assign('users', $this->projectService->getUsersWithAccessToProject((int) session('currentProject')));
|
||||
|
||||
$allAssignedprojects = $this->projectService->getProjectsUserHasAccessTo(session('userdata.id'));
|
||||
$this->tpl->assign('allAssignedprojects', $allAssignedprojects);
|
||||
|
||||
return $this->tpl->displayPartial('tickets.newTicketModal');
|
||||
}
|
||||
}
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/newTicket');
|
||||
}
|
||||
}
|
||||
63
app/Domain/Tickets/Controllers/Roadmap.php
Normal file
63
app/Domain/Tickets/Controllers/Roadmap.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
|
||||
class Roadmap extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
/**
|
||||
* init - initialize private variables
|
||||
*/
|
||||
public function init(
|
||||
TicketService $ticketService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
|
||||
session(['lastPage' => CURRENT_URL]);
|
||||
session(['lastMilestoneView' => 'timeline']);
|
||||
session(['lastFilterdMilestonesView' => CURRENT_URL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* get - handle get requests
|
||||
*/
|
||||
public function get($params)
|
||||
{
|
||||
$params = $this->ticketService->normalizeRoadmapParams($params);
|
||||
|
||||
// Sets the filter module to show a quick toggle for task types
|
||||
$this->tpl->assign('enableTaskTypeToggle', true);
|
||||
$this->tpl->assign('showTasks', $params['showTasks'] ?? 'false');
|
||||
|
||||
$template_assignments = $this->ticketService->getTicketTemplateAssignments($params);
|
||||
|
||||
array_map([$this->tpl, 'assign'], array_keys($template_assignments), array_values($template_assignments));
|
||||
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestones($template_assignments['searchCriteria'], 'standard');
|
||||
$allProjectMilestones = $this->ticketService->getBulkMilestoneProgress($allProjectMilestones);
|
||||
|
||||
$this->tpl->assign('timelineTasks', $allProjectMilestones);
|
||||
|
||||
return $this->tpl->display('tickets.roadmap');
|
||||
}
|
||||
|
||||
/**
|
||||
* post - handle post requests
|
||||
*/
|
||||
public function post($params)
|
||||
{
|
||||
|
||||
$template_assignments = $this->ticketService->getTicketTemplateAssignments($params);
|
||||
array_map([$this->tpl, 'assign'], array_keys($template_assignments), array_values($template_assignments));
|
||||
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestones($template_assignments['searchCriteria']);
|
||||
|
||||
$this->tpl->assign('timelineTasks', $allProjectMilestones);
|
||||
|
||||
return $this->tpl->display('tickets.roadmap');
|
||||
}
|
||||
}
|
||||
62
app/Domain/Tickets/Controllers/RoadmapAll.php
Normal file
62
app/Domain/Tickets/Controllers/RoadmapAll.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Domain\Clients\Services\Clients as ClientService;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
|
||||
class RoadmapAll extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
private ClientService $clientService;
|
||||
|
||||
/**
|
||||
* init - initialize private variables
|
||||
*/
|
||||
public function init(
|
||||
ClientService $clientService,
|
||||
TicketService $ticketService
|
||||
): void {
|
||||
$this->clientService = $clientService;
|
||||
$this->ticketService = $ticketService;
|
||||
}
|
||||
|
||||
/**
|
||||
* get - handle get requests
|
||||
*/
|
||||
public function get($params)
|
||||
{
|
||||
$clientId = 0;
|
||||
$currentClientName = '';
|
||||
if (isset($_GET['client']) === true && $_GET['client'] != '') {
|
||||
$clientId = (int) $_GET['client'];
|
||||
$currentClientName = $this->ticketService->getClientNameById($clientId);
|
||||
}
|
||||
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestonesOverview(false, 'date', false, $clientId);
|
||||
|
||||
$allClients = $this->clientService->getUserClients(session('userdata.id'));
|
||||
|
||||
$this->tpl->assign('currentClientName', $currentClientName);
|
||||
$this->tpl->assign('currentClient', $clientId);
|
||||
|
||||
$this->tpl->assign('milestones', $allProjectMilestones);
|
||||
$this->tpl->assign('clients', $allClients);
|
||||
|
||||
return $this->tpl->display('tickets.roadmapAll');
|
||||
}
|
||||
|
||||
/**
|
||||
* post - handle post requests
|
||||
*/
|
||||
public function post($params)
|
||||
{
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestonesOverview();
|
||||
|
||||
$this->tpl->assign('milestones', $allProjectMilestones);
|
||||
|
||||
return $this->tpl->display('tickets.roadmapAll');
|
||||
}
|
||||
}
|
||||
39
app/Domain/Tickets/Controllers/ShowAll.php
Normal file
39
app/Domain/Tickets/Controllers/ShowAll.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShowAll extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
public function init(
|
||||
TicketService $ticketService,
|
||||
): void {
|
||||
|
||||
$this->ticketService = $ticketService;
|
||||
|
||||
session(['lastPage' => CURRENT_URL]);
|
||||
session(['lastTicketView' => 'table']);
|
||||
session(['lastFilterdTicketTableView' => CURRENT_URL]);
|
||||
|
||||
if (! session()->exists('currentProjectName')) {
|
||||
Frontcontroller::redirect(BASE_URL.'/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($params): Response
|
||||
{
|
||||
$template_assignments = $this->ticketService->getTicketTemplateAssignments($params);
|
||||
array_map([$this->tpl, 'assign'], array_keys($template_assignments), array_values($template_assignments));
|
||||
|
||||
return $this->tpl->display('tickets.showAll');
|
||||
}
|
||||
}
|
||||
40
app/Domain/Tickets/Controllers/ShowAllMilestones.php
Normal file
40
app/Domain/Tickets/Controllers/ShowAllMilestones.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShowAllMilestones extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
public function init(
|
||||
TicketService $ticketService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
|
||||
session(['lastPage' => CURRENT_URL]);
|
||||
session(['lastMilestoneView' => 'milestonetable']);
|
||||
session(['lastFilterdMilestoneView' => CURRENT_URL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($params): Response
|
||||
{
|
||||
|
||||
$params = $this->ticketService->normalizeRoadmapParams($params);
|
||||
|
||||
// Sets the filter module to show a quick toggle for task types
|
||||
$this->tpl->assign('enableTaskTypeToggle', true);
|
||||
$this->tpl->assign('showTasks', $params['showTasks'] ?? 'false');
|
||||
|
||||
$template_assignments = $this->ticketService->getTicketTemplateAssignments($params);
|
||||
array_map([$this->tpl, 'assign'], array_keys($template_assignments), array_values($template_assignments));
|
||||
|
||||
return $this->tpl->display('tickets.showAllMilestones');
|
||||
}
|
||||
}
|
||||
71
app/Domain/Tickets/Controllers/ShowAllMilestonesOverview.php
Normal file
71
app/Domain/Tickets/Controllers/ShowAllMilestonesOverview.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Domain\Clients\Services\Clients as ClientService;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Leantime\Domain\Users\Services\Users as UserService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShowAllMilestonesOverview extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
private UserService $userService;
|
||||
|
||||
private ClientService $clientService;
|
||||
|
||||
public function init(
|
||||
TicketService $ticketService,
|
||||
UserService $userService,
|
||||
ClientService $clientService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
$this->userService = $userService;
|
||||
$this->clientService = $clientService;
|
||||
|
||||
session(['lastPage' => CURRENT_URL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($params): Response
|
||||
{
|
||||
$clientId = 0;
|
||||
$currentClientName = '';
|
||||
if (isset($_GET['client']) === true && $_GET['client'] != '') {
|
||||
$clientId = (int) $_GET['client'];
|
||||
$currentClientName = $this->ticketService->getClientNameById($clientId);
|
||||
}
|
||||
|
||||
$searchCriteria = $this->ticketService->getMilestonesOverviewSearchCriteria($params);
|
||||
|
||||
$this->tpl->assign('allTickets', $this->ticketService->getAllMilestonesOverview(false, 'duedate', false, $clientId, $searchCriteria));
|
||||
$this->tpl->assign('allTicketStates', $this->ticketService->getStatusLabels());
|
||||
$this->tpl->assign('efforts', $this->ticketService->getEffortLabels());
|
||||
$this->tpl->assign('priorities', $this->ticketService->getPriorityLabels());
|
||||
|
||||
$this->tpl->assign('ticketTypeIcons', $this->ticketService->getTypeIcons());
|
||||
|
||||
$this->tpl->assign('searchCriteria', $searchCriteria);
|
||||
$this->tpl->assign('numOfFilters', $this->ticketService->countSetFilters($searchCriteria));
|
||||
|
||||
$allClients = $this->clientService->getUserClients(session('userdata.id'));
|
||||
|
||||
$this->tpl->assign('clients', $allClients);
|
||||
$this->tpl->assign('currentClientName', $currentClientName);
|
||||
$this->tpl->assign('currentClient', $clientId);
|
||||
|
||||
$this->tpl->assign('users', $this->userService->getAll());
|
||||
$this->tpl->assign('milestones', $this->ticketService->getAllMilestones([
|
||||
'sprint' => '',
|
||||
'type' => 'milestone',
|
||||
'currentProject' => session('currentProject'),
|
||||
]));
|
||||
$this->tpl->assign('types', $this->ticketService->getTicketTypes());
|
||||
|
||||
return $this->tpl->display('tickets.showAllMilestonesOverview');
|
||||
}
|
||||
}
|
||||
85
app/Domain/Tickets/Controllers/ShowKanban.php
Normal file
85
app/Domain/Tickets/Controllers/ShowKanban.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShowKanban extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
public function init(
|
||||
TicketService $ticketService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
|
||||
session(['lastPage' => CURRENT_URL]);
|
||||
session(['lastTicketView' => 'kanban']);
|
||||
session(['lastFilterdTicketKanbanView' => CURRENT_URL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get(array $params): Response
|
||||
{
|
||||
// Status groupBy is redundant on Kanban (status already shown as columns)
|
||||
// Auto-reset to "all" (no grouping) for cleaner default view
|
||||
if (isset($params['groupBy']) && $params['groupBy'] === 'status') {
|
||||
$params['groupBy'] = 'all';
|
||||
}
|
||||
|
||||
$template_assignments = $this->ticketService->getTicketTemplateAssignments($params);
|
||||
$allKanbanColumns = $this->ticketService->getKanbanColumns();
|
||||
|
||||
// NEW: Calculate status breakdown for swimlane visualizations
|
||||
$statusBreakdown = $this->ticketService->getStatusBreakdownBySwimlane(
|
||||
$template_assignments['allTickets'],
|
||||
$allKanbanColumns
|
||||
);
|
||||
|
||||
array_map([$this->tpl, 'assign'], array_keys($template_assignments), array_values($template_assignments));
|
||||
$this->tpl->assign('allKanbanColumns', $allKanbanColumns);
|
||||
$this->tpl->assign('statusBreakdown', $statusBreakdown);
|
||||
|
||||
return $this->tpl->display('tickets.showKanban');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function post(array $params): Response
|
||||
{
|
||||
// QuickAdd
|
||||
if (isset($_POST['quickadd']) && $_POST['quickadd'] == 1) {
|
||||
$formParams = [
|
||||
'headline' => $_POST['headline'] ?? '',
|
||||
'status' => $_POST['status'] ?? '',
|
||||
'milestone' => $_POST['milestone'] ?? '',
|
||||
'sprint' => $_POST['sprint'] ?? '',
|
||||
'projectId' => session('currentProject'),
|
||||
'editorId' => session('userdata.id'),
|
||||
];
|
||||
|
||||
$swimlaneValue = $_POST['swimlane'] ?? null;
|
||||
$groupBy = $_POST['groupBy'] ?? null;
|
||||
$stayOpen = isset($_POST['stay_open']) && $_POST['stay_open'] === '1';
|
||||
|
||||
$result = $this->ticketService->quickAddTicketFromKanban($formParams, $swimlaneValue, $groupBy, $stayOpen);
|
||||
|
||||
if ($result['success'] === false) {
|
||||
$this->tpl->setNotification($result['message'], 'error');
|
||||
} else {
|
||||
$this->tpl->setNotification('Task created: '.htmlspecialchars($result['headline']), 'success');
|
||||
}
|
||||
|
||||
return Frontcontroller::redirect(CURRENT_URL.'#status-'.$result['status']);
|
||||
}
|
||||
|
||||
return Frontcontroller::redirect(CURRENT_URL);
|
||||
}
|
||||
}
|
||||
60
app/Domain/Tickets/Controllers/ShowList.php
Normal file
60
app/Domain/Tickets/Controllers/ShowList.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShowList extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
public function init(
|
||||
TicketService $ticketService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
|
||||
session(['lastPage' => CURRENT_URL]);
|
||||
session(['lastTicketView' => 'list']);
|
||||
session(['lastFilterdTicketListView' => CURRENT_URL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($params): Response
|
||||
{
|
||||
$template_assignments = $this->ticketService->getTicketTemplateAssignments($params);
|
||||
array_map([$this->tpl, 'assign'], array_keys($template_assignments), array_values($template_assignments));
|
||||
|
||||
return $this->tpl->display('tickets.showList');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function post(array $params): Response
|
||||
{
|
||||
// QuickAdd
|
||||
if (isset($_POST['quickadd'])) {
|
||||
$formParams = [
|
||||
'headline' => $_POST['headline'] ?? '',
|
||||
'milestone' => $_POST['milestone'] ?? '',
|
||||
'sprint' => $_POST['sprint'] ?? '',
|
||||
'projectId' => session('currentProject'),
|
||||
'editorId' => session('userdata.id'),
|
||||
];
|
||||
|
||||
$result = $this->ticketService->quickAddTicket($formParams);
|
||||
|
||||
if (is_array($result)) {
|
||||
$this->tpl->setNotification($result['message'], $result['status']);
|
||||
}
|
||||
}
|
||||
|
||||
return Frontcontroller::redirect(CURRENT_URL);
|
||||
}
|
||||
}
|
||||
53
app/Domain/Tickets/Controllers/ShowProjectCalendar.php
Normal file
53
app/Domain/Tickets/Controllers/ShowProjectCalendar.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* showAll Class - show My Calender
|
||||
*/
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
|
||||
class ShowProjectCalendar extends Controller
|
||||
{
|
||||
private TicketService $ticketService;
|
||||
|
||||
/**
|
||||
* init - initialize private variables
|
||||
*/
|
||||
public function init(
|
||||
TicketService $ticketService
|
||||
): void {
|
||||
$this->ticketService = $ticketService;
|
||||
|
||||
session(['lastPage' => CURRENT_URL]);
|
||||
session(['lastMilestoneView' => 'calendar']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get - handle get requests
|
||||
*/
|
||||
public function get($params)
|
||||
{
|
||||
$template_assignments = $this->ticketService->getTicketTemplateAssignments($params);
|
||||
array_map([$this->tpl, 'assign'], array_keys($template_assignments), array_values($template_assignments));
|
||||
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestones($template_assignments['searchCriteria']);
|
||||
$this->tpl->assign('milestones', $allProjectMilestones);
|
||||
|
||||
return $this->tpl->display('tickets.calendar');
|
||||
}
|
||||
|
||||
/**
|
||||
* post - handle post requests
|
||||
*/
|
||||
public function post($params)
|
||||
{
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestones(['sprint' => '', 'type' => 'milestone', 'currentProject' => session('currentProject')]);
|
||||
$this->tpl->assign('milestones', $allProjectMilestones);
|
||||
|
||||
return $this->tpl->display('tickets.roadmap');
|
||||
}
|
||||
}
|
||||
267
app/Domain/Tickets/Controllers/ShowTicket.php
Normal file
267
app/Domain/Tickets/Controllers/ShowTicket.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Core\Support\FromFormat;
|
||||
use Leantime\Domain\Comments\Services\Comments as CommentService;
|
||||
use Leantime\Domain\Files\Services\Files as FileService;
|
||||
use Leantime\Domain\Projects\Services\Projects as ProjectService;
|
||||
use Leantime\Domain\Sprints\Services\Sprints as SprintService;
|
||||
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
|
||||
use Leantime\Domain\Tickets\Services\TicketResource as TicketResourceService;
|
||||
use Leantime\Domain\Timesheets\Services\Timesheets as TimesheetService;
|
||||
use Leantime\Domain\Users\Services\Users as UserService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShowTicket extends Controller
|
||||
{
|
||||
private ProjectService $projectService;
|
||||
|
||||
private TicketService $ticketService;
|
||||
|
||||
private SprintService $sprintService;
|
||||
|
||||
private FileService $fileService;
|
||||
|
||||
private CommentService $commentService;
|
||||
|
||||
private TimesheetService $timesheetService;
|
||||
|
||||
private UserService $userService;
|
||||
|
||||
private TicketResourceService $ticketResourceService;
|
||||
|
||||
public function init(
|
||||
ProjectService $projectService,
|
||||
TicketService $ticketService,
|
||||
SprintService $sprintService,
|
||||
FileService $fileService,
|
||||
CommentService $commentService,
|
||||
TimesheetService $timesheetService,
|
||||
UserService $userService,
|
||||
TicketResourceService $ticketResourceService
|
||||
): void {
|
||||
$this->projectService = $projectService;
|
||||
$this->ticketService = $ticketService;
|
||||
$this->sprintService = $sprintService;
|
||||
$this->fileService = $fileService;
|
||||
$this->commentService = $commentService;
|
||||
$this->timesheetService = $timesheetService;
|
||||
$this->userService = $userService;
|
||||
$this->ticketResourceService = $ticketResourceService;
|
||||
|
||||
if (session()->exists('lastPage') === false) {
|
||||
session(['lastPage' => BASE_URL.'/tickets/showKanban']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function get($params): Response
|
||||
{
|
||||
if (! isset($params['id'])) {
|
||||
return $this->tpl->displayPartial('errors.error400', responseCode: 400);
|
||||
}
|
||||
|
||||
$id = (int) ($params['id']);
|
||||
$ticket = $this->ticketService->getTicket($id);
|
||||
|
||||
if ($ticket === false) {
|
||||
return $this->tpl->display('errors.error500', responseCode: 500);
|
||||
}
|
||||
|
||||
// Ensure this ticket belongs to the current project
|
||||
if (session('currentProject') != $ticket->projectId) {
|
||||
$this->projectService->changeCurrentSessionProject($ticket->projectId);
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id);
|
||||
}
|
||||
|
||||
// Delete file
|
||||
if (isset($params['delFile']) === true) {
|
||||
if ($result = $this->fileService->deleteFile($params['delFile'])) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.file_deleted'), 'success');
|
||||
|
||||
return Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id.'#files');
|
||||
}
|
||||
|
||||
$this->tpl->setNotification($this->language->__('notifications.file_deleted_error'), 'error');
|
||||
}
|
||||
|
||||
// Delete comment
|
||||
if (isset($params['delComment']) === true) {
|
||||
$commentId = (int) ($params['delComment']);
|
||||
|
||||
if ($this->commentService->deleteComment($commentId)) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_deleted'), 'success');
|
||||
$response = Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id);
|
||||
$response->headers->set('HX-Trigger', 'ticketUpdate');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_deleted_error'), 'error');
|
||||
}
|
||||
// Delete Subtask
|
||||
if (isset($params['delSubtask']) === true) {
|
||||
|
||||
}
|
||||
|
||||
$this->tpl->assign('ticket', $ticket);
|
||||
$this->tpl->assign('ticketParents', $this->ticketService->getAllPossibleParents($ticket));
|
||||
$this->tpl->assign('statusLabels', $this->ticketService->getStatusLabels());
|
||||
$this->tpl->assign('ticketTypes', $this->ticketService->getTicketTypes());
|
||||
$this->tpl->assign('ticketTypeIcons', $this->ticketService->getTypeIcons());
|
||||
$this->tpl->assign('efforts', $this->ticketService->getEffortLabels());
|
||||
$this->tpl->assign('priorities', $this->ticketService->getPriorityLabels());
|
||||
|
||||
$allProjectMilestones = $this->ticketService->getAllMilestones([
|
||||
'sprint' => '',
|
||||
'type' => 'milestone',
|
||||
'currentProject' => session('currentProject'),
|
||||
]);
|
||||
$this->tpl->assign('milestones', $allProjectMilestones);
|
||||
$this->tpl->assign('sprints', $this->sprintService->getAllSprints(session('currentProject')));
|
||||
|
||||
$this->tpl->assign('kind', $this->timesheetService->getLoggableHourTypes());
|
||||
$this->tpl->assign('ticketHours', $this->timesheetService->getLoggedHoursForTicketByDate($id));
|
||||
$this->tpl->assign('userHours', $this->timesheetService->getUsersTicketHours($id, session('userdata.id')));
|
||||
|
||||
$this->tpl->assign('timesheetsAllHours', $this->timesheetService->getSumLoggedHoursForTicket($id));
|
||||
$this->tpl->assign('remainingHours', $this->timesheetService->getRemainingHours($ticket));
|
||||
|
||||
$this->tpl->assign('userInfo', $this->userService->getUser(session('userdata.id')));
|
||||
$this->tpl->assign('users', $this->projectService->getUsersWithAccessToProject((int) $ticket->projectId));
|
||||
|
||||
$projectData = $this->projectService->getProject($ticket->projectId);
|
||||
$this->tpl->assign('projectData', $projectData);
|
||||
|
||||
$comments = $this->commentService->getComments('ticket', $id);
|
||||
|
||||
$this->tpl->assign('numComments', count($comments));
|
||||
$this->tpl->assign('comments', $comments);
|
||||
|
||||
$files = $this->fileService->getFilesByModule('ticket', $id);
|
||||
$this->tpl->assign('numFiles', count($files));
|
||||
$this->tpl->assign('files', $files);
|
||||
|
||||
$this->tpl->assign('onTheClock', $this->timesheetService->isClocked(session('userdata.id')));
|
||||
|
||||
$this->tpl->assign('timesheetValues', [
|
||||
'kind' => '',
|
||||
'date' => Carbon::now(session('usersettings.timezone'))->setTimezone('UTC'),
|
||||
'hours' => '',
|
||||
'description' => '',
|
||||
]);
|
||||
|
||||
// TODO: Refactor thumbnail generation in file manager
|
||||
$this->tpl->assign('imgExtensions', ['jpg', 'jpeg', 'png', 'gif', 'psd', 'bmp', 'tif', 'thm', 'yuv']);
|
||||
|
||||
$allAssignedprojects = $this->projectService->getProjectsUserHasAccessTo(session('userdata.id'));
|
||||
$this->tpl->assign('allAssignedprojects', $allAssignedprojects);
|
||||
|
||||
// 关联资源(BOM/工艺文件/工具清单/文件/wiki)
|
||||
$this->tpl->assign('linkedResources', $this->ticketResourceService->getLinkedResources($id));
|
||||
$this->tpl->assign('resourceCandidates', $this->ticketResourceService->listCandidates((int) $ticket->projectId));
|
||||
|
||||
$response = $this->tpl->displayPartial('tickets.showTicketModal');
|
||||
$response->headers->set('HX-Trigger', 'ticketUpdate');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function post($params): Response
|
||||
{
|
||||
if (! isset($_GET['id'])) {
|
||||
return $this->tpl->display('errors.error400', responseCode: 400);
|
||||
}
|
||||
|
||||
$tab = '';
|
||||
$id = (int) ($_GET['id']);
|
||||
$ticket = $this->ticketService->getTicket($id);
|
||||
|
||||
if ($ticket === false) {
|
||||
return $this->tpl->display('errors.error500', responseCode: 500);
|
||||
}
|
||||
|
||||
// Upload File
|
||||
if (isset($params['upload'])) {
|
||||
if ($this->fileService->upload($_FILES, 'ticket', $id, $ticket)) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.file_upload_success'), 'success');
|
||||
} else {
|
||||
$this->tpl->setNotification($this->language->__('notifications.file_upload_error'), 'error');
|
||||
}
|
||||
|
||||
$tab = '#files';
|
||||
}
|
||||
|
||||
// Add or edit a comment
|
||||
if (isset($params['comment']) === true && isset($params['text']) && $params['text'] != '' && isset($params['edit-comment-helper']) && $params['edit-comment-helper'] !== '') {
|
||||
if ($this->commentService->editComment($_POST, (int) $params['edit-comment-helper'])) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_edited_success'), 'success');
|
||||
} else {
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_edit_error'), 'error');
|
||||
}
|
||||
$tab = '#comment';
|
||||
} elseif (isset($params['comment']) === true && isset($params['text']) && $params['text'] != '') {
|
||||
if ($this->commentService->addComment($_POST, 'ticket', $id, $ticket)) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_create_success'), 'success');
|
||||
} else {
|
||||
$this->tpl->setNotification($this->language->__('notifications.comment_create_error'), 'error');
|
||||
}
|
||||
|
||||
$tab = '#comment';
|
||||
}
|
||||
|
||||
// Log time
|
||||
if (isset($params['saveTimes']) === true) {
|
||||
|
||||
try {
|
||||
$result = $this->timesheetService->logTime($id, $params);
|
||||
$this->tpl->setNotification($this->language->__('notifications.time_logged_success'), 'success');
|
||||
} catch (\Exception $e) {
|
||||
$this->tpl->setNotification($e->getMessage(), 'error');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Save Ticket
|
||||
if (isset($params['saveTicket']) === true || isset($params['saveAndCloseTicket']) === true) {
|
||||
// $params['projectId'] = $ticket->projectId;
|
||||
$params['id'] = $id;
|
||||
|
||||
// Prepare values, time comes in as 24hours from time input. Service expects time to be in local user format
|
||||
$params['timeToFinish'] = format(value: $params['timeToFinish'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
|
||||
$params['timeFrom'] = format(value: $params['timeFrom'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
|
||||
$params['timeTo'] = format(value: $params['timeTo'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
|
||||
|
||||
$result = $this->ticketService->updateTicket($params);
|
||||
|
||||
if ($result === true) {
|
||||
$this->tpl->setNotification($this->language->__('notifications.ticket_saved'), 'success');
|
||||
} else {
|
||||
$this->tpl->setNotification($this->language->__($result['msg']), 'error');
|
||||
}
|
||||
|
||||
if (isset($params['saveAndCloseTicket']) === true && $params['saveAndCloseTicket'] == 1) {
|
||||
$response = Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id.'?closeModal=1');
|
||||
$response->headers->set('HX-Trigger', 'ticketUpdate');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
$response = Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id.''.$tab);
|
||||
$response->headers->set('HX-Trigger', 'ticketUpdate');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
60
app/Domain/Tickets/Controllers/TicketResourceApi.php
Normal file
60
app/Domain/Tickets/Controllers/TicketResourceApi.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Leantime\Domain\Tickets\Services\TicketResource as TicketResourceService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* 待办事项 ↔ 资源关联(BOM/工艺文件/工具清单/文件/wiki)JSON API。
|
||||
* 每个动作都在 Service 层按 ticket 所属项目自鉴权。
|
||||
*/
|
||||
class TicketResourceApi
|
||||
{
|
||||
public function __construct(
|
||||
private TicketResourceService $service,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 关联一个资源:POST /tickets/resource-api/{ticketId}/link
|
||||
*/
|
||||
public function link(int $ticketId, Request $request): Response
|
||||
{
|
||||
$type = (string) $request->input('type', '');
|
||||
$resourceId = (int) $request->input('resourceId', 0);
|
||||
$result = $this->service->link($ticketId, $type, $resourceId);
|
||||
|
||||
return $result['success']
|
||||
? response()->json(['status' => 'success'])
|
||||
: response()->json(['status' => 'error', 'message' => $result['message'] ?? '关联失败'], 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除一个资源关联:DELETE /tickets/resource-api/{ticketId}/unlink/{type}/{resourceId}
|
||||
*/
|
||||
public function unlink(int $ticketId, string $type, int $resourceId): Response
|
||||
{
|
||||
$result = $this->service->unlink($ticketId, $type, $resourceId);
|
||||
|
||||
return $result['success']
|
||||
? response()->json(['status' => 'success'])
|
||||
: response()->json(['status' => 'error', 'message' => $result['message'] ?? '解除失败'], 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* 候选资源列表:GET /tickets/resource-api/{projectId}/candidates
|
||||
*/
|
||||
public function candidates(int $projectId): Response
|
||||
{
|
||||
return response()->json(['status' => 'success', 'data' => $this->service->listCandidates($projectId)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 已关联资源:GET /tickets/resource-api/{ticketId}/links
|
||||
*/
|
||||
public function links(int $ticketId): Response
|
||||
{
|
||||
return response()->json(['status' => 'success', 'data' => $this->service->getLinkedResources($ticketId)]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user