Browse Source

Add view of uploaded files to admin panel

Visman 2 years ago
parent
commit
8ee1da7acc

+ 1 - 1
app/Controllers/Routing.php

@@ -783,7 +783,7 @@ class Routing
             );
             $r->add(
                 $r::DUO,
-                '/admin/uploads',
+                '/admin/uploads[/{page|i:[1-9]\d*}]',
                 'AdminUploads:view',
                 'AdminUploads'
             );

+ 83 - 3
app/Models/Attachment/Attachments.php

@@ -12,18 +12,19 @@ namespace ForkBB\Models\Attachment;
 
 use ForkBB\Core\File;
 use ForkBB\Core\Image;
-use ForkBB\Models\Manager;
+use ForkBB\Models\Model;
 use ForkBB\Models\Post\Post;
 use ForkBB\Models\PM\PPost;
 use ForkBB\Models\User\User;
 use PDO;
 use RuntimeException;
 
-class Attachments extends Manager
+class Attachments extends Model
 {
     const HTML_CONT = '<!DOCTYPE html><html lang="en"><head><title>.</title></head><body>.</body></html>';
     const BAD_EXTS  = '%^(?:php.*|phar|[ps]?html?|jsp?|htaccess|htpasswd|f?cgi|svg|)$%i';
     const FOLDER    = '/upload/';
+    const PER_PAGE  = 20;
 
     /**
      * Ключ модели для контейнера
@@ -230,7 +231,10 @@ class Attachments extends Manager
         return;
     }
 
-    public function recalculate(User $user)
+    /**
+     * Пересчитывает объём файлов пользователя
+     */
+    public function recalculate(User $user): void
     {
         $vars = [
             ':uid' => $user->id,
@@ -241,4 +245,80 @@ class Attachments extends Manager
 
         $this->c->users->update($user); //???? оптимизировать?
     }
+
+    /**
+     * Количество страниц для просмотра файлов
+     */
+    protected function getnumPages(): int
+    {
+        $query = 'SELECT COUNT(id) FROM ::attachments';
+
+        $this->fileCount = (int) $this->c->DB->query($query)->fetchColumn();
+
+        return (int) \ceil(($this->fileCount ?: 1) / self::PER_PAGE);
+    }
+
+    /**
+     * Статус наличия установленной страницы
+     */
+    public function hasPage(): bool
+    {
+        return $this->page > 0 && $this->page <= $this->numPages;
+    }
+
+    /**
+     * Массив страниц
+     */
+    protected function getpagination(): array
+    {
+        return $this->c->Func->paginate(
+            $this->numPages,
+            $this->page,
+            'AdminUploads',
+            ['#' => 'filelist']
+        );
+    }
+
+    /**
+     * Возвращает массив данных с установленной страницы
+     */
+    public function pageData(): array
+    {
+        if (! $this->hasPage()) {
+            throw new InvalidArgumentException('Bad number of displayed page');
+        }
+
+        if (empty($this->fileCount)) {
+            return [];
+        }
+
+        $vars = [
+            ':offset' => ($this->page - 1) * self::PER_PAGE,
+            ':rows'   => self::PER_PAGE,
+        ];
+        $query = "SELECT id
+            FROM ::attachments
+            ORDER BY id DESC
+            LIMIT ?i:rows OFFSET ?i:offset";
+
+        $this->idsList = $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_COLUMN);
+
+        if (empty($this->idsList)) {
+            return [];
+        }
+
+        $vars = [
+            ':ids' => $this->idsList,
+        ];
+        $query = 'SELECT * FROM ::attachments WHERE id IN (?ai:ids)';
+
+        $stmt = $this->c->DB->query($query, $vars);
+        $data = [];
+
+        while ($row = $stmt->fetch()) {
+            $data[$row['id']] = $row;
+        }
+
+        return $data;
+    }
 }

+ 117 - 4
app/Models/Pages/Admin/Uploads.php

@@ -12,9 +12,10 @@ namespace ForkBB\Models\Pages\Admin;
 
 use ForkBB\Core\Validator;
 use ForkBB\Models\Page;
+use ForkBB\Models\Attachment\Attachments;
 use ForkBB\Models\Pages\Admin;
 use ForkBB\Models\Config\Config;
-use function \ForkBB\__;
+use function \ForkBB\{__, dt, size};
 use RuntimeException;
 
 class Uploads extends Admin
@@ -57,9 +58,19 @@ class Uploads extends Admin
             $this->fIswev = $v->getErrors();
         }
 
-        $this->nameTpl         = 'admin/uploads';
-        $this->aIndex          = 'uploads';
-        $this->formUploads     = $this->formUploads($config);
+        $this->nameTpl     = 'admin/uploads';
+        $this->aIndex      = 'uploads';
+        $this->formUploads = $this->formUploads($config);
+
+        $attachments       = $this->c->attachments;
+        $attachments->page = $args['page'] ?: 1;
+        $this->pagination  = $attachments->pagination;
+
+        if ($attachments->hasPage()) {
+            $this->formFileList = $this->formFileList($attachments, $args);
+        } else {
+            $this->badPage = $attachments->page;
+        }
 
         return $this;
     }
@@ -156,4 +167,106 @@ class Uploads extends Admin
             return \implode(',', $result);
         }
     }
+
+    /**
+     * Подготавливает массив данных для формы
+     */
+    protected function formFileList(Attachments $attachments, array $args): array
+    {
+        $data = $attachments->pageData();
+        $uIds = [];
+
+        foreach ($attachments->idsList as $id) {
+            if (isset($data[$id])) {
+                $uid        = $data[$id]['uid'];
+                $uIds[$uid] = $uid;
+            }
+        }
+
+        $users = $this->c->users->loadByIds($uIds);
+
+        $form = [/*
+            'action' => $this->c->Router->link('AdminUploads', $args),
+            'hidden' => [
+                'token' => $this->c->Csrf->create('AdminUploads', $args),
+            ],*/
+            'sets'   => [],
+            /*'btns'   => [],*/
+        ];
+
+        $ids = $attachments->idsList;
+
+        \array_unshift($ids, 0);
+
+        foreach ($ids as $id) {
+            $att    = $data[$id] ?? null;
+            $user   = isset($att['uid'], $users[$att['uid']]) ? $users[$att['uid']] : null;
+            $fields = [];
+
+            $fields["f{$id}-wrap"] = [
+                'class' => ['main-wrap'],
+                'type'  => 'wrap',
+            ];
+            $y = isset($att['path']);
+            $fields["f{$id}-file"] = [
+                'class'   => ['filelist', 'file'],
+                'type'    => $y ? 'include' : 'str',
+                'caption' => 'File head',
+                'value'   => $y ? \basename($att['path']) : '',
+                'title'   => $y ? $att['path'] : '',
+                'href'    => $y ? $this->c->PUBLIC_URL . $attachments::FOLDER . $att['path'] : '',
+                'include' => 'admin/uploads_file',
+            ];
+            $fields["f{$id}-size"] = [
+                'class'   => ['filelist', 'size'],
+                'type'    => 'str',
+                'caption' => 'Size head',
+                'value'   => isset($att['size_kb']) ? size(1024 * ($att['size_kb'] ?: 1)) : '',
+            ];
+            $y = isset($att['created']);
+            $fields["f{$id}-created"] = [
+                'class'   => ['filelist', 'created'],
+                'type'    => $y ? 'link' : 'str',
+                'caption' => 'Created head',
+                'value'   => $y ? dt($att['created']) : '',
+                'title'   => $y ? $att['uip'] : '',
+                'href'    => $y ? $this->c->Router->link('AdminHost', ['ip' => $att['uip']]) : '',
+            ];
+
+            if ($user) {
+                $fields["f{$id}-user"] = [
+                    'class'   => ['filelist', 'user'],
+                    'type'    => 'link',
+                    'caption' => 'User head',
+                    'value'   => $user->username,
+                    'href'    => $this->c->Router->link('User', ['id' => $user->id, 'name' => $user->username]),
+                ];
+            } else {
+                $fields["f{$id}-user"] = [
+                    'class'   => ['filelist', 'user'],
+                    'type'    => 'str',
+                    'caption' => 'User head',
+                    'value'   => $id ? 'User #' . ($att['uid'] ?: '??') : '',
+                ];
+            }
+            $fields[] = [
+                'type' => 'endwrap',
+            ];
+            $fields["f{$id}-action"] = [
+                'class'   => ['action'],
+                'caption' => 'Action',
+                'type'    => 'str',
+                'value'   => $id ? 'X' : '',
+            ];
+
+
+            $form['sets']["f{$id}"] = [
+                'class'  => ['filelist'],
+                'legend' => (string) $id,
+                'fields' => $fields,
+            ];
+        }
+
+        return $form;
+    }
 }

+ 18 - 0
app/lang/en/admin_uploads.po

@@ -38,3 +38,21 @@ msgstr "Output image types"
 
 msgid "Output image types help"
 msgstr "Types of pictures to save to this site, separated by commas. If a picture of another type is loaded, it will be converted to the first type from this list."
+
+msgid "Page %s missing"
+msgstr "Page %s missing."
+
+msgid "File list head"
+msgstr "File list"
+
+msgid "File head"
+msgstr "File"
+
+msgid "Size head"
+msgstr "Size"
+
+msgid "Created head"
+msgstr "Created"
+
+msgid "User head"
+msgstr "Author"

+ 18 - 0
app/lang/ru/admin_uploads.po

@@ -38,3 +38,21 @@ msgstr "Вых. типы картинок"
 
 msgid "Output image types help"
 msgstr "Типы картинок для сохранения на этот сайт, через запятую. Если загружена картинка другого типа, то она будет конвертирована в первый тип из этого списка."
+
+msgid "Page %s missing"
+msgstr "Страница %s отсутствует."
+
+msgid "File list head"
+msgstr "Список файлов"
+
+msgid "File head"
+msgstr "Файл"
+
+msgid "Size head"
+msgstr "Размер"
+
+msgid "Created head"
+msgstr "Создан"
+
+msgid "User head"
+msgstr "Автор"

+ 45 - 0
app/templates/admin/uploads.forkbb.php

@@ -1,3 +1,24 @@
+@section ('pagination')
+    @if ($p->pagination)
+        <nav class="f-pages">
+        @foreach ($p->pagination as $cur)
+            @if ($cur[2])
+          <a class="f-page active" href="{{ $cur[0] }}">{{ $cur[1] }}</a>
+            @elseif ('info' === $cur[1])
+          <span class="f-pinfo">{!! __($cur[0]) !!}</span>
+            @elseif ('space' === $cur[1])
+          <span class="f-page f-pspacer">{!! __('Spacer') !!}</span>
+            @elseif ('prev' === $cur[1])
+          <a rel="prev" class="f-page f-pprev" href="{{ $cur[0] }}" title="{{ __('Previous') }}"><span>{!! __('Previous') !!}</span></a>
+            @elseif ('next' === $cur[1])
+          <a rel="next" class="f-page f-pnext" href="{{ $cur[0] }}" title="{{ __('Next') }}"><span>{!! __('Next') !!}</span></a>
+            @else
+          <a class="f-page" href="{{ $cur[0] }}">{{ $cur[1] }}</a>
+            @endif
+        @endforeach
+        </nav>
+    @endif
+@endsection
 @extends ('layouts/admin')
       <section id="fork-uploads" class="f-admin">
         <h2>{!! __('Uploads head') !!}</h2>
@@ -7,3 +28,27 @@
 @endif
         </div>
       </section>
+@if ($p->pagination)
+      <div id="filelist" class="f-nav-links">
+        <div class="f-nlinks">
+    @yield ('pagination')
+        </div>
+      </div>
+@endif
+      <section id="fork-uploads-files" class="f-admin">
+        <h2>{!! __('File list head') !!}</h2>
+        <div class="f-fdiv">
+@if (null !== $p->badPage && $iswev = [FORK_MESS_ERR => [['Page %s missing', $p->badPage]]])
+    @include ('layouts/iswev')
+@elseif ($form = $p->formFileList)
+    @include ('layouts/form')
+@endif
+        </div>
+      </section>
+@if ($p->pagination)
+      <div class="f-nav-links">
+        <div class="f-nlinks">
+    @yield ('pagination')
+        </div>
+      </div>
+@endif

+ 4 - 0
app/templates/admin/uploads_file.forkbb.php

@@ -0,0 +1,4 @@
+<a id="id-{{ $key }}" class="f-link" href="{{ $cur['href'] }}" @if ($cur['rel']) rel="{{ $cur['rel'] }}" @endif title="{{ $cur['title'] or $cur['value'] }}">{{ $cur['value'] }}</a>
+@if (\preg_match('%\.(webp|avif|jpe?g|gif|png|bmp)$%i', $cur['href']))
+<span class="f-filelist-image-span"><a href="{{ $cur['href'] }}"><img class="f-filelist-image" src="{{ $cur['href'] }}" alt="{{ $cur['value'] }}"></a></span>
+@endif

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

@@ -1068,3 +1068,156 @@
 #forka .f-field-provider.f-field-allow .f-flblch {
   height: 2rem;
 }
+
+/****************************************/
+/* Админка/Загрузки                     */
+/****************************************/
+#fork-uploads + #filelist {
+  margin-bottom: 1rem;
+}
+
+#fork-uploads-files .f-fleg {
+  display: none;
+}
+
+#fork-uploads-files .f-fs-filelist {
+  display: flex;
+}
+
+#fork-uploads-files .f-wrap-main-wrap {
+  width: calc(100% - 3rem);
+}
+
+#fork-uploads-files .f-field-action {
+  width: 3rem;
+}
+
+#fork-uploads-files .f-field-action > dd {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  width: 100%;
+}
+
+#fork-uploads-files .f-filelist-image {
+  max-width: 10rem;
+  max-height: 10rem;
+  display: block;
+}
+
+#fork-uploads-files .f-field-action {
+  border-inline-start: 0.0625rem dotted #AA7939;
+}
+
+@media screen and (max-width: 85.9999rem) {
+  #fork-uploads-files .f-field-action > dt {
+    display: none;
+  }
+
+  #fork-uploads-files .f-field-file,
+  #fork-uploads-files .f-field-size,
+  #fork-uploads-files .f-field-created {
+    border-bottom: 0;
+  }
+
+  #fork-uploads-files .f-wrap-main-wrap .f-ycaption::after {
+    content: ": ";
+  }
+
+  #fork-uploads-files .f-fs-filelist:first-of-type {
+    display: none;
+  }
+}
+
+@media screen and (max-width: 35.9999rem) {
+  #fork-uploads-files .f-field-filelist > dt,
+  #fork-uploads-files .f-field-filelist > dd,
+  #fork-uploads-files .f-wrap-main-wrap .f-ycaption,
+  #fork-uploads-files .f-wrap-main-wrap .f-str {
+    display: inline;
+  }
+}
+
+@media screen and (min-width: 36rem) and (max-width: 85.9999rem) {
+  #fork-uploads-files .f-field-filelist > dt {
+    width: 8rem;
+  }
+
+  #fork-uploads-files .f-field-filelist > dd {
+    width: calc(100% - 8rem);
+  }
+}
+
+@media screen and (min-width: 86rem)  {
+  #fork-uploads-files .f-wrap-main-wrap {
+    display: flex;
+  }
+
+  #fork-uploads-files .f-field-filelist {
+    flex-direction: column;
+    justify-content: space-around;
+  }
+
+  #fork-uploads-files .f-field-filelist > dt{
+    width: 100%;
+    text-align: center;
+  }
+
+  #fork-uploads-files .f-field-filelist > dd {
+    display: flex;
+    gap: 0.3125rem;
+    align-items: center;
+    height: 100%;
+    width: 100%;
+  }
+
+  #fork-uploads-files .f-ycaption {
+    font-weight: bold;
+  }
+
+  #fork-uploads-files .f-field-file {
+    width: 45%;
+  }
+
+  #fork-uploads-files .f-field-file > dd {
+    justify-content: space-between;
+  }
+
+  #fork-uploads-files .f-field-size {
+    width: 10%;
+    border-inline-start: 0.0625rem dotted #AA7939;
+  }
+
+  #fork-uploads-files .f-field-size > dd {
+    justify-content: flex-end;
+  }
+
+  #fork-uploads-files .f-field-created {
+    width: 21%;
+    border-inline-start: 0.0625rem dotted #AA7939;
+  }
+
+  #fork-uploads-files .f-field-created > dd {
+    justify-content: center;
+  }
+
+  #fork-uploads-files .f-field-user {
+    width: 24%;
+    border-inline-start: 0.0625rem dotted #AA7939;
+  }
+
+  #fork-uploads-files .f-fs-filelist:not(:first-of-type) dt {
+    display: none;
+  }
+
+  #fork-uploads-files .f-fs-filelist:first-of-type {
+    overflow: hidden;
+    white-space: nowrap;
+  }
+
+  #fork-uploads-files .f-fs-filelist:first-of-type dt {
+    display: block;
+    width: 100%;
+  }
+}