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,75 @@
<?php
namespace Leantime\Domain\Install\Controllers;
use Illuminate\Http\Exceptions\HttpResponseException;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller as FrontcontrollerCore;
use Leantime\Domain\Install\Services\Install as InstallService;
use Symfony\Component\HttpFoundation\Response;
class Index extends Controller
{
private InstallService $installService;
/**
* init - initialize private variables
*
* @throws HttpResponseException
*/
public function init(InstallService $installService)
{
$this->installService = $installService;
if ($this->installService->isInstalled()) {
return FrontcontrollerCore::redirect(BASE_URL.'/');
}
}
/**
* get - handle get requests
*
* @param $params parameters or body of the request
*/
public function get($params)
{
return $this->tpl->display('install.new', 'entry');
}
/**
* post - process the installation form submission
*
* @param array $params parameters or body of the request
*/
public function post($params): Response
{
if (isset($_POST['install'])) {
$values = [
'email' => ($params['email']),
'firstname' => ($params['firstname']),
'lastname' => ($params['lastname']),
'company' => ($params['company']),
];
try {
$this->installService->validateInstallInput($values);
} catch (\InvalidArgumentException $e) {
$this->tpl->setNotification($e->getMessage(), 'error');
return FrontcontrollerCore::redirect(BASE_URL.'/install');
}
if ($this->installService->runInstall($values)) {
$this->tpl->setNotification(sprintf($this->language->__('notifications.installation_success_setup_account'), BASE_URL), 'success');
if (session()->has('pwReset')) {
return FrontcontrollerCore::redirect(BASE_URL.'/auth/userInvite/'.session('pwReset'));
}
} else {
$this->tpl->setNotification($this->language->__('notification.error_installing'), 'error');
}
}
return FrontcontrollerCore::redirect(BASE_URL.'/install');
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Leantime\Domain\Install\Controllers;
use Illuminate\Contracts\Container\BindingResolutionException;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller as FrontcontrollerCore;
use Leantime\Domain\Install\Services\Install as InstallService;
use Symfony\Component\HttpFoundation\Response;
class Update extends Controller
{
private InstallService $installService;
/**
* init - initialize private variables
*/
public function init(InstallService $installService)
{
$this->installService = $installService;
}
/**
* get - handle get requests
*
* @params parameters or body of the request
*/
public function get($params)
{
if (! $this->installService->needsUpdate()) {
return FrontcontrollerCore::redirect(BASE_URL.'/auth/login');
}
$updatePage = self::dispatch_filter('customUpdatePage', 'install.update');
return $this->tpl->display($updatePage, 'entry');
}
/**
* @throws BindingResolutionException
*/
public function post($params): Response
{
if (isset($_POST['updateDB'])) {
$success = $this->installService->runUpdate();
if (is_array($success) === true) {
foreach ($success as $errorMessage) {
$this->tpl->setNotification('There was a problem. Please reach out to support@leantime.io for assistance.', 'error');
// report($errorMessage);
}
$this->tpl->setNotification('There was a problem updating your database. Please check your error logs to verify your database is up to date.', 'error');
return FrontcontrollerCore::redirect(BASE_URL.'/install/update');
}
if ($success === true) {
return FrontcontrollerCore::redirect(BASE_URL);
}
}
$this->tpl->setNotification('There was a problem. Please reach out to support@leantime.io for assistance.', 'error');
return FrontcontrollerCore::redirect(BASE_URL.'/install/update');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
<?php
namespace Leantime\Domain\Install\Services;
use Illuminate\Contracts\Container\BindingResolutionException;
use Leantime\Core\Configuration\AppSettings;
use Leantime\Domain\Install\Repositories\Install as InstallRepository;
use Leantime\Domain\Setting\Services\Setting as SettingService;
class Install
{
/**
* @param AppSettings $appSettings Application settings (version metadata).
* @param InstallRepository $installRepo Install data-access layer (DB setup/update). The Install
* domain runs before the full app/DB bootstrap is guaranteed,
* so the service legitimately wraps the repository directly.
* @param SettingService $settingService Setting service used to read the stored db-version.
*/
public function __construct(
protected AppSettings $appSettings,
protected InstallRepository $installRepo,
protected SettingService $settingService
) {}
/**
* currentVersion - gets the currently installed leantime version
*
* @api
*/
public function currentVersion(): string
{
return $this->appSettings->appVersion;
}
/**
* isInstalled - determines whether Leantime has already been installed.
*
* @return bool True when the installation has already completed.
*
* @api
*/
public function isInstalled(): bool
{
return $this->installRepo->checkIfInstalled();
}
/**
* validateInstallInput - validates the admin/company fields submitted during installation.
*
* Throws on the first missing field, preserving the original per-field error order
* (email, firstname, lastname, company). The exception message carries the language
* key the controller should surface as a notification.
*
* @param array $values Submitted install values (email, firstname, lastname, company).
*
* @throws \InvalidArgumentException When a required field is missing.
*
* @api
*/
public function validateInstallInput(array $values): void
{
if (empty($values['email'])) {
throw new \InvalidArgumentException('notification.enter_email');
}
if (empty($values['firstname'])) {
throw new \InvalidArgumentException('notification.enter_firstname');
}
if (empty($values['lastname'])) {
throw new \InvalidArgumentException('notification.enter_lastname');
}
if (empty($values['company'])) {
throw new \InvalidArgumentException('notification.enter_company');
}
}
/**
* runInstall - executes the database setup for a fresh installation.
*
* @param array $values Validated install values (email, firstname, lastname, company).
* @return bool True on successful setup, false otherwise.
*
* @api
*/
public function runInstall(array $values): bool
{
return $this->installRepo->setupDB($values);
}
/**
* needsUpdate - determines whether the stored db-version is behind the application's db-version.
*
* @return bool True when a database update is required.
*
* @api
*/
public function needsUpdate(): bool
{
$dbVersion = $this->settingService->getSetting('db-version');
return $this->appSettings->dbVersion != $dbVersion;
}
/**
* runUpdate - executes pending database update scripts.
*
* Clears the cached db-version before running so the update starts from a clean state,
* then delegates to the repository. Returns true on success or an array of error messages
* on failure (preserving the repository's existing return contract).
*
* @return bool|array True on success, or an array of error messages on failure.
*
* @throws BindingResolutionException
*
* @api
*/
public function runUpdate(): bool|array
{
session()->forget('db-version');
return $this->installRepo->updateDB();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
@extends($layout)
@section('content')
<div class="pageheader">
<div class="pagetitle">
<h1>{!! __('headlines.installation') !!}</h1>
</div>
</div>
<div class="regcontent" id="login">
<p>{!! __('text.this_script_will_set_up_leantime') !!}</p><br />
{!! $tpl->displayInlineNotification() !!}
<form action="{{ BASE_URL }}/install" method="post" class="registrationForm">
<h3 class="subtitle">{!! __('subtitles.login_info') !!}</h3>
<x-global::forms.text-input type="email" name="email" placeholder="{{ __('label.email') }}" value="" /><br />
<br /><br />
<h3 class="subtitle">{!! __('subtitles.user_info') !!}</h3>
<x-global::forms.text-input name="firstname" placeholder="{{ __('label.firstname') }}" value="" /><br />
<x-global::forms.text-input name="lastname" placeholder="{{ __('label.lastname') }}" value="" />
<x-global::forms.text-input name="company" placeholder="{{ __('label.company_name') }}" value="" />
<br /><br />
<input type="hidden" name="install" value="Install" />
<p><x-global::forms.button tag="input" inputType="submit" name="installAction" contentRole="primary" :labelText="__('buttons.install')" onClick="this.form.submit(); this.disabled=true; this.value='{{ __('buttons.install') }}'; " /></p>
</form>
</div>
@endsection

View File

@@ -0,0 +1,19 @@
@extends($layout)
@section('content')
<div class="pageheader">
<div class="pagetitle">
<h1>{!! __('headlines.update_database') !!}</h1>
</div>
</div>
{!! $tpl->displayInlineNotification() !!}
<div class="regcontent" id="login">
<p>{!! __('text.new_db_version') !!}</p><br />
<form action="{{ BASE_URL }}/install/update" method="post" class="registrationForm">
<input type="hidden" name="updateDB" value="1" />
<p><x-global::forms.button tag="input" inputType="submit" name="updateAction" contentRole="primary" :labelText="__('buttons.update_now')" onClick="this.form.submit(); this.disabled=true; this.value='Updating…'; " /></p>
</form>
</div>
@endsection

View File

@@ -0,0 +1,23 @@
<?php
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Core\Events\EventDispatcher;
EventDispatcher::add_filter_listener('leantime.*.welcomeText', function ($welcomeText) {
$language = app()->make(\Leantime\Core\Language::class);
if (Frontcontroller::getCurrentRoute() == 'install') {
$welcomeText = '<h1 class="mainWelcome">'.$language->__('headlines.welcome').'</h1>';
$subText = '';
$welcomeText = $welcomeText.$subText;
}
if (Frontcontroller::getCurrentRoute() == 'install.update') {
$welcomeText = '<h1 class="mainWelcome">'.$language->__('headlines.welcome').'</h1>';
$subText = '';
$welcomeText = $welcomeText.$subText;
}
return $welcomeText;
});