OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)

This commit is contained in:
wangruiguo
2026-09-03 18:49:20 +08:00
commit d647428529
3501 changed files with 1988906 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
<?php
namespace Leantime\Domain\Ideas\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Ideas\Permissions\IdeasPermissions;
use Leantime\Domain\Ideas\Services\Ideas as IdeaService;
use Leantime\Domain\Projects\Services\Projects as ProjectService;
use Symfony\Component\HttpFoundation\Response;
class AdvancedBoards extends Controller
{
private ProjectService $projectService;
private IdeaService $ideaService;
/**
* Initializes dependencies.
*/
public function init(
IdeaService $ideaService,
ProjectService $projectService
): void {
$this->ideaService = $ideaService;
$this->projectService = $projectService;
session(['lastPage' => CURRENT_URL]);
session(['lastIdeaView' => 'kanban']);
}
/**
* Displays the advanced (kanban) idea boards view.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function get(array $params): Response
{
$allCanvas = $this->ideaService->getAllBoards(session('currentProject'));
$currentCanvasId = $this->resolveCurrentCanvasId($allCanvas, $params);
$this->assignTemplateVars($currentCanvasId, $allCanvas);
if (! isset($_GET['raw'])) {
return $this->tpl->display('ideas.advancedBoards');
}
return new Response;
}
/**
* Handles idea board mutations (create, edit, search).
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function post(array $params): Response
{
$allCanvas = $this->ideaService->getAllBoards(session('currentProject'));
$currentCanvasId = $this->resolveCurrentCanvasId($allCanvas, $params);
if (isset($_POST['searchCanvas'])) {
$currentCanvasId = (int) $_POST['searchCanvas'];
session(['currentIdeaCanvas' => $currentCanvasId]);
}
if (isset($_POST['newCanvas'])) {
$result = $this->handleNewCanvas($currentCanvasId, $allCanvas);
if ($result !== null) {
return $result;
}
}
if (isset($_POST['editCanvas']) && $currentCanvasId > 0) {
$result = $this->handleEditCanvas($currentCanvasId);
if ($result !== null) {
return $result;
}
}
$this->assignTemplateVars($currentCanvasId, $allCanvas);
if (! isset($_GET['raw'])) {
return $this->tpl->display('ideas.advancedBoards');
}
return new Response;
}
/**
* Resolves the current canvas ID from session or request parameters.
*/
private function resolveCurrentCanvasId(array $allCanvas, array $params): int
{
if (session()->exists('currentIdeaCanvas')) {
$currentCanvasId = session('currentIdeaCanvas');
} else {
$currentCanvasId = -1;
session(['currentIdeaCanvas' => '']);
}
if (count($allCanvas) > 0 && session('currentIdeaCanvas') == '') {
$currentCanvasId = $allCanvas[0]['id'];
session(['currentIdeaCanvas' => $currentCanvasId]);
}
if (isset($params['id'])) {
$currentCanvasId = (int) $params['id'];
session(['currentIdeaCanvas' => $currentCanvasId]);
}
return $currentCanvasId;
}
/**
* Handles creating a new idea board.
*/
private function handleNewCanvas(int &$currentCanvasId, array &$allCanvas): ?Response
{
if (! isset($_POST['canvastitle']) || empty($_POST['canvastitle'])) {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return null;
}
$currentCanvasId = $this->ideaService->createBoard(
$_POST['canvastitle'],
(int) session('currentProject'),
(int) session('userdata.id')
);
$allCanvas = $this->ideaService->getAllBoards(session('currentProject'));
$this->tpl->setNotification($this->language->__('notification.idea_board_created'), 'success', 'ideaboard_created');
session(['currentIdeaCanvas' => $currentCanvasId]);
return Frontcontroller::redirect(BASE_URL.'/ideas/advancedBoards/');
}
/**
* Handles editing an idea board title.
*/
private function handleEditCanvas(int &$currentCanvasId): ?Response
{
if (! isset($_POST['canvastitle']) || empty($_POST['canvastitle'])) {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return null;
}
$currentCanvasId = $this->ideaService->updateBoard($currentCanvasId, $_POST['canvastitle']);
$this->tpl->setNotification($this->language->__('notification.board_edited'), 'success', 'ideaboard_edited');
return Frontcontroller::redirect(BASE_URL.'/ideas/advancedBoards/');
}
/**
* Assigns common template variables.
*/
private function assignTemplateVars(int $currentCanvasId, array $allCanvas): void
{
$this->tpl->assign('currentCanvas', $currentCanvasId);
$this->tpl->assign('users', $this->projectService->getUsersAssignedToProject(session('currentProject')));
$this->tpl->assign('allCanvas', $allCanvas);
$this->tpl->assign('canvasItems', $this->ideaService->getBoardItems($currentCanvasId));
$this->tpl->assign('canvasLabels', $this->ideaService->getBoardLabels());
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace Leantime\Domain\Ideas\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Ideas\Permissions\IdeasPermissions;
use Leantime\Domain\Ideas\Services\Ideas as IdeaService;
use Leantime\Domain\Projects\Services\Projects as ProjectService;
use Symfony\Component\HttpFoundation\Response;
class BoardDialog extends Controller
{
/**
* Constant that must be redefined.
*/
protected const CANVAS_NAME = '??';
private ProjectService $projectService;
private IdeaService $ideaService;
/**
* Initializes dependencies.
*/
public function init(ProjectService $projectService, IdeaService $ideaService): void
{
$this->projectService = $projectService;
$this->ideaService = $ideaService;
}
/**
* Displays the board dialog form.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function get(array $params): Response
{
$currentCanvasId = '';
$canvasTitle = '';
if (isset($params['id'])) {
$currentCanvasId = (int) $params['id'];
$canvasTitle = $this->ideaService->getBoardTitle($currentCanvasId);
session(['current'.strtoupper(static::CANVAS_NAME).'Canvas' => $currentCanvasId]);
}
$this->tpl->assign('canvasTitle', $canvasTitle);
$this->tpl->assign('currentCanvas', $currentCanvasId);
$this->tpl->assign('canvasname', 'idea');
$this->tpl->assign('users', $this->projectService->getUsersAssignedToProject(session('currentProject')));
if (! isset($_GET['raw'])) {
return $this->tpl->displayPartial('ideas.boardDialog');
}
return new Response;
}
/**
* Handles board creation and editing.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function post(array $params): Response
{
$currentCanvasId = '';
$canvasTitle = '';
if (isset($params['id'])) {
$currentCanvasId = (int) $params['id'];
$canvasTitle = $this->ideaService->getBoardTitle($currentCanvasId);
session(['current'.strtoupper(static::CANVAS_NAME).'Canvas' => $currentCanvasId]);
}
if (isset($_POST['newCanvas'])) {
$result = $this->handleNewCanvas($currentCanvasId);
if ($result !== null) {
return $result;
}
}
if (isset($_POST['editCanvas']) && $currentCanvasId > 0) {
$result = $this->handleEditCanvas($currentCanvasId);
if ($result !== null) {
return $result;
}
}
$this->tpl->assign('canvasTitle', $canvasTitle);
$this->tpl->assign('currentCanvas', $currentCanvasId);
$this->tpl->assign('canvasname', 'idea');
$this->tpl->assign('users', $this->projectService->getUsersAssignedToProject(session('currentProject')));
if (! isset($_GET['raw'])) {
return $this->tpl->displayPartial('ideas.boardDialog');
}
return new Response;
}
/**
* Handles creating a new board.
*/
private function handleNewCanvas(int|string &$currentCanvasId): ?Response
{
if (! isset($_POST['canvastitle']) || empty($_POST['canvastitle'])) {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return null;
}
$currentCanvasId = $this->ideaService->createBoardFromDialog(
$_POST['canvastitle'],
(int) session('currentProject'),
(int) session('userdata.id')
);
$this->tpl->setNotification($this->language->__('notification.board_created'), 'success', static::CANVAS_NAME.'board_created');
session(['current'.strtoupper(static::CANVAS_NAME).'Canvas' => $currentCanvasId]);
return Frontcontroller::redirect(BASE_URL.'/ideas/boardDialog/'.$currentCanvasId);
}
/**
* Handles editing a board title.
*/
private function handleEditCanvas(int $currentCanvasId): ?Response
{
if (! isset($_POST['canvastitle']) || empty($_POST['canvastitle'])) {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return null;
}
$this->ideaService->updateBoard($currentCanvasId, $_POST['canvastitle']);
$this->tpl->setNotification($this->language->__('notification.board_edited'), 'success');
return Frontcontroller::redirect(BASE_URL.'/ideas/boardDialog/'.$currentCanvasId);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Leantime\Domain\Ideas\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Ideas\Permissions\IdeasPermissions;
use Leantime\Domain\Ideas\Services\Ideas as IdeaService;
use Symfony\Component\HttpFoundation\Response;
class DelCanvas extends Controller
{
private IdeaService $ideaService;
/**
* Initializes dependencies.
*/
public function init(IdeaService $ideaService): void
{
$this->ideaService = $ideaService;
}
/**
* Displays the delete idea board confirmation.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::DELETE)]
public function get(array $params): Response
{
return $this->tpl->display('ideas.delCanvas');
}
/**
* Handles idea board deletion. The controller gate defers (entityScoped) to the service's
* deleteCanvas(), which authorizes DELETE against the board's REAL project.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::DELETE, entityScoped: true)]
public function post(array $params): Response
{
$id = (int) ($params['id'] ?? $_GET['id'] ?? 0);
if (isset($_POST['del']) && $id > 0) {
$this->ideaService->deleteCanvas($id);
$this->tpl->setNotification($this->language->__('notification.idea_board_deleted'), 'success', 'ideaboard_deleted');
return Frontcontroller::redirect(BASE_URL.'/ideas/showBoards');
}
return $this->tpl->display('ideas.delCanvas');
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Leantime\Domain\Ideas\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Ideas\Permissions\IdeasPermissions;
use Leantime\Domain\Ideas\Services\Ideas as IdeaService;
use Symfony\Component\HttpFoundation\Response;
class DelCanvasItem extends Controller
{
private IdeaService $ideaService;
/**
* Initializes dependencies.
*/
public function init(IdeaService $ideaService): void
{
$this->ideaService = $ideaService;
}
/**
* Displays the delete idea item confirmation.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::DELETE)]
public function get(array $params): Response
{
return $this->tpl->displayPartial('ideas.delCanvasItem');
}
/**
* Handles idea item deletion. The controller gate defers (entityScoped) to the service's
* deleteCanvasItem(), which authorizes DELETE against the item's REAL project.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::DELETE, entityScoped: true)]
public function post(array $params): Response
{
$id = (int) ($params['id'] ?? $_GET['id'] ?? 0);
if (isset($_POST['del']) && $id > 0) {
$this->ideaService->deleteCanvasItem($id);
$this->tpl->setNotification($this->language->__('notification.idea_board_item_deleted'), 'success', 'ideaitem_deleted');
return Frontcontroller::redirect(BASE_URL.'/ideas/showBoards');
}
return $this->tpl->displayPartial('ideas.delCanvasItem');
}
}

View File

@@ -0,0 +1,181 @@
<?php
namespace Leantime\Domain\Ideas\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Ideas\Permissions\IdeasPermissions;
use Leantime\Domain\Ideas\Services\Ideas as IdeaService;
class IdeaDialog extends Controller
{
private IdeaService $ideaService;
/**
* init - initialize private variables
*/
public function init(IdeaService $ideaService): void
{
$this->ideaService = $ideaService;
}
/**
* get - handle get requests. The embedded ?delComment / ?removeMilestone mutations are fenced
* by the service (removeIdeaComment = author-or-moderate; removeMilestone = edit) in-body.
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function get($params)
{
if (isset($params['id'])) {
// Delete comment
if (isset($params['delComment']) === true) {
$commentId = (int) ($params['delComment']);
$this->ideaService->removeIdeaComment($commentId);
$this->tpl->setNotification($this->language->__('notifications.comment_deleted'), 'success', 'ideacomment_deleted');
}
// Delete milestone relationship
if (isset($params['removeMilestone']) === true) {
$this->ideaService->removeMilestone((int) $params['id']);
$this->tpl->setNotification($this->language->__('notifications.milestone_detached'), 'success');
}
$canvasItem = $this->ideaService->getIdeaItem((int) $params['id']);
$comments = $this->ideaService->getIdeaComments('idea', $canvasItem['id']);
$this->tpl->assign('numComments', $this->ideaService->countIdeaComments('ideas', $canvasItem['id']));
} else {
$type = $params['type'] ?? 'idea';
$canvasItem = $this->ideaService->getIdeaItem(null, $type);
$comments = [];
}
$this->tpl->assign('comments', $comments);
$this->tpl->assign('milestones', $this->ideaService->getProjectMilestones((int) session('currentProject')));
$this->tpl->assign('canvasTypes', $this->ideaService->getCanvasTypes());
$this->tpl->assign('canvasItem', $canvasItem);
$this->tpl->assign('currentCanvas', (int) session('currentIdeaCanvas'));
return $this->tpl->displayPartial('ideas.ideaDialog');
}
/**
* post - handle post requests. Create/update/comment all defer to the service methods, which
* authorize the correct verb (ideas.create / ideas.edit / comments.create) against the entity's
* real project.
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function post($params)
{
if (isset($params['comment']) === true) {
if ($params['text'] != '') {
$this->ideaService->addIdeaComment(
$params['text'],
(int) $_GET['id'],
$params['father'],
(int) session('currentProject'),
(int) session('userdata.id')
);
$this->tpl->setNotification($this->language->__('notifications.comment_create_success'), 'success');
return Frontcontroller::redirect(BASE_URL.'/ideas/ideaDialog/'.(int) $_GET['id']);
}
}
$id = $_GET['id'] ?? null;
// changeItem is set for new or edited item changes.
if (isset($params['changeItem'])) {
if (isset($params['itemId']) && $params['itemId'] != '') {
if (isset($params['description']) === true) {
$currentCanvasId = (int) ($params['canvasId'] ?? session('currentIdeaCanvas'));
$input = [
'box' => $params['box'],
'description' => $params['description'],
'status' => $params['status'],
'data' => $params['data'],
'tags' => $params['tags'],
'itemId' => $params['itemId'],
'canvasId' => $currentCanvasId,
'milestoneId' => $params['milestoneId'],
'newMilestone' => $params['newMilestone'] ?? '',
'existingMilestone' => $params['existingMilestone'] ?? '',
];
$this->ideaService->updateIdeaItem(
$input,
(int) session('currentProject'),
(int) session('userdata.id')
);
$comments = $this->ideaService->getIdeaComments('leancanvasitem', (int) $params['itemId']);
$this->tpl->assign(
'numComments',
$this->ideaService->countIdeaComments('leancanvasitem', (int) $params['itemId'])
);
$this->tpl->assign('comments', $comments);
$this->tpl->setNotification($this->language->__('notification.idea_edited'), 'success');
return Frontcontroller::redirect(BASE_URL.'/ideas/ideaDialog/'.(int) $params['itemId']);
} else {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return Frontcontroller::redirect(BASE_URL.'/ideas/ideaDialog/');
}
} else {
if (isset($_POST['description']) === true) {
$currentCanvasId = (int) ($params['canvasId'] ?? session('currentIdeaCanvas'));
$input = [
'box' => $params['box'],
'description' => $params['description'],
'status' => $params['status'],
'data' => $params['data'],
'canvasId' => $currentCanvasId,
];
$id = $this->ideaService->createIdeaItem(
$input,
(int) session('currentProject'),
(int) session('userdata.id')
);
$this->tpl->setNotification($this->language->__('notification.idea_created'), 'success', 'idea_created');
return Frontcontroller::redirect(BASE_URL.'/ideas/ideaDialog/'.(int) $id);
} else {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return Frontcontroller::redirect(BASE_URL.'/ideas/ideaDialog/');
}
}
}
$canvasItem = $this->ideaService->getRawIdeaItem($id !== null ? (int) $id : null);
$this->tpl->assign('canvasTypes', $this->ideaService->getCanvasTypes());
$this->tpl->assign('canvasItem', $canvasItem);
return $this->tpl->displayPartial('ideas.ideaDialog');
}
/**
* put - handle put requests
*/
public function put($params) {}
/**
* delete - handle delete requests
*/
public function delete($params) {}
}

View File

@@ -0,0 +1,181 @@
<?php
namespace Leantime\Domain\Ideas\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Ideas\Permissions\IdeasPermissions;
use Leantime\Domain\Ideas\Services\Ideas as IdeaService;
use Leantime\Domain\Projects\Services\Projects as ProjectService;
use Symfony\Component\HttpFoundation\Response;
class ShowBoards extends Controller
{
private IdeaService $ideaService;
private ProjectService $projectService;
/**
* Initializes dependencies.
*/
public function init(IdeaService $ideaService, ProjectService $projectService): void
{
$this->ideaService = $ideaService;
$this->projectService = $projectService;
session(['lastPage' => CURRENT_URL]);
session(['lastIdeaView' => 'board']);
}
/**
* Displays the idea boards view.
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function get(array $params): Response
{
$allCanvas = $this->ideaService->getAllBoards(session('currentProject'));
$currentCanvasId = $this->resolveCurrentCanvasId($allCanvas, $params);
$this->assignTemplateVars($currentCanvasId, $allCanvas);
if (! isset($_GET['raw'])) {
return $this->tpl->display('ideas.showBoards');
}
return new Response;
}
/**
* Handles idea board mutations (create, edit, search).
*
* @param array $params Request parameters
*/
#[RequiresPermission(IdeasPermissions::VIEW)]
public function post(array $params): Response
{
$allCanvas = $this->ideaService->getAllBoards(session('currentProject'));
$currentCanvasId = $this->resolveCurrentCanvasId($allCanvas, $params);
if (isset($_POST['searchCanvas'])) {
$currentCanvasId = (int) $_POST['searchCanvas'];
session(['currentIdeaCanvas' => $currentCanvasId]);
}
if (isset($_POST['newCanvas'])) {
$result = $this->handleNewCanvas($currentCanvasId, $allCanvas);
if ($result !== null) {
return $result;
}
}
if (isset($_POST['editCanvas']) && $currentCanvasId > 0) {
$result = $this->handleEditCanvas($currentCanvasId);
if ($result !== null) {
return $result;
}
}
$this->assignTemplateVars($currentCanvasId, $allCanvas);
if (! isset($_GET['raw'])) {
return $this->tpl->display('ideas.showBoards');
}
return new Response;
}
/**
* Resolves the current canvas ID from session or request parameters.
*
* Auto-creates a default board (via the service) when the project has none.
*/
private function resolveCurrentCanvasId(array &$allCanvas, array $params): int
{
if (! $allCanvas) {
$currentCanvasId = $this->ideaService->ensureBoardExists(
(int) session('currentProject'),
(int) session('userdata.id'),
$allCanvas
);
$allCanvas = $this->ideaService->getAllBoards(session('currentProject'));
return $currentCanvasId;
}
if (session()->exists('currentIdeaCanvas')) {
$currentCanvasId = session('currentIdeaCanvas');
} else {
$currentCanvasId = -1;
session(['currentIdeaCanvas' => '']);
}
if (session('currentIdeaCanvas') == '') {
$currentCanvasId = $allCanvas[0]['id'];
session(['currentIdeaCanvas' => $currentCanvasId]);
}
if (isset($params['id'])) {
$currentCanvasId = (int) $params['id'];
session(['currentIdeaCanvas' => $currentCanvasId]);
}
return $currentCanvasId;
}
/**
* Handles creating a new idea board.
*/
private function handleNewCanvas(int &$currentCanvasId, array &$allCanvas): ?Response
{
if (! isset($_POST['canvastitle']) || empty($_POST['canvastitle'])) {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return null;
}
$currentCanvasId = $this->ideaService->createBoard(
$_POST['canvastitle'],
(int) session('currentProject'),
(int) session('userdata.id')
);
$allCanvas = $this->ideaService->getAllBoards(session('currentProject'));
$this->tpl->setNotification($this->language->__('notification.idea_board_created'), 'success', 'idea_board_created');
session(['currentIdeaCanvas' => $currentCanvasId]);
return Frontcontroller::redirect(BASE_URL.'/ideas/showBoards/');
}
/**
* Handles editing an idea board title.
*/
private function handleEditCanvas(int &$currentCanvasId): ?Response
{
if (! isset($_POST['canvastitle']) || empty($_POST['canvastitle'])) {
$this->tpl->setNotification($this->language->__('notification.please_enter_title'), 'error');
return null;
}
$currentCanvasId = $this->ideaService->updateBoard($currentCanvasId, $_POST['canvastitle']);
$this->tpl->setNotification($this->language->__('notification.board_edited'), 'success', 'idea_board_edited');
return $this->tpl->display('canvas.boardDialog');
}
/**
* Assigns common template variables.
*/
private function assignTemplateVars(int $currentCanvasId, array $allCanvas): void
{
$this->tpl->assign('currentCanvas', $currentCanvasId);
$this->tpl->assign('canvasLabels', $this->ideaService->getBoardLabels());
$this->tpl->assign('allCanvas', $allCanvas);
$this->tpl->assign('canvasItems', $this->ideaService->getBoardItems($currentCanvasId));
$this->tpl->assign('users', $this->projectService->getUsersAssignedToProject(session('currentProject')));
}
}