Bläddra i källkod

Add bbcode management 1.5

:(
Visman 4 år sedan
förälder
incheckning
559525fe7f

+ 235 - 0
app/Models/BBCodeList/Structure.php

@@ -0,0 +1,235 @@
+<?php
+
+namespace ForkBB\Models\BBCodeList;
+
+use ForkBB\Core\Container;
+use ForkBB\Models\Model as ParentModel;
+use RuntimeException;
+
+class Structure extends ParentModel
+{
+    public function __construct(Container $container)
+    {
+        parent::__construct($container);
+
+        $this->zDepend = [
+            'text handler' => ['text_handler'],
+            'text only'    => ['text_only'],
+            'tags only'    => ['tags_only'],
+            'self nesting' => ['self_nesting'],
+            'attrs'        => ['no_attr', 'def_attr'],
+        ];
+    }
+
+    public function fromString(string $data): Structure
+    {
+        return $this->setAttrs(\json_decode($data, true, 512, \JSON_THROW_ON_ERROR));
+    }
+
+    public function toString(): string
+    {
+
+    }
+
+    protected function gettype(): string
+    {
+        $type = $this->getAttr('type');
+
+        return \is_string($type) ? $type : 'inline';
+    }
+
+    protected function getparents(): array
+    {
+        $parents = $this->getAttr('parents');
+
+        if (\is_array($parents)) {
+            return $parents;
+        } elseif ('inline' === $this->type) {
+            return ['inline', 'block'];
+        } else {
+            return ['block'];
+        }
+    }
+
+    protected function gettext_handler(): ?string
+    {
+        return $this->getAttr('text handler');
+    }
+
+    protected function settext_handler(?string $value): void
+    {
+        $this->setAttr('text handler', $value);
+    }
+
+    protected function setrecursive($value): void
+    {
+        $value = empty($value) ? null : true;
+        $this->setAttr('recursive', $value);
+    }
+
+    protected function gettext_only(): ?bool
+    {
+        return $this->getAttr('text only');
+    }
+
+    protected function settext_only($value): void
+    {
+        $value = empty($value) ? null : true;
+        $this->setAttr('text only', $value);
+    }
+
+    protected function gettags_only(): ?bool
+    {
+        return $this->getAttr('tags only');
+    }
+
+    protected function settags_only($value): void
+    {
+        $value = empty($value) ? null : true;
+        $this->setAttr('tags only', $value);
+    }
+
+    protected function setpre($value): void
+    {
+        $value = empty($value) ? null : true;
+        $this->setAttr('pre', $value);
+    }
+
+    protected function setsingle($value): void
+    {
+        $value = empty($value) ? null : true;
+        $this->setAttr('single', $value);
+    }
+
+    protected function getauto(): bool
+    {
+        $auto = $this->getAttr('auto');
+
+        if (\is_bool($auto)) {
+            return $auto;
+        } elseif ('inline' === $this->type) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    protected function setauto($value): void
+    {
+        $value = ! empty($value);
+        $this->setAttr('auto', $value);
+    }
+
+    protected function getself_nesting() /* : mixed */
+    {
+        return $this->getAttr('self nesting');
+    }
+
+    protected function setself_nesting($value): void
+    {
+        $value = $value < 1 ? false : (int) $value;
+        $this->setAttr('self nesting', $value);
+    }
+
+    protected function getBBAttr(/* mixed */ $data, array $fields) /* : mixed */
+    {
+        if (true === $data) {
+            return true;
+        } elseif (! \is_array($data)) {
+            return null;
+        } else {
+            $result = [];
+
+            foreach ($fields as $field) {
+                switch ($field) {
+                    case 'format':
+                    case 'body format':
+                        $value = isset($data[$field]) && \is_string($data[$field]) ? $data[$field] : null;
+                        break;
+                    case 'required':
+                    case 'text only':
+                        $value = isset($data[$field]) && true === $data[$field] ? true : null;
+                        break;
+                    default:
+                        throw new RuntimeException('Unknown attribute property');
+                }
+
+                $key          = \str_replace(' ', '_', $field);
+                $result[$key] = $value;
+            }
+
+            return $result;
+        }
+    }
+
+    protected function setBBAttr(/* mixed */ $data, array $fields) /* : mixed */
+    {
+        if (
+            empty($data['allowed'])
+            || $data['allowed'] < 1
+        ) {
+            return null;
+        }
+
+        $result = [];
+        foreach ($fields as $field) {
+            switch ($field) {
+                case 'format':
+                case 'body format':
+                    $value = isset($data[$field]) && \is_string($data[$field]) ? $data[$field] : null;
+                    break;
+                case 'required':
+                case 'text only':
+                    $value = isset($data[$field]) && true === $data[$field] ? true : null;
+                    break;
+                default:
+                    throw new RuntimeException('Unknown attribute property');
+            }
+
+            if (isset($value)) {
+                $key          = \str_replace(' ', '_', $field);
+                $result[$key] = $value;
+            }
+        }
+
+        return empty($result) ? true : $result;
+    }
+
+    protected function getno_attr() /* mixed */
+    {
+        return $this->getBBAttr($this->attrs['no attr'] ?? null, ['body format', 'text only']);
+    }
+
+    protected function setno_attr(array $value): void
+    {
+        $value = $this->getBBAttr($value, ['body format', 'text only']);
+        $attrs = $this->getAttr('attrs');
+
+        if (null === $value) {
+            unset($attrs['no attr']);
+        } else {
+            $attrs['no attr'] = $value;
+        }
+
+        $this->setAttr('attrs', $attrs);
+    }
+
+    protected function getdef_attr() /* mixed */
+    {
+        return $this->getBBAttr($this->attrs['Def'] ?? null, ['required', 'format', 'body format', 'text only']);
+    }
+
+    protected function setdef_attr(array $value): void
+    {
+        $value = $this->getBBAttr($value, ['required', 'format', 'body format', 'text only']);
+        $attrs = $this->getAttr('attrs');
+
+        if (null === $value) {
+            unset($attrs['Def']);
+        } else {
+            $attrs['Def'] = $value;
+        }
+
+        $this->setAttr('attrs', $attrs);
+    }
+}

+ 263 - 1
app/Models/Pages/Admin/Parser/BBCode.php

@@ -4,8 +4,8 @@ namespace ForkBB\Models\Pages\Admin\Parser;
 
 use ForkBB\Core\Validator;
 use ForkBB\Models\Page;
+use ForkBB\Models\BBCodeList\Structure;
 use ForkBB\Models\Pages\Admin\Parser;
-use ForkBB\Models\Config\Model as Config;
 use function \ForkBB\__;
 
 class BBCode extends Parser
@@ -218,4 +218,266 @@ class BBCode extends Parser
 
         return $this->c->Redirect->page('AdminBBCode')->message('BBCode deleted redirect');
     }
+
+    /**
+     * Редактирование/добавление нового bbcode
+     */
+    public function edit(array $args, string $method): Page
+    {
+        $this->c->bbcode->load();
+
+        $structure = $this->c->BBStructure;
+        $id        = isset($args['id']) ? (int) $args['id'] : 0;
+        if ($id > 0) {
+            if (
+                empty($this->c->bbcode->bbcodeTable[$id])
+                || 1 !== $this->c->bbcode->bbcodeTable[$id]['bb_edit']
+            ) {
+                return $this->c->Message->message('Bad request');
+            }
+
+            $structure = $structure->fromString($this->c->bbcode->bbcodeTable[$id]['bb_structure']);
+        }
+
+        $bbTypes = [];
+        foreach ($this->c->bbcode->bbcodeTable as $cur) {
+            $type = $this->c->BBStructure->fromString($cur['bb_structure'])->type;
+            $bbTypes[$type] = $type;
+        }
+        $this->bbTypes = $bbTypes;
+
+        if ('POST' === $method) {
+        }
+
+
+        if ($id > 0) {
+            $title            = __('Edit bbcode head');
+            $this->formAction = $this->c->Router->link('AdminBBCodeEdit', ['id' => $id]);
+            $this->formToken  = $this->c->Csrf->create('AdminBBCodeEdit', ['id' => $id]);
+        } else {
+            $title            = __('Add bbcode head');
+            $this->formAction = $this->c->Router->link('AdminBBCodeNew');
+            $this->formToken  = $this->c->Csrf->create('AdminBBCodeNew');
+        }
+        $this->aCrumbs[] = [
+            $this->formAction,
+            $title,
+        ];
+        if ($id > 0) {
+            $this->aCrumbs[] = __('"%s"', $this->c->bbcode->bbcodeTable[$id]['bb_tag']);
+        }
+        $this->aCrumbs[] = [
+            $this->c->Router->link('AdminBBCode'),
+            __('BBCode management'),
+        ];
+        $this->form      = $this->formEdit($id, $structure);
+        $this->titleForm = $title;
+        $this->classForm = 'editbbcode';
+        $this->nameTpl   = 'admin/form';
+
+        return $this;
+    }
+
+    /**
+     * Формирует данные для формы
+     */
+    protected function formEdit(int $id, Structure $structure): array
+    {
+        $form = [
+            'action' => $this->formAction,
+            'hidden' => [
+                'token' => $this->formToken,
+            ],
+            'sets' => [],
+            'btns'   => [
+                'save' => [
+                    'type'      => 'submit',
+                    'value'     => __('Save'),
+//                    'accesskey' => 's',
+                ],
+            ],
+        ];
+
+        $yn     = [1 => __('Yes'), 0 => __('No')];
+
+        $form['sets']["structure"] = [
+            'class'  => 'structure',
+//            'legend' => ,
+            'fields' => [
+                'tag' => [
+                    'type'      => $id > 0 ? 'str' : 'text',
+                    'value'     => $structure->tag,
+                    'caption'   => __('Tag label'),
+                    'info'      => __('Tag info'),
+                    'maxlength' => 11,
+                    'pattern'   => '^[a-z\*][a-z\d-]{0,10}$',
+                    'required'  => true,
+                ],
+                'type' => [
+                    'type'      => 'select',
+                    'options'   => $this->bbTypes,
+                    'value'     => $structure->type,
+                    'caption'   => __('Type label'),
+                    'info'      => __('Type info'),
+                ],
+                'type_new' => [
+                    'type'      => 'text',
+                    'value'     => isset($this->bbTypes[$structure->type]) ? '' : $structure->type,
+                    'caption'   => __('Type label'),
+                    'info'      => __('New type info'),
+                    'maxlength' => 20,
+                    'pattern'   => '^[a-z][a-z\d-]{0,19}$',
+                ],
+                'parents' => [
+                    'type'      => 'multiselect',
+                    'options'   => $this->bbTypes,
+                    'value'     => $structure->parents,
+                    'caption'   => __('Parents label'),
+                    'info'      => __('Parents info'),
+                    'size'      => \min(15, \count($this->bbTypes)),
+                    'required'  => true,
+                ],
+                'handler' => [
+                    'class'     => 'handler',
+                    'type'      => 'textarea',
+                    'value'     => $structure->handler,
+                    'caption'   => __('Handler label'),
+                    'info'      => __('Handler info'),
+                ],
+                'text_handler' => [
+                    'class'     => 'handler',
+                    'type'      => 'textarea',
+                    'value'     => $structure->text_handler,
+                    'caption'   => __('Text handler label'),
+                    'info'      => __('Text handler info'),
+                ],
+                'recursive' => [
+                    'type'    => 'radio',
+                    'value'   => true === $structure->recursive ? 1 : 0,
+                    'values'  => $yn,
+                    'caption' => __('Recursive label'),
+                    'info'    => __('Recursive info'),
+                ],
+                'text_only' => [
+                    'type'    => 'radio',
+                    'value'   => true === $structure->text_only ? 1 : 0,
+                    'values'  => $yn,
+                    'caption' => __('Text only label'),
+                    'info'    => __('Text only info'),
+                ],
+                'tags_only' => [
+                    'type'    => 'radio',
+                    'value'   => true === $structure->tags_only ? 1 : 0,
+                    'values'  => $yn,
+                    'caption' => __('Tags only label'),
+                    'info'    => __('Tags only info'),
+                ],
+                'pre' => [
+                    'type'    => 'radio',
+                    'value'   => true === $structure->pre ? 1 : 0,
+                    'values'  => $yn,
+                    'caption' => __('Pre label'),
+                    'info'    => __('Pre info'),
+                ],
+                'single' => [
+                    'type'    => 'radio',
+                    'value'   => true === $structure->single ? 1 : 0,
+                    'values'  => $yn,
+                    'caption' => __('Single label'),
+                    'info'    => __('Single info'),
+                ],
+                'auto' => [
+                    'type'    => 'radio',
+                    'value'   => true === $structure->auto ? 1 : 0,
+                    'values'  => $yn,
+                    'caption' => __('Auto label'),
+                    'info'    => __('Auto info'),
+                ],
+                'self_nesting' => [
+                    'type'    => 'number',
+                    'value'   => $structure->self_nesting > 0 ? $structure->self_nesting : 0,
+                    'min'     => 0,
+                    'max'     => 10,
+                    'caption' => __('Self nesting label'),
+                    'info'    => __('Self nesting info'),
+                ],
+            ],
+        ];
+
+        $tagStr = $id > 0 ? $structure->tag : 'TAG';
+
+        $form['sets']["no_attr"] = [
+            'class'  => ['attr', 'no_attr'],
+            'legend' => __('No attr subhead', $tagStr),
+            'fields' => [
+                'no_attr[allowed]' => [
+                    'type'    => 'radio',
+                    'value'   => null === $structure->no_attr ? 0 : 1,
+                    'values'  => $yn,
+                    'caption' => __('Allowed label'),
+                    'info'    => __('Allowed no_attr info'),
+                ],
+                'no_attr[body_format]' => [
+                    'class'     => 'format',
+                    'type'      => 'text',
+                    'value'     => $structure->no_attr['body_format'] ?? '',
+                    'caption'   => __('Body format label'),
+                    'info'      => __('Body format info'),
+                ],
+                'no_attr[text_only]' => [
+                    'type'    => 'radio',
+                    'value'   => empty($structure->no_attr['text_only']) ? 0 : 1,
+                    'values'  => $yn,
+                    'caption' => __('Text only label'),
+                    'info'    => __('Text only info'),
+                ],
+            ],
+        ];
+
+        $form['sets']["def_attr"] = [
+            'class'  => ['attr', 'def_attr'],
+            'legend' => __('Def attr subhead', $tagStr),
+            'fields' => [
+                'def_attr[allowed]' => [
+                    'type'    => 'radio',
+                    'value'   => null === $structure->def_attr ? 0 : 1,
+                    'values'  => $yn,
+                    'caption' => __('Allowed label'),
+                    'info'    => __('Allowed def_attr info'),
+                ],
+                'def_attr[required]' => [
+                    'type'    => 'radio',
+                    'value'   => empty($structure->def_attr['required']) ? 0 : 1,
+                    'values'  => $yn,
+                    'caption' => __('Required label'),
+                    'info'    => __('Required info'),
+                ],
+                'def_attr[format]' => [
+                    'class'     => 'format',
+                    'type'      => 'text',
+                    'value'     => $structure->def_attr['format'] ?? '',
+                    'caption'   => __('Format label'),
+                    'info'      => __('Format info'),
+                ],
+                'def_attr[body_format]' => [
+                    'class'     => 'format',
+                    'type'      => 'text',
+                    'value'     => $structure->def_attr['body_format'] ?? '',
+                    'caption'   => __('Body format label'),
+                    'info'      => __('Body format info'),
+                ],
+                'def_attr[text_only]' => [
+                    'type'    => 'radio',
+                    'value'   => empty($structure->def_attr['text_only']) ? 0 : 1,
+                    'values'  => $yn,
+                    'caption' => __('Text only label'),
+                    'info'    => __('Text only info'),
+                ],
+            ],
+        ];
+
+        $fields = [];
+
+        return $form;
+    }
 }

+ 17 - 0
app/Models/Pages/Admin/Update.php

@@ -825,4 +825,21 @@ class Update extends Admin
 
         return null;
     }
+
+    /**
+     * rev.17 to rev.18
+     */
+    protected function stageNumber17(array $args): ?int
+    {
+        $coreConfig = new CoreConfig($this->c->DIR_CONFIG . '/' . self::CONFIG_FILE);
+
+        $coreConfig->add(
+            'multiple=>BBStructure',
+            '\\ForkBB\\Models\\BBCodeList\\Structure::class'
+        );
+
+        $coreConfig->save();
+
+        return null;
+    }
 }

+ 1 - 1
app/bootstrap.php

@@ -42,7 +42,7 @@ if (
 }
 $c->PUBLIC_URL = $c->BASE_URL . $forkPublicPrefix;
 
-$c->FORK_REVISION = 17;
+$c->FORK_REVISION = 18;
 $c->START         = $forkStart;
 $c->DIR_APP       = __DIR__;
 $c->DIR_PUBLIC    = $forkPublic;

+ 2 - 0
app/config/main.dist.php

@@ -310,5 +310,7 @@ return [
         'SearchModelTruncateIndex'   => \ForkBB\Models\Search\TruncateIndex::class,
         'SearchModelPrepare' => \ForkBB\Models\Search\Prepare::class,
         'SearchModelExecute' => \ForkBB\Models\Search\Execute::class,
+
+        'BBStructure' => \ForkBB\Models\BBCodeList\Structure::class,
     ],
 ];

+ 114 - 0
app/lang/en/admin_parser.po

@@ -155,3 +155,117 @@ msgstr "In signatures"
 
 msgid "BBCode deleted redirect"
 msgstr "BBCode deleted."
+
+msgid "Add bbcode head"
+msgstr "Add BBCode"
+
+msgid "Edit bbcode head"
+msgstr "Edit BBCode"
+
+msgid "Tag label"
+msgstr "Tag"
+
+msgid "Tag info"
+msgstr "BB-code name. Name format: <code>^[a-z\*][a-z\d-]{0,10}$</code>"
+
+msgid "Type label"
+msgstr "Type"
+
+msgid "Type info"
+msgstr "BBCode type ..."
+
+msgid "New type info"
+msgstr "... or new type. Format: <code>^[a-z][a-z\d-]{0,19}$</code>"
+
+msgid "Parents label"
+msgstr "Parents type"
+
+msgid "Parents info"
+msgstr "Types of parent BBCode. You can specify multiple types."
+
+msgid "Handler label"
+msgstr "Handler"
+
+msgid "Handler info"
+msgstr "PHP code for converting BBCode to HTML."
+
+msgid "Text handler label"
+msgstr "Text handler"
+
+msgid "Text handler info"
+msgstr "PHP code for converting BBCode to plain text."
+
+msgid "Recursive label"
+msgstr "Recursive"
+
+msgid "Recursive info"
+msgstr "This is the recursive BBCode. Until the end of the recursion, the parser does not respond to other BBCode."
+
+msgid "Text only label"
+msgstr "Text only"
+
+msgid "Text only info"
+msgstr "This BBCode contains text only."
+
+msgid "Tags only label"
+msgstr "Tags only"
+
+msgid "Tags only info"
+msgstr "This BBCode contains other BBCode only."
+
+msgid "Pre label"
+msgstr "Pre"
+
+msgid "Pre info"
+msgstr "In this BBCode, line feeds, tabs and adjacent spaces will not be changed."
+
+msgid "Single label"
+msgstr "Single"
+
+msgid "Single info"
+msgstr "Single tag. This BBCode has no body and no end tag."
+
+msgid "Auto label"
+msgstr "Auto"
+
+msgid "Auto info"
+msgstr "Autoclosing of this tag with other BBCode."
+
+msgid "Self nesting label"
+msgstr "Self nesting"
+
+msgid "Self nesting info"
+msgstr "How many times can this BBCode be embedded in the same one."
+
+msgid "No attr subhead"
+msgstr "Settings for BBCode type [%s]..."
+
+msgid "Allowed label"
+msgstr "Allowed"
+
+msgid "Allowed no_attr info"
+msgstr "Allow use of this BBCode without attributes."
+
+msgid "Body format label"
+msgstr "Body format"
+
+msgid "Body format info"
+msgstr "The regular expression, sets the rule for body of this BBCode."
+
+msgid "Def attr subhead"
+msgstr "Settings for BBCode type [%s=<i>value</i>]..."
+
+msgid "Allowed def_attr info"
+msgstr "Allow use of this BBCode with primary attribute."
+
+msgid "Required label"
+msgstr "Required"
+
+msgid "Required info"
+msgstr "Required attribute."
+
+msgid "Format label"
+msgstr "Format"
+
+msgid "Format info"
+msgstr "The regular expression, sets the rule for this attribute."

+ 114 - 0
app/lang/ru/admin_parser.po

@@ -155,3 +155,117 @@ msgstr "В подписях"
 
 msgid "BBCode deleted redirect"
 msgstr "BB-код удален."
+
+msgid "Add bbcode head"
+msgstr "Добавление BB-кода"
+
+msgid "Edit bbcode head"
+msgstr "Редактирование BB-кода"
+
+msgid "Tag label"
+msgstr "Tag"
+
+msgid "Tag info"
+msgstr "Имя BB-кода. Формат имени: <code>^[a-z\*][a-z\d-]{0,10}$</code>"
+
+msgid "Type label"
+msgstr "Type"
+
+msgid "Type info"
+msgstr "Тип BB-кода ..."
+
+msgid "New type info"
+msgstr "... или новый тип. Формат: <code>^[a-z][a-z\d-]{0,19}$</code>"
+
+msgid "Parents label"
+msgstr "Parents type"
+
+msgid "Parents info"
+msgstr "Типы родительского BB-кода. Можно указать несколько типов."
+
+msgid "Handler label"
+msgstr "Handler"
+
+msgid "Handler info"
+msgstr "Код PHP для преобразования BB-кода в HTML."
+
+msgid "Text handler label"
+msgstr "Text handler"
+
+msgid "Text handler info"
+msgstr "Код PHP для преобразования BB-кода в простой текст."
+
+msgid "Recursive label"
+msgstr "Recursive"
+
+msgid "Recursive info"
+msgstr "Это рекурсивный BB-код. До окончания рекурсии парсер не реагирует на другие BB-коды."
+
+msgid "Text only label"
+msgstr "Text only"
+
+msgid "Text only info"
+msgstr "Этот BB-код содержит только текст."
+
+msgid "Tags only label"
+msgstr "Tags only"
+
+msgid "Tags only info"
+msgstr "Этот BB-код содержит только другие bb-коды."
+
+msgid "Pre label"
+msgstr "Pre"
+
+msgid "Pre info"
+msgstr "В этом BB-коде символы перевода строки, табуляции и пробелы стоящие рядом не будут изменены."
+
+msgid "Single label"
+msgstr "Single"
+
+msgid "Single info"
+msgstr "Одиночный тег. В этом BB-коде нет тела и закрывающего тега."
+
+msgid "Auto label"
+msgstr "Auto"
+
+msgid "Auto info"
+msgstr "Автозакрытие тега другим BB-кодом."
+
+msgid "Self nesting label"
+msgstr "Self nesting"
+
+msgid "Self nesting info"
+msgstr "Сколько раз этот BB-код может быть вложен в такой же."
+
+msgid "No attr subhead"
+msgstr "Настройки для BB-кода вида [%s]..."
+
+msgid "Allowed label"
+msgstr "Allowed"
+
+msgid "Allowed no_attr info"
+msgstr "Разрешить использовать этот BB-код без атрибутов."
+
+msgid "Body format label"
+msgstr "Body format"
+
+msgid "Body format info"
+msgstr "Регулярное выражение, задает правило для тела BB-кода."
+
+msgid "Def attr subhead"
+msgstr "Настройки для BB-кода вида [%s=<i>значение</i>]..."
+
+msgid "Allowed def_attr info"
+msgstr "Разрешить использовать этот BB-код с первичным атрибутом."
+
+msgid "Required label"
+msgstr "Required"
+
+msgid "Required info"
+msgstr "Обязательный атрибут."
+
+msgid "Format label"
+msgstr "Format"
+
+msgid "Format info"
+msgstr "Регулярное выражение, задает правило для атрибута."

+ 9 - 0
public/style/ForkBB/style.css

@@ -3006,6 +3006,15 @@ body,
   display: block;
 }
 
+#fork .f-field-handler textarea {
+  font-family: "DejaVu Sans Mono", Consolas, "Droid Sans Mono", Monospace, Monaco, "Courier New", Courier;
+  height: 15rem;
+}
+
+#fork .f-field-format input {
+  font-family: "DejaVu Sans Mono", Consolas, "Droid Sans Mono", Monospace, Monaco, "Courier New", Courier;
+}
+
 @media screen and (min-width: 25rem) {
   #fork .f-bbcode-form .f-fs-bbcode {
     display: flex;