]> BookStack Code Mirror - bookstack/commitdiff
ZIP Imports: Added full contents view to import display
authorDan Brown <redacted>
Tue, 5 Nov 2024 13:17:31 +0000 (13:17 +0000)
committerDan Brown <redacted>
Tue, 5 Nov 2024 13:17:31 +0000 (13:17 +0000)
Reduced import data will now be stored on the import itself, instead of
storing a set of totals.

20 files changed:
app/Exports/Controllers/ImportController.php
app/Exports/Import.php
app/Exports/ImportRepo.php
app/Exports/ZipExports/Models/ZipExportAttachment.php
app/Exports/ZipExports/Models/ZipExportBook.php
app/Exports/ZipExports/Models/ZipExportChapter.php
app/Exports/ZipExports/Models/ZipExportImage.php
app/Exports/ZipExports/Models/ZipExportModel.php
app/Exports/ZipExports/Models/ZipExportPage.php
app/Exports/ZipExports/Models/ZipExportTag.php
app/Exports/ZipExports/ZipExportReader.php
app/Exports/ZipExports/ZipExportValidator.php
database/factories/Exports/ImportFactory.php
database/migrations/2024_11_02_160700_create_imports_table.php
lang/en/entities.php
resources/sass/styles.scss
resources/views/exports/import-show.blade.php
resources/views/exports/parts/import-item.blade.php [new file with mode: 0644]
resources/views/exports/parts/import.blade.php
tests/Exports/ZipImportTest.php

index a2389c725f3107785ed59848037391638125d705..3a56ed0345647a67c8979576115437275ea330a6 100644 (file)
@@ -65,10 +65,13 @@ class ImportController extends Controller
     {
         $import = $this->imports->findVisible($id);
 
+//        dd($import->decodeMetadata());
+
         $this->setPageTitle(trans('entities.import_continue'));
 
         return view('exports.import-show', [
             'import' => $import,
+            'data' => $import->decodeMetadata(),
         ]);
     }
 
@@ -89,7 +92,7 @@ class ImportController extends Controller
            // TODO - Validate again before
            // TODO - Check permissions before (create for main item, create for children, create for related items [image, attachments])
         // TODO - Redirect to result
-        // TOOD - Or redirect back with errors
+        // TODO - Or redirect back with errors
     }
 
     /**
index 8400382fd0d757a0974064e1b36e2cee9272fa0e..9c1771c468f27e27d94d48c75bc8de67386490b8 100644 (file)
@@ -3,6 +3,9 @@
 namespace BookStack\Exports;
 
 use BookStack\Activity\Models\Loggable;
+use BookStack\Exports\ZipExports\Models\ZipExportBook;
+use BookStack\Exports\ZipExports\Models\ZipExportChapter;
+use BookStack\Exports\ZipExports\Models\ZipExportPage;
 use BookStack\Users\Models\User;
 use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -14,9 +17,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
  * @property string $path
  * @property string $name
  * @property int $size - ZIP size in bytes
- * @property int $book_count
- * @property int $chapter_count
- * @property int $page_count
+ * @property string $type
+ * @property string $metadata
  * @property int $created_by
  * @property Carbon $created_at
  * @property Carbon $updated_at
@@ -26,24 +28,6 @@ class Import extends Model implements Loggable
 {
     use HasFactory;
 
-    public const TYPE_BOOK = 'book';
-    public const TYPE_CHAPTER = 'chapter';
-    public const TYPE_PAGE = 'page';
-
-    /**
-     * Get the type (model) that this import is intended to be.
-     */
-    public function getType(): string
-    {
-        if ($this->book_count === 1) {
-            return self::TYPE_BOOK;
-        } elseif ($this->chapter_count === 1) {
-            return self::TYPE_CHAPTER;
-        }
-
-        return self::TYPE_PAGE;
-    }
-
     public function getSizeString(): string
     {
         $mb = round($this->size / 1000000, 2);
@@ -68,4 +52,15 @@ class Import extends Model implements Loggable
     {
         return $this->belongsTo(User::class, 'created_by');
     }
+
+    public function decodeMetadata(): ZipExportBook|ZipExportChapter|ZipExportPage|null
+    {
+        $metadataArray = json_decode($this->metadata, true);
+        return match ($this->type) {
+            'book' => ZipExportBook::fromArray($metadataArray),
+            'chapter' => ZipExportChapter::fromArray($metadataArray),
+            'page' => ZipExportPage::fromArray($metadataArray),
+            default => null,
+        };
+    }
 }
index d7e169ad1663b8327710c210e6a82090820a62ad..3265e1c80dc006bded4f3e030e202bc2542d25a7 100644 (file)
@@ -2,7 +2,12 @@
 
 namespace BookStack\Exports;
 
+use BookStack\Exceptions\FileUploadException;
+use BookStack\Exceptions\ZipExportException;
 use BookStack\Exceptions\ZipValidationException;
+use BookStack\Exports\ZipExports\Models\ZipExportBook;
+use BookStack\Exports\ZipExports\Models\ZipExportChapter;
+use BookStack\Exports\ZipExports\Models\ZipExportPage;
 use BookStack\Exports\ZipExports\ZipExportReader;
 use BookStack\Exports\ZipExports\ZipExportValidator;
 use BookStack\Uploads\FileStorage;
@@ -41,6 +46,11 @@ class ImportRepo
         return $query->findOrFail($id);
     }
 
+    /**
+     * @throws FileUploadException
+     * @throws ZipValidationException
+     * @throws ZipExportException
+     */
     public function storeFromUpload(UploadedFile $file): Import
     {
         $zipPath = $file->getRealPath();
@@ -50,15 +60,23 @@ class ImportRepo
             throw new ZipValidationException($errors);
         }
 
-        $zipEntityInfo = (new ZipExportReader($zipPath))->getEntityInfo();
+        $reader = new ZipExportReader($zipPath);
+        $exportModel = $reader->decodeDataToExportModel();
+
         $import = new Import();
-        $import->name = $zipEntityInfo['name'];
-        $import->book_count = $zipEntityInfo['book_count'];
-        $import->chapter_count = $zipEntityInfo['chapter_count'];
-        $import->page_count = $zipEntityInfo['page_count'];
+        $import->type = match (get_class($exportModel)) {
+            ZipExportPage::class => 'page',
+            ZipExportChapter::class => 'chapter',
+            ZipExportBook::class => 'book',
+        };
+
+        $import->name = $exportModel->name;
         $import->created_by = user()->id;
         $import->size = filesize($zipPath);
 
+        $exportModel->metadataOnly();
+        $import->metadata = json_encode($exportModel);
+
         $path = $this->storage->uploadFile(
             $file,
             'uploads/files/imports/',
@@ -72,6 +90,13 @@ class ImportRepo
         return $import;
     }
 
+    public function runImport(Import $import, ?string $parent = null)
+    {
+        // TODO - Download import zip (if needed)
+        // TODO - Validate zip file again
+        // TODO - Check permissions before (create for main item, create for children, create for related items [image, attachments])
+    }
+
     public function deleteImport(Import $import): void
     {
         $this->storage->delete($import->path);
index e586b91b0ee69556ba367a348db18ca265e46342..1dbdc7333e823de3e4d9189f22a869ced2c31cb6 100644 (file)
@@ -14,6 +14,11 @@ class ZipExportAttachment extends ZipExportModel
     public ?string $link = null;
     public ?string $file = null;
 
+    public function metadataOnly(): void
+    {
+        $this->order = $this->link = $this->file = null;
+    }
+
     public static function fromModel(Attachment $model, ZipExportFiles $files): self
     {
         $instance = new self();
@@ -49,4 +54,17 @@ class ZipExportAttachment extends ZipExportModel
 
         return $context->validateData($data, $rules);
     }
+
+    public static function fromArray(array $data): self
+    {
+        $model = new self();
+
+        $model->id = $data['id'] ?? null;
+        $model->name = $data['name'];
+        $model->order = isset($data['order']) ? intval($data['order']) : null;
+        $model->link = $data['link'] ?? null;
+        $model->file = $data['file'] ?? null;
+
+        return $model;
+    }
 }
index 7e1f2d8106e5f35665644c4b4bc69c147b50b6a7..0dc4e93d43cb34070a2c53a065259ef0787c44dc 100644 (file)
@@ -21,6 +21,21 @@ class ZipExportBook extends ZipExportModel
     /** @var ZipExportTag[] */
     public array $tags = [];
 
+    public function metadataOnly(): void
+    {
+        $this->description_html = $this->cover = null;
+
+        foreach ($this->chapters as $chapter) {
+            $chapter->metadataOnly();
+        }
+        foreach ($this->pages as $page) {
+            $page->metadataOnly();
+        }
+        foreach ($this->tags as $tag) {
+            $tag->metadataOnly();
+        }
+    }
+
     public static function fromModel(Book $model, ZipExportFiles $files): self
     {
         $instance = new self();
@@ -71,4 +86,19 @@ class ZipExportBook extends ZipExportModel
 
         return $errors;
     }
+
+    public static function fromArray(array $data): self
+    {
+        $model = new self();
+
+        $model->id = $data['id'] ?? null;
+        $model->name = $data['name'];
+        $model->description_html = $data['description_html'] ?? null;
+        $model->cover = $data['cover'] ?? null;
+        $model->tags = ZipExportTag::fromManyArray($data['tags'] ?? []);
+        $model->pages = ZipExportPage::fromManyArray($data['pages'] ?? []);
+        $model->chapters = ZipExportChapter::fromManyArray($data['chapters'] ?? []);
+
+        return $model;
+    }
 }
index 03df31b707848a93757e61601dc93601f6fed979..50440d61a5a771b7c01371dc4eddac0c51ed8001 100644 (file)
@@ -18,6 +18,18 @@ class ZipExportChapter extends ZipExportModel
     /** @var ZipExportTag[] */
     public array $tags = [];
 
+    public function metadataOnly(): void
+    {
+        $this->description_html = $this->priority = null;
+
+        foreach ($this->pages as $page) {
+            $page->metadataOnly();
+        }
+        foreach ($this->tags as $tag) {
+            $tag->metadataOnly();
+        }
+    }
+
     public static function fromModel(Chapter $model, ZipExportFiles $files): self
     {
         $instance = new self();
@@ -61,4 +73,18 @@ class ZipExportChapter extends ZipExportModel
 
         return $errors;
     }
+
+    public static function fromArray(array $data): self
+    {
+        $model = new self();
+
+        $model->id = $data['id'] ?? null;
+        $model->name = $data['name'];
+        $model->description_html = $data['description_html'] ?? null;
+        $model->priority = isset($data['priority']) ? intval($data['priority']) : null;
+        $model->tags = ZipExportTag::fromManyArray($data['tags'] ?? []);
+        $model->pages = ZipExportPage::fromManyArray($data['pages'] ?? []);
+
+        return $model;
+    }
 }
index 3388c66df365e18494fd47c17d73eda5ea403c0f..691eb918fc68c346f51333c9b8c2d133b780f41b 100644 (file)
@@ -25,6 +25,11 @@ class ZipExportImage extends ZipExportModel
         return $instance;
     }
 
+    public function metadataOnly(): void
+    {
+        //
+    }
+
     public static function validate(ZipValidationHelper $context, array $data): array
     {
         $rules = [
@@ -36,4 +41,16 @@ class ZipExportImage extends ZipExportModel
 
         return $context->validateData($data, $rules);
     }
+
+    public static function fromArray(array $data): self
+    {
+        $model = new self();
+
+        $model->id = $data['id'] ?? null;
+        $model->name = $data['name'];
+        $model->file = $data['file'];
+        $model->type = $data['type'];
+
+        return $model;
+    }
 }
index 4d66f010f860f086b765faebcc0542e8d6796c26..d3a8c35674b15cda80381fafe3335a195b1130bb 100644 (file)
@@ -26,4 +26,32 @@ abstract class ZipExportModel implements JsonSerializable
      * item in the array for its own validation messages.
      */
     abstract public static function validate(ZipValidationHelper $context, array $data): array;
+
+    /**
+     * Decode the array of data into this export model.
+     */
+    abstract public static function fromArray(array $data): self;
+
+    /**
+     * Decode an array of array data into an array of export models.
+     * @param array[] $data
+     * @return self[]
+     */
+    public static function fromManyArray(array $data): array
+    {
+        $results = [];
+        foreach ($data as $item) {
+            $results[] = static::fromArray($item);
+        }
+        return $results;
+    }
+
+    /**
+     * Remove additional content in this model to reduce it down
+     * to just essential id/name values for identification.
+     *
+     * The result of this may be something that does not pass validation, but is
+     * simple for the purpose of creating a contents.
+     */
+    abstract public function metadataOnly(): void;
 }
index 2c8b9a88abdf13bf990aafef630e276eac026565..3a876e7aaff22a57ec47d5282b2b978877c40664 100644 (file)
@@ -21,6 +21,21 @@ class ZipExportPage extends ZipExportModel
     /** @var ZipExportTag[] */
     public array $tags = [];
 
+    public function metadataOnly(): void
+    {
+        $this->html = $this->markdown = $this->priority = null;
+
+        foreach ($this->attachments as $attachment) {
+            $attachment->metadataOnly();
+        }
+        foreach ($this->images as $image) {
+            $image->metadataOnly();
+        }
+        foreach ($this->tags as $tag) {
+            $tag->metadataOnly();
+        }
+    }
+
     public static function fromModel(Page $model, ZipExportFiles $files): self
     {
         $instance = new self();
@@ -70,4 +85,20 @@ class ZipExportPage extends ZipExportModel
 
         return $errors;
     }
+
+    public static function fromArray(array $data): self
+    {
+        $model = new self();
+
+        $model->id = $data['id'] ?? null;
+        $model->name = $data['name'];
+        $model->html = $data['html'] ?? null;
+        $model->markdown = $data['markdown'] ?? null;
+        $model->priority = isset($data['priority']) ? intval($data['priority']) : null;
+        $model->attachments = ZipExportAttachment::fromManyArray($data['attachments'] ?? []);
+        $model->images = ZipExportImage::fromManyArray($data['images'] ?? []);
+        $model->tags = ZipExportTag::fromManyArray($data['tags'] ?? []);
+
+        return $model;
+    }
 }
index 99abb811c0657037d23cb04183e52b425373a9bd..b6c9e338aef20e081580148a613f67c833412273 100644 (file)
@@ -11,6 +11,11 @@ class ZipExportTag extends ZipExportModel
     public ?string $value = null;
     public ?int $order = null;
 
+    public function metadataOnly(): void
+    {
+        $this->value = $this->order = null;
+    }
+
     public static function fromModel(Tag $model): self
     {
         $instance = new self();
@@ -36,4 +41,15 @@ class ZipExportTag extends ZipExportModel
 
         return $context->validateData($data, $rules);
     }
+
+    public static function fromArray(array $data): self
+    {
+        $model = new self();
+
+        $model->name = $data['name'];
+        $model->value = $data['value'] ?? null;
+        $model->order = isset($data['order']) ? intval($data['order']) : null;
+
+        return $model;
+    }
 }
index 7187a18897d9288c068f74042f1362ce84b59749..c3e47da048da26c5988fb9d26aea27cb442b8f3c 100644 (file)
@@ -3,6 +3,10 @@
 namespace BookStack\Exports\ZipExports;
 
 use BookStack\Exceptions\ZipExportException;
+use BookStack\Exports\ZipExports\Models\ZipExportBook;
+use BookStack\Exports\ZipExports\Models\ZipExportChapter;
+use BookStack\Exports\ZipExports\Models\ZipExportModel;
+use BookStack\Exports\ZipExports\Models\ZipExportPage;
 use ZipArchive;
 
 class ZipExportReader
@@ -71,32 +75,18 @@ class ZipExportReader
 
     /**
      * @throws ZipExportException
-     * @returns array{name: string, book_count: int, chapter_count: int, page_count: int}
      */
-    public function getEntityInfo(): array
+    public function decodeDataToExportModel(): ZipExportBook|ZipExportChapter|ZipExportPage
     {
         $data = $this->readData();
-        $info = ['name' => '', 'book_count' => 0, 'chapter_count' => 0, 'page_count' => 0];
-
         if (isset($data['book'])) {
-            $info['name'] = $data['book']['name'] ?? '';
-            $info['book_count']++;
-            $chapters = $data['book']['chapters'] ?? [];
-            $pages = $data['book']['pages'] ?? [];
-            $info['chapter_count'] += count($chapters);
-            $info['page_count'] += count($pages);
-            foreach ($chapters as $chapter) {
-                $info['page_count'] += count($chapter['pages'] ?? []);
-            }
-        } elseif (isset($data['chapter'])) {
-            $info['name'] = $data['chapter']['name'] ?? '';
-            $info['chapter_count']++;
-            $info['page_count'] += count($data['chapter']['pages'] ?? []);
-        } elseif (isset($data['page'])) {
-            $info['name'] = $data['page']['name'] ?? '';
-            $info['page_count']++;
+            return ZipExportBook::fromArray($data['book']);
+        } else if (isset($data['chapter'])) {
+            return ZipExportChapter::fromArray($data['chapter']);
+        } else if (isset($data['page'])) {
+            return ZipExportPage::fromArray($data['page']);
         }
 
-        return $info;
+        throw new ZipExportException("Could not identify content in ZIP file data.");
     }
 }
index e476998c216dd0ffa54081ac8ab58ccabc902a32..e27ae53c774348d09d2654b17c63334c0341035f 100644 (file)
@@ -38,7 +38,6 @@ class ZipExportValidator
             return ['format' => trans('errors.import_zip_no_data')];
         }
 
-
         return $this->flattenModelErrors($modelErrors, $keyPrefix);
     }
 
index 55378d5832e9b746335f861dac90e433aacbb2e3..74a2bcd65f36d24ee5db2bd1b9219315793ba441 100644 (file)
@@ -23,9 +23,8 @@ class ImportFactory extends Factory
         return [
             'path' => 'uploads/imports/' . Str::random(10) . '.zip',
             'name' => $this->faker->words(3, true),
-            'book_count' => 1,
-            'chapter_count' => 5,
-            'page_count' => 15,
+            'type' => 'book',
+            'metadata' => '{"name": "My book"}',
             'created_at' => User::factory(),
         ];
     }
index ed188226981408c8cb03cc22b8f06f2a67fb6a4e..0784591b8e32936ecc3fd29697298ff4e10ce2e2 100644 (file)
@@ -16,10 +16,9 @@ return new class extends Migration
             $table->string('name');
             $table->string('path');
             $table->integer('size');
-            $table->integer('book_count');
-            $table->integer('chapter_count');
-            $table->integer('page_count');
-            $table->integer('created_by');
+            $table->string('type');
+            $table->longText('metadata');
+            $table->integer('created_by')->index();
             $table->timestamps();
         });
     }
index 065eb043a11bf7bb9de25c443cc67f843c1729b0..ae1c1e8d4cc773d2a84dbe48868b851cb7ca1e51 100644 (file)
@@ -45,7 +45,7 @@ return [
     'default_template_select' => 'Select a template page',
     'import' => 'Import',
     'import_validate' => 'Validate Import',
-    'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to import then press "Validate Import" to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.',
+    'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.',
     'import_zip_select' => 'Select ZIP file to upload',
     'import_zip_validation_errors' => 'Errors were detected while validating the provided ZIP file:',
     'import_pending' => 'Pending Imports',
@@ -53,9 +53,9 @@ return [
     'import_continue' => 'Continue Import',
     'import_continue_desc' => 'Review the content due to be imported from the uploaded ZIP file. When ready, run the import to add its contents to this system. The uploaded ZIP import file will be automatically removed on successful import.',
     'import_run' => 'Run Import',
-    'import_size' => 'Import ZIP Size:',
-    'import_uploaded_at' => 'Uploaded:',
-    'import_uploaded_by' => 'Uploaded by:',
+    'import_size' => ':size Import ZIP Size',
+    'import_uploaded_at' => 'Uploaded :relativeTime',
+    'import_uploaded_by' => 'Uploaded by',
     'import_location' => 'Import Location',
     'import_location_desc' => 'Select a target location for your imported content. You\'ll need the relevant permissions to create within the location you choose.',
     'import_delete_confirm' => 'Are you sure you want to delete this import?',
index 942265d04d8bf16ceaae4d51f0ccd4a43eb943ca..2cf3cbf822192173d2fc2884b463ee27fc35aa50 100644 (file)
@@ -248,4 +248,9 @@ $loadingSize: 10px;
       transform: rotate(180deg);
     }
   }
+}
+
+.import-item {
+  border-inline-start: 2px solid currentColor;
+  padding-inline-start: $-xs;
 }
\ No newline at end of file
index 63977947dbacacf21f56b571fa317b37672becbb..40867377fb08d6cdb91302e227e010fcfce7e6ff 100644 (file)
@@ -1,11 +1,6 @@
 @extends('layouts.simple')
 
 @section('body')
-
-    @php
-        $type = $import->getType();
-    @endphp
-
     <div class="container small">
 
         <main class="card content-wrap auto-height mt-xxl">
             <p class="text-muted">{{ trans('entities.import_continue_desc') }}</p>
 
             <div class="mb-m">
-                <label class="setting-list-label">Import Details</label>
-                <div class="flex-container-row items-center justify-space-between wrap">
+                <label class="setting-list-label mb-m">Import Details</label>
+                <div class="flex-container-row justify-space-between wrap">
                     <div>
-                        <p class="text-{{ $type }} mb-xs bold">@icon($type) {{ $import->name }}</p>
-                        @if($type === 'book')
-                            <p class="text-chapter mb-xs ml-l">@icon('chapter') {{ trans_choice('entities.x_chapters', $import->chapter_count) }}</p>
-                        @endif
-                        @if($type === 'book' || $type === 'chapter')
-                            <p class="text-page mb-xs ml-l">@icon('page') {{ trans_choice('entities.x_pages', $import->page_count) }}</p>
-                        @endif
+                        @include('exports.parts.import-item', ['type' => $import->type, 'model' => $data])
                     </div>
-                    <div>
-                        <div class="opacity-80">
-                            <strong>{{ trans('entities.import_size') }}</strong>
-                            <span>{{ $import->getSizeString() }}</span>
-                        </div>
-                        <div class="opacity-80">
-                            <strong>{{ trans('entities.import_uploaded_at') }}</strong>
-                            <span title="{{ $import->created_at->toISOString() }}">{{ $import->created_at->diffForHumans() }}</span>
-                        </div>
+                    <div class="text-right text-muted">
+                        <div>{{ trans('entities.import_size', ['size' => $import->getSizeString()]) }}</div>
+                        <div><span title="{{ $import->created_at->toISOString() }}">{{ trans('entities.import_uploaded_at', ['relativeTime' => $import->created_at->diffForHumans()]) }}</span></div>
                         @if($import->createdBy)
-                            <div class="opacity-80">
-                                <strong>{{ trans('entities.import_uploaded_by') }}</strong>
+                            <div>
+                                {{ trans('entities.import_uploaded_by') }}
                                 <a href="{{ $import->createdBy->getProfileUrl() }}">{{ $import->createdBy->name }}</a>
                             </div>
                         @endif
                   method="POST">
                 {{ csrf_field() }}
 
-                @if($type === 'page' || $type === 'chapter')
+                @if($import->type === 'page' || $import->type === 'chapter')
                     <hr>
                     <label class="setting-list-label">{{ trans('entities.import_location') }}</label>
                     <p class="small mb-m">{{ trans('entities.import_location_desc') }}</p>
                     @include('entities.selector', [
                         'name' => 'parent',
-                        'entityTypes' => $type === 'page' ? 'chapter,book' : 'book',
-                        'entityPermission' => "{$type}-create",
+                        'entityTypes' => $import->type === 'page' ? 'chapter,book' : 'book',
+                        'entityPermission' => "{$import->type}-create",
                         'selectorSize' => 'compact small',
                     ])
                     @include('form.errors', ['name' => 'parent'])
diff --git a/resources/views/exports/parts/import-item.blade.php b/resources/views/exports/parts/import-item.blade.php
new file mode 100644 (file)
index 0000000..811a3b3
--- /dev/null
@@ -0,0 +1,26 @@
+{{--
+$type - string
+$model - object
+--}}
+<div class="import-item text-{{ $type }} mb-xs">
+    <p class="mb-none">@icon($type){{ $model->name }}</p>
+    <div class="ml-s">
+        <div class="text-muted">
+            @if($model->attachments ?? [])
+                <span>@icon('attach'){{ count($model->attachments) }}</span>
+            @endif
+            @if($model->images ?? [])
+                <span>@icon('image'){{ count($model->images) }}</span>
+            @endif
+            @if($model->tags ?? [])
+                <span>@icon('tag'){{ count($model->tags) }}</span>
+            @endif
+        </div>
+        @foreach($model->chapters ?? [] as $chapter)
+            @include('exports.parts.import-item', ['type' => 'chapter', 'model' => $chapter])
+        @endforeach
+        @foreach($model->pages ?? [] as $page)
+            @include('exports.parts.import-item', ['type' => 'page', 'model' => $page])
+        @endforeach
+    </div>
+</div>
\ No newline at end of file
index 5ff6600f24b48980ee810fbbc947fde1bd51f0f7..fd53095a4221d08c5af46dff847ae83d68be853f 100644 (file)
@@ -1,18 +1,9 @@
-@php
-    $type = $import->getType();
-@endphp
 <div class="item-list-row flex-container-row items-center justify-space-between wrap">
     <div class="px-m py-s">
         <a href="{{ $import->getUrl() }}"
-           class="text-{{ $type }}">@icon($type) {{ $import->name }}</a>
+           class="text-{{ $import->type }}">@icon($import->type) {{ $import->name }}</a>
     </div>
     <div class="px-m py-s flex-container-row gap-m items-center">
-        @if($type === 'book')
-            <div class="text-chapter opacity-80 bold">@icon('chapter') {{ $import->chapter_count }}</div>
-        @endif
-        @if($type === 'book' || $type === 'chapter')
-            <div class="text-page opacity-80 bold">@icon('page') {{ $import->page_count }}</div>
-        @endif
         <div class="bold opacity-80">{{ $import->getSizeString() }}</div>
         <div class="bold opacity-80 text-muted" title="{{ $import->created_at->toISOString() }}">@icon('time'){{ $import->created_at->diffForHumans() }}</div>
     </div>
index b9a8598fabe6c00fd2f7db4efdb44c70f7ea858c..2b40100aabe854ec2d7f296ef3fb441756b83405 100644 (file)
@@ -4,6 +4,9 @@ namespace Tests\Exports;
 
 use BookStack\Activity\ActivityType;
 use BookStack\Exports\Import;
+use BookStack\Exports\ZipExports\Models\ZipExportBook;
+use BookStack\Exports\ZipExports\Models\ZipExportChapter;
+use BookStack\Exports\ZipExports\Models\ZipExportPage;
 use Illuminate\Http\UploadedFile;
 use Illuminate\Testing\TestResponse;
 use Tests\TestCase;
@@ -130,7 +133,7 @@ class ZipImportTest extends TestCase
     {
         $admin = $this->users->admin();
         $this->actingAs($admin);
-        $resp = $this->runImportFromFile($this->zipUploadFromData([
+        $data = [
             'book' => [
                 'name' => 'My great book name',
                 'chapters' => [
@@ -149,13 +152,13 @@ class ZipImportTest extends TestCase
                     ]
                 ],
             ],
-        ]));
+        ];
+
+        $resp = $this->runImportFromFile($this->zipUploadFromData($data));
 
         $this->assertDatabaseHas('imports', [
             'name' => 'My great book name',
-            'book_count' => 1,
-            'chapter_count' => 1,
-            'page_count' => 2,
+            'type' => 'book',
             'created_by' => $admin->id,
         ]);
 
@@ -168,11 +171,25 @@ class ZipImportTest extends TestCase
 
     public function test_import_show_page()
     {
-        $import = Import::factory()->create(['name' => 'MySuperAdminImport']);
+        $exportBook = new ZipExportBook();
+        $exportBook->name = 'My exported book';
+        $exportChapter = new ZipExportChapter();
+        $exportChapter->name = 'My exported chapter';
+        $exportPage = new ZipExportPage();
+        $exportPage->name = 'My exported page';
+        $exportBook->chapters = [$exportChapter];
+        $exportChapter->pages = [$exportPage];
+
+        $import = Import::factory()->create([
+            'name' => 'MySuperAdminImport',
+            'metadata' => json_encode($exportBook)
+        ]);
 
         $resp = $this->asAdmin()->get("/import/{$import->id}");
         $resp->assertOk();
-        $resp->assertSee('MySuperAdminImport');
+        $resp->assertSeeText('My exported book');
+        $resp->assertSeeText('My exported chapter');
+        $resp->assertSeeText('My exported page');
     }
 
     public function test_import_show_page_access_limited()