Another teaspoon of code for the extension system 2
This commit is contained in:
parent
dff71bcace
commit
b3afd2b87f
9 changed files with 409 additions and 26 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
|||
/app/config/main.php
|
||||
/app/config/_*
|
||||
/app/config/db/*
|
||||
/app/config/ext/*
|
||||
/app/cache/**/*.php
|
||||
/app/cache/**/*.lock
|
||||
/app/cache/**/*.tmp
|
||||
|
|
|
@ -29,6 +29,8 @@ class Extension extends Model
|
|||
*/
|
||||
protected string $cKey = 'Extension';
|
||||
|
||||
protected array $prepareData;
|
||||
|
||||
protected function getdispalyName(): string
|
||||
{
|
||||
return $this->dbData['extra']['display-name'] ?? $this->fileData['extra']['display-name'];
|
||||
|
@ -137,4 +139,46 @@ class Extension extends Model
|
|||
{
|
||||
return \in_array($this->status, [self::ENABLED, self::ENABLED_DOWN, self::ENABLED_UP, self::CRASH], true);
|
||||
}
|
||||
|
||||
public function prepare(): bool|string|array
|
||||
{
|
||||
$this->prepareData = [];
|
||||
|
||||
if ($this->fileData['extra']['templates']) {
|
||||
foreach ($this->fileData['extra']['templates'] as $cur) {
|
||||
switch($cur['type']) {
|
||||
case 'pre':
|
||||
if (empty($cur['name'])) {
|
||||
return 'PRE name not found';
|
||||
} elseif (empty($cur['file'])) {
|
||||
return ['Template file \'%s\' not found', $cur['file']];
|
||||
}
|
||||
|
||||
$path = $this->fileData['path'] . '/' . \ltrim($cur['file'], '\\/');
|
||||
|
||||
if (! \is_file($path)) {
|
||||
return ['Template file \'%s\' not found', $cur['file']];
|
||||
}
|
||||
|
||||
$data = \file_get_contents($path);
|
||||
|
||||
$this->prepareData['templates']['pre'][$cur['template']][$cur['name']][] = [
|
||||
'priority' => $cur['priority'] ?: 0,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
break;
|
||||
default:
|
||||
return 'Invalid template type';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function prepareData(): array
|
||||
{
|
||||
return $this->prepareData;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,36 @@ class Extensions extends Manager
|
|||
*/
|
||||
protected string $cKey = 'Extensions';
|
||||
|
||||
/**
|
||||
* Список отсканированных папок
|
||||
*/
|
||||
protected array $folders = [];
|
||||
|
||||
/**
|
||||
* Текст ошибки
|
||||
*/
|
||||
protected string|array $error = '';
|
||||
|
||||
/**
|
||||
* Путь до файла, который содержит данные из всех установленных расширений
|
||||
*/
|
||||
protected string $commonFile;
|
||||
|
||||
/**
|
||||
* Возвращает action (или свойство) по его имени
|
||||
*/
|
||||
public function __get(string $name): mixed
|
||||
{
|
||||
return 'error' === $name ? $this->error : parent::__get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализирует менеджер
|
||||
*/
|
||||
public function init(): Extensions
|
||||
{
|
||||
$this->commonFile = $this->c->DIR_CONFIG . '/ext/common.php';
|
||||
|
||||
$this->fromDB();
|
||||
|
||||
$list = $this->scan($this->c->DIR_EXT);
|
||||
|
@ -110,24 +133,30 @@ class Extensions extends Manager
|
|||
$v = $v->reset()
|
||||
->addValidators([
|
||||
])->addRules([
|
||||
'name' => 'required|string',
|
||||
'type' => 'required|string|in:forkbb-extension',
|
||||
'description' => 'required|string',
|
||||
'homepage' => 'string',
|
||||
'version' => 'required|string',
|
||||
'time' => 'string',
|
||||
'license' => 'string',
|
||||
'authors' => 'required|array',
|
||||
'authors.*.name' => 'required|string',
|
||||
'authors.*.email' => 'string',
|
||||
'authors.*.homepage' => 'string',
|
||||
'authors.*.role' => 'string',
|
||||
'autoload.psr-4' => 'required|array',
|
||||
'autoload.psr-4.*' => 'required|string',
|
||||
'require' => 'required|array',
|
||||
'extra' => 'required|array',
|
||||
'extra.display-name' => 'required|string',
|
||||
'extra.requirements' => 'array',
|
||||
'name' => 'required|string',
|
||||
'type' => 'required|string|in:forkbb-extension',
|
||||
'description' => 'required|string',
|
||||
'homepage' => 'string',
|
||||
'version' => 'required|string',
|
||||
'time' => 'string',
|
||||
'license' => 'string',
|
||||
'authors' => 'required|array',
|
||||
'authors.*.name' => 'required|string',
|
||||
'authors.*.email' => 'string',
|
||||
'authors.*.homepage' => 'string',
|
||||
'authors.*.role' => 'string',
|
||||
'autoload.psr-4' => 'required|array',
|
||||
'autoload.psr-4.*' => 'required|string',
|
||||
'require' => 'required|array',
|
||||
'extra' => 'required|array',
|
||||
'extra.display-name' => 'required|string',
|
||||
'extra.requirements' => 'array',
|
||||
'extra.templates' => 'array',
|
||||
'extra.templates.*.type' => 'required|string|in:pre',
|
||||
'extra.templates.*.template' => 'required|string',
|
||||
'extra.templates.*.name' => 'string',
|
||||
'extra.templates.*.priority' => 'integer',
|
||||
'extra.templates.*.file' => 'string',
|
||||
])->addAliases([
|
||||
])->addArguments([
|
||||
])->addMessages([
|
||||
|
@ -170,4 +199,254 @@ class Extensions extends Manager
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает расширение
|
||||
*/
|
||||
public function install(Extension $ext): bool
|
||||
{
|
||||
if (true !== $ext->canInstall) {
|
||||
$this->error = 'Invalid action';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $ext->prepare();
|
||||
|
||||
if (true !== $result) {
|
||||
$this->error = $result;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$vars = [
|
||||
':name' => $ext->name,
|
||||
':data' => \json_encode($ext->fileData, FORK_JSON_ENCODE),
|
||||
];
|
||||
$query = 'INSERT INTO ::extensions (ext_name, ext_status, ext_data)
|
||||
VALUES(?s:name, 1, ?s:data)';
|
||||
|
||||
$this->c->DB->exec($query, $vars);
|
||||
|
||||
$ext->setModelAttrs([
|
||||
'name' => $ext->name,
|
||||
'dbStatus' => 1,
|
||||
'dbData' => $ext->fileData,
|
||||
'fileData' => $ext->fileData,
|
||||
]);
|
||||
|
||||
$this->updateCommon($ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет расширение
|
||||
*/
|
||||
public function uninstall(Extension $ext): bool
|
||||
{
|
||||
if (true !== $ext->canUninstall) {
|
||||
$this->error = 'Invalid action';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$vars = [
|
||||
':name' => $ext->name,
|
||||
];
|
||||
$query = 'DELETE
|
||||
FROM ::extensions
|
||||
WHERE ext_name=?s:name';
|
||||
|
||||
$this->c->DB->exec($query, $vars);
|
||||
|
||||
$ext->setModelAttrs([
|
||||
'name' => $ext->name,
|
||||
'dbStatus' => null,
|
||||
'dbData' => null,
|
||||
'fileData' => $ext->fileData,
|
||||
]);
|
||||
|
||||
$this->updateCommon($ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет расширение
|
||||
*/
|
||||
public function update(Extension $ext): bool
|
||||
{
|
||||
if (true !== $ext->canUpdate) {
|
||||
$this->error = 'Invalid action';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $ext->prepare();
|
||||
|
||||
if (true !== $result) {
|
||||
$this->error = $result;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$vars = [
|
||||
':name' => $ext->name,
|
||||
':data' => \json_encode($ext->fileData, FORK_JSON_ENCODE),
|
||||
];
|
||||
$query = 'UPDATE ::extensions SET ext_data=?s:data
|
||||
WHERE ext_name=?s:name';
|
||||
|
||||
$this->c->DB->exec($query, $vars);
|
||||
|
||||
$ext->setModelAttrs([
|
||||
'name' => $ext->name,
|
||||
'dbStatus' => $ext->dbStatus,
|
||||
'dbData' => $ext->fileData,
|
||||
'fileData' => $ext->fileData,
|
||||
]);
|
||||
|
||||
$this->updateCommon($ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет расширение
|
||||
*/
|
||||
public function downdate(Extension $ext): bool
|
||||
{
|
||||
if (true !== $ext->canDowndate) {
|
||||
$this->error = 'Invalid action';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $ext->prepare();
|
||||
|
||||
if (true !== $result) {
|
||||
$this->error = $result;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$vars = [
|
||||
':name' => $ext->name,
|
||||
':data' => \json_encode($ext->fileData, FORK_JSON_ENCODE),
|
||||
];
|
||||
$query = 'UPDATE ::extensions SET ext_data=?s:data
|
||||
WHERE ext_name=?s:name';
|
||||
|
||||
$this->c->DB->exec($query, $vars);
|
||||
|
||||
$ext->setModelAttrs([
|
||||
'name' => $ext->name,
|
||||
'dbStatus' => $ext->dbStatus,
|
||||
'dbData' => $ext->fileData,
|
||||
'fileData' => $ext->fileData,
|
||||
]);
|
||||
|
||||
$this->updateCommon($ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Включает расширение
|
||||
*/
|
||||
public function enable(Extension $ext): bool
|
||||
{
|
||||
if (true !== $ext->canEnable) {
|
||||
$this->error = 'Invalid action';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$vars = [
|
||||
':name' => $ext->name,
|
||||
];
|
||||
$query = 'UPDATE ::extensions SET ext_status=1
|
||||
WHERE ext_name=?s:name';
|
||||
|
||||
$this->c->DB->exec($query, $vars);
|
||||
|
||||
$ext->setModelAttrs([
|
||||
'name' => $ext->name,
|
||||
'dbStatus' => 1,
|
||||
'dbData' => $ext->dbData,
|
||||
'fileData' => $ext->fileData,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выключает расширение
|
||||
*/
|
||||
public function disable(Extension $ext): bool
|
||||
{
|
||||
if (true !== $ext->canDisable) {
|
||||
$this->error = 'Invalid action';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$vars = [
|
||||
':name' => $ext->name,
|
||||
];
|
||||
$query = 'UPDATE ::extensions SET ext_status=0
|
||||
WHERE ext_name=?s:name';
|
||||
|
||||
$this->c->DB->exec($query, $vars);
|
||||
|
||||
$ext->setModelAttrs([
|
||||
'name' => $ext->name,
|
||||
'dbStatus' => 0,
|
||||
'dbData' => $ext->dbData,
|
||||
'fileData' => $ext->fileData,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет файл с общими данными по расширениям
|
||||
*/
|
||||
protected function updateCommon(Extension $ext): bool
|
||||
{
|
||||
if (\is_file($this->commonFile)) {
|
||||
$data = include $this->commonFile;
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
if ($ext::NOT_INSTALLED === $ext->status) {
|
||||
unset($data[$ext->name]);
|
||||
} else {
|
||||
$data[$ext->name] = $ext->prepareData();
|
||||
}
|
||||
|
||||
return $this->putData($this->commonFile, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Записывает данные в указанный файл
|
||||
*/
|
||||
protected function putData(string $file, mixed $data): bool
|
||||
{
|
||||
$content = "<?php\n\nreturn " . \var_export($data, true) . ";\n";
|
||||
|
||||
if (false === \file_put_contents($file, $content, \LOCK_EX)) {
|
||||
return false;
|
||||
} else {
|
||||
if (\function_exists('\\opcache_invalidate')) {
|
||||
\opcache_invalidate($file, true);
|
||||
} elseif (\function_exists('\\apc_delete_file')) {
|
||||
\apc_delete_file($file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,13 +77,10 @@ class Extensions extends Admin
|
|||
return $this->c->Message->message('Invalid action');
|
||||
}
|
||||
|
||||
$property = 'can' . \ucfirst($action);
|
||||
|
||||
if (true !== $ext->{$property}) {
|
||||
return $this->c->Message->message('Invalid action');
|
||||
if (true !== $this->c->extensions->{$action}($ext)) {
|
||||
return $this->c->Message->message($this->c->extensions->error);
|
||||
}
|
||||
|
||||
exit(var_dump('<pre>', $_POST, '</pre>'));
|
||||
|
||||
return $this->c->Redirect->page('AdminExtensions')->message("Redirect {$action}", FORK_MESS_SUCC);
|
||||
}
|
||||
}
|
||||
|
|
0
app/config/ext/.gitkeep
Normal file
0
app/config/ext/.gitkeep
Normal file
|
@ -86,3 +86,30 @@ msgstr "Extension not found."
|
|||
|
||||
msgid "Invalid action"
|
||||
msgstr "Invalid action."
|
||||
|
||||
msgid "Redirect install"
|
||||
msgstr "The extension is installed."
|
||||
|
||||
msgid "Redirect uninstall"
|
||||
msgstr "The extension has been uninstalled."
|
||||
|
||||
msgid "Redirect update"
|
||||
msgstr "The extension has been updated."
|
||||
|
||||
msgid "Redirect downdate"
|
||||
msgstr "The extension version has been downgraded."
|
||||
|
||||
msgid "Redirect enable"
|
||||
msgstr "The extension is enabled."
|
||||
|
||||
msgid "Redirect disable"
|
||||
msgstr "The extension is disabled."
|
||||
|
||||
msgid "Invalid template type"
|
||||
msgstr "Invalid template type."
|
||||
|
||||
msgid "PRE name not found"
|
||||
msgstr "PRE name not found."
|
||||
|
||||
msgid "Template file '%s' not found"
|
||||
msgstr "Template file '%s' not found."
|
||||
|
|
|
@ -86,3 +86,30 @@ msgstr "Расширение не найдено."
|
|||
|
||||
msgid "Invalid action"
|
||||
msgstr "Недопустимое действие."
|
||||
|
||||
msgid "Redirect install"
|
||||
msgstr "Расширение установлено."
|
||||
|
||||
msgid "Redirect uninstall"
|
||||
msgstr "Расширение деинсталлировано."
|
||||
|
||||
msgid "Redirect update"
|
||||
msgstr "Расширение обновлено."
|
||||
|
||||
msgid "Redirect downdate"
|
||||
msgstr "Версия расширения понижена."
|
||||
|
||||
msgid "Redirect enable"
|
||||
msgstr "Расширение включено."
|
||||
|
||||
msgid "Redirect disable"
|
||||
msgstr "Расширение выключено."
|
||||
|
||||
msgid "Invalid template type"
|
||||
msgstr "Неверный тип шаблона."
|
||||
|
||||
msgid "PRE name not found"
|
||||
msgstr "PRE-имя не найдено."
|
||||
|
||||
msgid "Template file '%s' not found"
|
||||
msgstr "Файл шаблона '%s' не найден."
|
||||
|
|
|
@ -118,8 +118,15 @@
|
|||
<footer id="fork-footer">
|
||||
<p class="f-sim-header">{!! __('Board footer') !!}</p>
|
||||
<div id="fork-footer-in">
|
||||
<div></div>
|
||||
<div><p id="id-fpoweredby">{!! __('Powered by') !!}</p></div>
|
||||
<div>
|
||||
<!-- PRE footerFirstStart -->
|
||||
<!-- PRE footerFirstEnd -->
|
||||
</div>
|
||||
<div>
|
||||
<!-- PRE footerSecondStart -->
|
||||
<p id="id-fpoweredby">{!! __('Powered by') !!}</p>
|
||||
<!-- PRE footerSecondEnd -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- debuginfo -->
|
||||
</footer>
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
|
||||
#forka .f-fleg {
|
||||
margin-bottom: 0.3125rem;
|
||||
background: linear-gradient(var(--bg-like-nav), var(--bg-fprimary), var(--bg-like-nav));
|
||||
}
|
||||
|
||||
#forka .f-flblch {
|
||||
|
|
Loading…
Add table
Reference in a new issue