Browse Source

Add file uploads 7/...

Visman 2 years ago
parent
commit
485954ee5e

+ 130 - 10
app/Models/Attachment/Attachments.php

@@ -13,13 +13,15 @@ namespace ForkBB\Models\Attachment;
 use ForkBB\Core\File;
 use ForkBB\Core\Image;
 use ForkBB\Models\Manager;
-use ForkBB\Models\User\User;
+use ForkBB\Models\Post\Post;
+use ForkBB\Models\PM\PPost;
+use PDO;
 use RuntimeException;
 
 class Attachments extends Manager
 {
     const HTML_CONT = '<!DOCTYPE html><html lang="en"><head><title>.</title></head><body>.</body></html>';
-    const BAD_EXTS  = '%^(?:php.*|phar|phtml?|s?html?|jsp?|htaccess|htpasswd|f?cgi|)$%i';
+    const BAD_EXTS  = '%^(?:php.*|phar|[ps]?html?|jsp?|htaccess|htpasswd|f?cgi|)$%i';
     const FOLDER    = '/upload/';
 
     /**
@@ -28,7 +30,7 @@ class Attachments extends Manager
     protected string $cKey = 'Attachments';
 
     /**
-     * Сохраняет файл
+     * Сохраняет загруженный файл
      */
     public function addFile(File $file): ?array
     {
@@ -59,14 +61,23 @@ class Attachments extends Manager
         $path     = "{$p1}/{$p2}/{$p3}.{$ext}";
         $location = $this->c->DIR_PUBLIC . self::FOLDER . $path;
 
-        $result = $file
-            ->rename(false)
-            ->rewrite(false)
-            ->setQuality($this->c->config->i_upload_img_quality ?? 75)
-            //->resize($this->c->config->i_avatars_width, $this->c->config->i_avatars_height)
-            ->toFile($location);
+        if (
+            ! \is_dir($dir = \implode('/', \explode('/', $location, -1)))
+            && \mkdir($dir, 0755, true)
+        ) {
+            \file_put_contents("{$dir}/index.html", self::HTML_CONT);
+        }
+
+        $file->rename(false)->rewrite(false);
+
+        if ($file instanceof Image) {
+            $file->setQuality($this->c->config->i_upload_img_quality ?? 75);
+                //->resize($this->c->config->i_avatars_width, $this->c->config->i_avatars_height)
+        }
+
+        $status = $file->toFile($location);
 
-        if (true !== $result) {
+        if (true !== $status) {
             $this->c->Log->warning("Attachments Failed processing {$path}", [
                 'user'    => $this->user->fLog(),
                 'error'   => $file->error(),
@@ -99,6 +110,115 @@ class Attachments extends Manager
             'size_kb'  => $size,
             'path'     => $path,
             'location' => $location,
+            'url'      => $this->c->PUBLIC_URL . self::FOLDER . $path,
+            'image'    => $file instanceof Image,
         ];
     }
+
+    /**
+     * Синхронизирует информацию о вложениях в постах и лс
+     */
+    public function syncWithPost(Post|PPost $post, bool $editPost = false)
+    {
+        if ($post->id < 1) {
+            return;
+        }
+
+        \preg_match_all('%' . self::FOLDER . '((\d{4})/(\d+)/[\w-]+?_(\d{3})\.[\w-]+)\b%', $post->message, $matches, \PREG_SET_ORDER);
+
+        $attInPost = [];
+
+        foreach ($matches as $match) {
+            $attInPost[1000 * $match[3] + $match[4]] = $match[1];
+        }
+
+        $table = $post instanceof PPost ? '::attachments_pos_pm' : '::attachments_pos';
+
+        if (empty($attInPost)) {
+            if (true === $ditPost) {
+                $vars = [
+                    ':pid' => $post->id
+                ];
+                $query = "DELETE FROM {$table} WHERE pid=?i:pid";
+
+                $this->c->DB->exec($query, $vars);
+            }
+        } else {
+            $ids  = \array_keys($attInPost);
+            $vars = [
+                ':ids' => $ids,
+            ];
+            $query = 'SELECT id, path FROM ::attachments WHERE id IN (?ai:ids)';
+            $attInDB = $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_KEY_PAIR);
+            $ids = [];
+
+            foreach ($attInDB as $id => $path) {
+                if ($path === $attInPost[$id]) {
+                    $ids[$id] = $id;
+                } else {
+                    $this->c->Log->warning("Attachments Sync Path do not match id={$id}", [
+                        'user'       => $this->user->fLog(),
+                        'pathInDB'   => $path,
+                        'pathInPost' => $attInPost[$id],
+                    ]);
+                }
+
+                unset($attInPost[$id]);
+            }
+
+            if (! empty($attInPost)) {
+                $this->c->Log->warning("Attachments Sync Unknown paths id={$id}", [
+                    'user'       => $this->user->fLog(),
+                    '$attInPost' => $attInPost,
+                ]);
+            }
+
+            switch ($this->c->DB->getType()) {
+                case 'mysql':
+                    $query = "INSERT IGNORE INTO {$table} (id, pid)
+                        VALUES (?i:id, ?i:pid)";
+
+                    break;
+                case 'sqlite':
+                case 'pgsql':
+                    $query = "INSERT INTO {$table} (id, pid)
+                        VALUES (?i:id, ?i:pid)
+                        ON CONFLICT(id, pid) DO NOTHING";
+
+                    break;
+                default:
+                    $query = "INSERT INTO {$table} (id, pid)
+                        SELECT tmp.*
+                        FROM (SELECT ?i:id AS f1, ?i:pid AS f2) AS tmp
+                        WHERE NOT EXISTS (
+                            SELECT 1
+                            FROM {$table}
+                            WHERE id=?i:id AND pid=?i:pid
+                        )";
+
+                    break;
+            }
+
+            foreach ($ids as $id) {
+                $vars = [
+                    ':id'  => $id,
+                    ':pid' => $post->id,
+                ];
+
+                $this->c->DB->exec($query, $vars);
+            }
+
+            if (true === $editPost) {
+                $vars = [
+                    ':pid' => $post->id,
+                    ':ids' => $ids,
+                ];
+                $query = "DELETE FROM {$table} WHERE pid=?i:pid AND id NOT IN (?ai:ids)";
+
+                $this->c->DB->exec($query, $vars);
+            }
+        }
+
+        return;
+    }
 }

+ 16 - 0
app/Models/Pages/Admin/Install.php

@@ -717,6 +717,22 @@ class Install extends Admin
         ];
         $this->c->DB->createTable('::attachments_pos', $schema);
 
+        //attachments_pos_pm
+        $schema = [
+            'FIELDS' => [
+                'id'          => ['SERIAL', false],
+                'pid'         => ['INT(10) UNSIGNED', false, 0],
+            ],
+            'UNIQUE KEYS' => [
+                'id_pid_idx' => ['id', 'pid'],
+            ],
+            'INDEXES' => [
+                'pid_idx' => ['pid'],
+            ],
+            'ENGINE' => $this->DBEngine,
+        ];
+        $this->c->DB->createTable('::attachments_pos_pm', $schema);
+
         // bans
         $schema = [
             'FIELDS' => [

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

@@ -650,6 +650,21 @@ class Update extends Admin
         ];
         $this->c->DB->createTable('::attachments_pos', $schema);
 
+        //attachments_pos_pm
+        $schema = [
+            'FIELDS' => [
+                'id'          => ['SERIAL', false],
+                'pid'         => ['INT(10) UNSIGNED', false, 0],
+            ],
+            'UNIQUE KEYS' => [
+                'id_pid_idx' => ['id', 'pid'],
+            ],
+            'INDEXES' => [
+                'pid_idx' => ['pid'],
+            ],
+        ];
+        $this->c->DB->createTable('::attachments_pos_pm', $schema);
+
         $coreConfig = new CoreConfig($this->configFile);
 
         $coreConfig->add(

+ 5 - 0
app/Models/Pages/Edit.php

@@ -221,6 +221,11 @@ class Edit extends Page
 
         $this->c->forums->update($topic->parent);
 
+        // синхронизация вложений
+        if ($this->userRules->useUpload) {
+            $this->c->attachments->syncWithPost($post, true);
+        }
+
         // антифлуд
         if (
             $calcPost

+ 5 - 0
app/Models/Pages/Post.php

@@ -286,6 +286,11 @@ class Post extends Page
         $this->c->topics->update($topic->calcStat());
         $this->c->forums->update($forum->calcStat());
 
+        // синхронизация вложений
+        if ($this->userRules->useUpload) {
+            $this->c->attachments->syncWithPost($merge ? $lastPost : $post);
+        }
+
         // обновление данных текущего пользователя
         if (
             ! $merge

+ 9 - 1
app/Models/Pages/PostValidatorTrait.php

@@ -309,6 +309,8 @@ trait PostValidatorTrait
         if (! $v->validation($_FILES + $_POST)) {
             $this->fIswev = $v->getErrors();
 
+            return null;
+        } elseif (! \is_array($v->attachments)) {
             return null;
         }
 
@@ -318,7 +320,13 @@ trait PostValidatorTrait
             $data = $this->c->attachments->addFile($file);
 
             if (\is_array($data)) {
-                $result .= ' ' . $data['path'];
+                $name = $file->name();
+
+                if ($data['image']) {
+                    $result .= "[img]{$data['url']}[/img]\n"; // ={$name}
+                } else {
+                    $result .= "[url={$data['url']}]{$name}[/url]\n";
+                }
             }
         }