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,28 @@
<?php
namespace Leantime\Core\Plugins\Attributes;
/**
* Marks a JSON-RPC service method (or whole class) as requiring a named plugin to be enabled.
*
* Enforced at the JSON-RPC dispatch boundary. When the named plugin is disabled, calls return
* a clean JSON-RPC error (-32004) in the response body with HTTP 200 — mirroring the convention
* used by AuthorizationException / NotFoundException.
*
* Class-level usage gates every method on the class; method-level usage overrides per-method.
*
* Single-plugin gating only — the attribute is NOT `IS_REPEATABLE`, so PHP rejects
* multiple `#[RequiresPlugin(...)]` on the same target at the language level. If a
* future tool legitimately needs to require several plugins together, mark this
* attribute repeatable and update the Jsonrpc dispatcher to iterate all instances
* (AND-semantics — every named plugin must be enabled).
*
* Client surfaces (mobile, MCP) should query the core JSON-RPC `config.getSystemInfo` method
* to gate UI client-side so the user never reaches a denied call. The attribute is the
* server-side defense; the capabilities response is the client-side prevention.
*/
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)]
class RequiresPlugin
{
public function __construct(public string $pluginName) {}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Leantime\Core\Plugins;
class PluginManager
{
public function __construct() {}
public function loadPlugin(string $pluginPath): void
{
$registerFile = $pluginPath.'/register.php';
if (file_exists($registerFile)) {
require_once $registerFile;
}
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace Leantime\Core\Plugins;
use Leantime\Core\Configuration;
use Leantime\Core\Events\DispatchesEvents;
/**
* Plugins class
*/
class Plugins
{
use DispatchesEvents;
/**
* Enabled plugins
*/
private array $enabledPlugins = [];
/**
* constructor
*
* @return void
*/
public function __construct(Configuration\Environment $config)
{
if (isset($config->plugins)) {
$plugins = json_decode($config->plugins);
} else {
$plugins = [];
}
$this->enabledPlugins = $this->standardize_plugin_keys(
(array) $plugins
);
}
/**
* Makes all plugin keys lowercase for easy comparisons
*/
private function standardize_plugin_keys(array $plugins): array
{
foreach ($plugins as $plugin_key => $plugin_enabled) {
if ($plugin_key == strtolower($plugin_key)) {
continue;
}
$plugins[strtolower($plugin_key)] = $plugin_enabled;
unset($plugins[$plugin_key]);
}
return $plugins;
}
/**
* Gets all plugin enabled/disabled settings
*/
public function getEnabledPlugins(): array
{
return $this->enabledPlugins;
}
/**
* Checks to see if a plugin is enabled
*/
public function isPluginEnabled(string $plugin_name): bool
{
$plugin_name = strtolower($plugin_name);
if (
in_array($plugin_name, array_keys($this->enabledPlugins)) && $this->enabledPlugins[$plugin_name]
) {
return true;
}
return false;
}
/**
* Gets paths for enabled plugins, supporting both folder and phar formats
*
* @return array Array of plugin paths with format information
*/
public function getEnabledPluginPaths(): array
{
$pluginPaths = [];
$pluginDirectory = APP_ROOT.'/app/Plugins/';
// Get enabled plugins from the domain service
try {
$pluginService = app()->make(\Leantime\Domain\Plugins\Services\Plugins::class);
$enabledPlugins = $pluginService->getEnabledPlugins();
foreach ($enabledPlugins as $plugin) {
// Skip incomplete class objects
if (is_a($plugin, '__PHP_Incomplete_Class') || $plugin == null) {
continue;
}
$pluginPath = $pluginDirectory.$plugin->foldername;
if ($plugin->format == 'phar') {
$pharPath = "phar://{$pluginPath}/{$plugin->foldername}.phar";
if (file_exists($pharPath)) {
$pluginPaths[] = [
'path' => $pharPath,
'foldername' => $plugin->foldername,
'format' => 'phar',
'namespace' => "Leantime\\Plugins\\{$plugin->foldername}\\",
];
}
} else {
// Folder-based plugin
if (is_dir($pluginPath)) {
$pluginPaths[] = [
'path' => $pluginPath,
'foldername' => $plugin->foldername,
'format' => 'folder',
'namespace' => "Leantime\\Plugins\\{$plugin->foldername}\\",
];
}
}
}
} catch (\Exception $e) {
// Fall back to system plugins if service unavailable
if (isset($this->enabledPlugins)) {
foreach ($this->enabledPlugins as $pluginName => $enabled) {
if ($enabled) {
$pluginPath = $pluginDirectory.$pluginName;
if (is_dir($pluginPath)) {
$pluginPaths[] = [
'path' => $pluginPath,
'foldername' => $pluginName,
'format' => 'folder',
'namespace' => "Leantime\\Plugins\\{$pluginName}\\",
];
}
}
}
}
}
return $pluginPaths;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Leantime\Core\Plugins;
use Illuminate\Support\ServiceProvider;
class PluginsServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(\Leantime\Domain\Plugins\Services\Plugins::class, \Leantime\Domain\Plugins\Services\Plugins::class);
}
}