Переглянути джерело

Add a simple hashtag system to posts and searches

Visman 2 роки тому
батько
коміт
a24a3e1fdf

+ 7 - 0
app/Core/Parser.php

@@ -54,6 +54,10 @@ class Parser extends Parserus
         $this->setAttr('baseUrl', $this->c->BASE_URL);
         $this->setAttr('showImg', 1 === $this->c->user->show_img);
         $this->setAttr('showImgSign', 1 === $this->c->user->show_img_sig);
+        $this->setAttr(
+            'hashtagLink',
+            1 === $this->c->user->g_search ? $this->c->Router->link('Search', ['keywords' => 'HASHTAG']) : null
+        );
     }
 
     /**
@@ -93,6 +97,9 @@ class Parser extends Parserus
             $this->detectUrls();
         }
 
+        // создание хэштегов
+        $this->detect('hashtag', '%(?<=^|\s|\n|\r)#(?=[\p{L}\p{N}_]{3})[\p{L}\p{N}]+(?:_+[\p{L}\p{N}]+)*(?=$|\s|\n|\r|\.|,)%u', true);
+
         return \preg_replace('%^(\x20*\n)+|(\n\x20*)+$%D', '', $this->getCode());
     }
 

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

@@ -685,4 +685,34 @@ class Update extends Admin
 
         return null;
     }
+
+    /**
+     * rev.59 to rev.60
+     */
+    protected function stageNumber59(array $args): ?int
+    {
+        $queryI  = 'INSERT INTO ::bbcode (bb_tag, bb_edit, bb_delete, bb_structure)
+            VALUES(?s:tag, 1, 0, ?s:structure)';
+        $queryU  = 'UPDATE ::bbcode
+            SET bb_edit=1, bb_delete=0, bb_structure=?s:structure
+            WHERE bb_tag=?s:tag';
+        $bbcodes = include $this->c->DIR_CONFIG . '/defaultBBCode.php';
+
+        foreach ($bbcodes as $bbcode) {
+            if ('hashtag' !== $bbcode['tag']) {
+                continue;
+            }
+
+            $vars = [
+                ':tag'       => $bbcode['tag'],
+                ':structure' => \json_encode($bbcode, FORK_JSON_ENCODE),
+            ];
+            $exist = $this->c->DB->query('SELECT 1 FROM ::bbcode WHERE bb_tag=?s:tag', $vars)->fetchColumn();
+            $query = empty($exist) ? $queryI : $queryU;
+
+            $this->c->DB->exec($query, $vars);
+        }
+
+        return null;
+    }
 }

+ 12 - 1
app/Models/Search/Prepare.php

@@ -19,7 +19,18 @@ class Prepare extends Method
      */
     public function prepare(string $query): bool
     {
-        if (\substr_count($query, '"') % 2) {
+        // обработка хэштега (может быть только один!)
+        if (
+            \preg_match('%^#(?=.{3})[\p{L}\p{N}]+(?:_+[\p{L}\p{N}]+)*$%uD', $query)
+            && \is_string($tag = $this->model->word(\mb_strtolower($query, 'UTF-8')))
+        ) {
+            $this->model->queryError = null;
+            $this->model->queryWords = [$tag];
+            $this->model->queryText  = $tag;
+
+            return true;
+        // не парные кавычки
+        } elseif (\substr_count($query, '"') % 2) {
             $this->model->queryError = 'Odd number of quotes: \'%s\'';
             $this->model->queryWords = [];
             $this->model->queryText  = $query;

+ 13 - 1
app/Models/Search/Search.php

@@ -72,6 +72,18 @@ class Search extends Model
      */
     public function cleanText(string $text, bool $indexing = false): string
     {
+        // хэштеги отдельно
+        $tags = [];
+        $text = \preg_replace_callback(
+            '%(?<=^|\s|\n|\r)#(?=[\p{L}\p{N}_]{3})[\p{L}\p{N}]+(?:_+[\p{L}\p{N}]+)*(?=$|\s|\n|\r|\.|,)%u',
+            function ($matches) use (&$tags)  {
+                $tags[] = $matches[0];
+
+                return ' ';
+            },
+            $text
+        );
+
         $text = \str_replace(['`', '’', 'ё'], ['\'', '\'', 'е'], $text);
         // четыре одинаковых буквы в одну
         $text = \preg_replace('%(\p{L})\1{3,}%u', '\1', $text);
@@ -90,7 +102,7 @@ class Search extends Model
         // сжатие пробелов
         $text = \preg_replace('% {2,}%', ' ', $text);
 
-        return \trim($text);
+        return \trim($text . ' '. \implode(' ', $tags));
     }
 
     /**

+ 21 - 0
app/config/defaultBBCode.php

@@ -676,5 +676,26 @@ $body = __(['Post from topic %s', $parser->de($body)]);
 return "</p><p class=\"f-bb-from\">{$body}</p><p>";
 HANDLER,
     ],
+    [
+        'tag' => 'hashtag',
+        'auto' => false,
+        'text_only' => true,
+        'attrs' => [
+            'No_attr' => [
+                'body_format' => '%^#(?=.{3})[\p{L}\p{N}]+(?:_+[\p{L}\p{N}]+)*$%uD',
+                'text_only' => true,
+            ],
+        ],
+        'handler' => <<<'HANDLER'
+$link = $parser->attr('hashtagLink');
+
+if (\is_string($link)) {
+    $link = \str_replace('HASHTAG', \rawurlencode($body), $link);
 
+    return "<a class=\"f-bb-hashtag\" href=\"{$link}\" rel=\"ugc\">{$body}</a>";
+} else {
+    return "<span class=\"f-bb-hashtag\">{$body}</span>";
+}
+HANDLER,
+    ],
 ];

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

@@ -1844,6 +1844,11 @@ body,
   font-size: xxx-large; /* https://caniuse.com/mdn-css_properties_font-size_xxx-large */
 }
 
+#fork .f-bb-hashtag {
+  border-bottom-style: dashed;
+  border-bottom-width: 0.0625rem;
+}
+
 /****************/
 /* Предпросмотр */
 /****************/