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,85 @@
<?php
namespace Leantime\Domain\Files\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Files\Permissions\FilesPermissions;
use Leantime\Domain\Files\Services\Files as FileService;
use Symfony\Component\HttpFoundation\Response;
class Browse extends Controller
{
private FileService $filesService;
/**
* Initializes dependencies.
*/
public function init(
FileService $filesService
): void {
$this->filesService = $filesService;
}
/**
* Displays the file browser.
*
* @param array $params Request parameters
*
* @throws \Exception
*/
#[RequiresPermission(FilesPermissions::VIEW)]
public function get(array $params): Response
{
$this->assignTemplateVars();
return $this->tpl->display('files.browse');
}
/**
* Handles file uploads and deletions via POST.
*
* @param array $params Request parameters
*
* @throws \Exception
*/
#[RequiresPermission(FilesPermissions::VIEW)]
public function post(array $params): Response
{
$result = $this->filesService->handleFileAction($_POST, $_FILES, 'project', session('currentProject'));
if ($result['action'] === 'delete') {
if ($result['success'] === true) {
$this->tpl->setNotification($this->language->__('notifications.file_deleted'), 'success', 'file_deleted');
return Frontcontroller::redirect(BASE_URL.'/files/showAll'.(($_GET['modalPopUp'] ?? '') ? '?modalPopUp=true' : ''));
}
$this->tpl->setNotification($this->language->__('notifications.file_deleted_error'), 'error');
}
if ($result['action'] === 'upload') {
if ($result['success'] === true) {
$this->tpl->setNotification('notifications.file_upload_success', 'success', 'file_created');
} else {
$this->tpl->setNotification('notifications.file_upload_error', 'error');
}
}
$this->assignTemplateVars();
return $this->tpl->display('files.browse');
}
/**
* Assigns common template variables.
*/
private function assignTemplateVars(): void
{
$this->tpl->assign('currentModule', session('currentProject'));
$this->tpl->assign('modules', $this->filesService->getModules(session('userdata.id')));
$this->tpl->assign('imgExtensions', $this->filesService->getImageExtensions());
$this->tpl->assign('files', $this->filesService->getFilesByModule('project', session('currentProject')));
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Leantime\Domain\Files\Controllers;
use Leantime\Core\Controller\Controller;
use Leantime\Domain\Files\Services\Files as FileService;
use Symfony\Component\HttpFoundation\Response;
class Get extends Controller
{
private FileService $filesService;
/**
* Initializes the controller with required dependencies.
*
* @param FileService $filesService The file service for retrieval and authorization.
*/
public function init(FileService $filesService): void
{
$this->filesService = $filesService;
}
/**
* Handles GET requests to download/view a file.
*
* Validates that the current user has access to the project the file belongs to
* before serving the file content.
*
* @return Response The file content response, 403 if unauthorized, or 404 if not found.
*
* @throws \Exception
*/
public function get(): Response
{
$rawEncName = $_GET['encName'] ?? '';
$encName = preg_replace('/[^a-zA-Z0-9]+/', '', $rawEncName);
if (empty($encName)) {
return new Response('Bad request', 400);
}
return $this->filesService->getFileForUser($encName, (int) session('userdata.id'));
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Leantime\Domain\Files\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\Files\Permissions\FilesPermissions;
use Leantime\Domain\Files\Services\Files as FileService;
use Symfony\Component\HttpFoundation\Response;
class ShowAll extends Controller
{
private FileService $filesService;
/**
* Initializes dependencies.
*/
public function init(
FileService $filesService
): void {
$this->filesService = $filesService;
}
/**
* Displays all project files.
*
* @param array $params Request parameters
*
* @throws BindingResolutionException
*/
#[RequiresPermission(FilesPermissions::VIEW)]
public function get(array $params): Response
{
$currentModule = $params['id'] ?? $_GET['id'] ?? '';
$this->assignTemplateVars($currentModule);
return $this->tpl->displayPartial('files.showAll');
}
/**
* Handles file uploads and deletions via POST.
*
* @param array $params Request parameters
*
* @throws BindingResolutionException
*/
#[RequiresPermission(FilesPermissions::VIEW)]
public function post(array $params): Response
{
$currentModule = $params['id'] ?? $_GET['id'] ?? '';
$result = $this->filesService->handleFileAction($_POST, $_FILES, 'project', session('currentProject'));
if ($result['action'] === 'delete') {
if ($result['success'] === true) {
$this->tpl->setNotification($this->language->__('notifications.file_deleted'), 'success', 'file_deleted');
return Frontcontroller::redirect(BASE_URL.'/files/showAll'.(($_GET['modalPopUp'] ?? '') ? '?modalPopUp=true' : ''));
}
$this->tpl->setNotification($this->language->__('notifications.file_deleted_error'), 'error');
}
if ($result['action'] === 'upload') {
if ($result['success'] === true) {
$this->tpl->setNotification('notifications.file_upload_success', 'success', 'file_uploaded');
} else {
$this->tpl->setNotification('notifications.file_upload_error', 'error');
}
}
$this->assignTemplateVars($currentModule);
return $this->tpl->displayPartial('files.showAll');
}
/**
* Assigns common template variables.
*/
private function assignTemplateVars(string $currentModule): void
{
$this->tpl->assign('currentModule', $currentModule);
$this->tpl->assign('modules', $this->filesService->getModules(session('userdata.id')));
$this->tpl->assign('imgExtensions', $this->filesService->getImageExtensions());
$this->tpl->assign('files', $this->filesService->getFilesByModule('project', session('currentProject'), session('userdata.id')));
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Leantime\Domain\Files\Controllers;
use Illuminate\Http\Request;
use Leantime\Domain\Files\Services\Files as FileService;
use Symfony\Component\HttpFoundation\Response;
/**
* Handles multipart file uploads (editor image paste/drop, Uppy file manager).
*
* A native Laravel controller (constructor DI, route-bound action). Relocated from the
* retired Api\Controllers\Files. Bound in Files/routes.php at the canonical /files/upload
* plus the backward-compatible /api/files alias used by Tiptap and Uppy. The dead
* paste-fallback branch, the unrelated PATCH (user-settings) handler and the 501 stubs
* from the old controller are intentionally not carried over.
*
* The success response MUST stay the raw upload() metadata array — Tiptap reads
* data.module/encName/extension/realName and Uppy reads the same off response.body.
*/
class Upload
{
public function __construct(private FileService $fileService) {}
/**
* POST — store an uploaded file against a module/moduleId (both from the query string;
* the file is the multipart field "file"). Returns the upload() metadata array as JSON.
*/
public function post(Request $request): Response
{
$module = $request->query('module');
$moduleId = $request->query('moduleId');
// Missing required parts is a client error, not a server fault.
if (! isset($_FILES['file']) || $module === null || $moduleId === null) {
return response()->json(['status' => 'error', 'message' => 'Missing file, module or moduleId'], 400);
}
$module = htmlentities($module);
$id = (int) $moduleId;
// The legacy endpoint had no project gate: a logged-in user could attach files to
// any module/moduleId by tampering with the query string. Authorize against the
// target's project (admins/owners bypass; modules with no project mapping fall back
// to the read-path behaviour in Files::getFileForUser()).
if (! $this->fileService->userCanUploadToModule($module, $id)) {
return response()->json(['status' => 'unauthorized'], 403);
}
$result = $this->fileService->upload($_FILES, $module, $id);
if (is_string($result)) {
return response()->json(['status' => 'error', 'message' => $result], 500);
}
return response()->json($result);
}
}