Forráskód Böngészése

Another teaspoon of code for the extension system 2

Visman 1 éve
szülő
commit
b3afd2b87f

+ 1 - 0
.gitignore

@@ -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

+ 44 - 0
app/Models/Extension/Extension.php

@@ -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;
+    }
 }

+ 297 - 18
app/Models/Extension/Extensions.php

@@ -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;
+        }
+    }
 }

+ 3 - 6
app/Models/Pages/Admin/Extensions.php

@@ -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 - 0
app/config/ext/.gitkeep


+ 27 - 0
app/lang/en/admin_extensions.po

@@ -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."

+ 27 - 0
app/lang/ru/admin_extensions.po

@@ -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' не найден."

+ 9 - 2
app/templates/_default/layouts/main.forkbb.php

@@ -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>

+ 1 - 0
public/style/ForkBB/admin.css

@@ -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 {