diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index baecf69..46d181b 100644 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -13,36 +13,6 @@ "permission": { "permission": 2, "type": null, "element": null }, "parent": 1006901001, "children": [ - { - "id": 1000402001, - "pid": "/media", - "type": 3, - "subtype": 1, - "name": "List", - "uri": "{/prefix}media/list?{?}", - "target": "self", - "icon": null, - "order": 1, - "from": "Media", - "permission": { "permission": 2, "type": null, "element": null }, - "parent": 1000401001, - "children": [] - }, - { - "id": 1000403001, - "pid": "/media", - "type": 3, - "subtype": 5, - "name": "Create", - "uri": "{/prefix}media/create?{?}", - "target": "self", - "icon": null, - "order": 5, - "from": "Media", - "permission": { "permission": 4, "type": null, "element": null }, - "parent": 1000401001, - "children": [] - } ] }, { diff --git a/Admin/Install/db.json b/Admin/Install/db.json index c1e2936..16d7466 100644 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -37,6 +37,11 @@ "type": "TINYINT", "null": false }, + "media_hidden": { + "name": "media_hidden", + "type": "TINYINT", + "null": false + }, "media_file": { "name": "media_file", "type": "VARCHAR(255)", diff --git a/Admin/Installer.php b/Admin/Installer.php index dcfa216..aae1d92 100644 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -84,6 +84,7 @@ class Installer extends InstallerAbstract $collection = new Collection(); $collection->setName((string) $data['name'] ?? ''); $collection->setVirtualPath((string) $data['virtualPath'] ?? '/'); + $collection->setPath((string) $data['virtualPath'] ?? '/'); $collection->setCreatedBy((int) $data['user'] ?? 1); CollectionMapper::create($collection); diff --git a/Admin/Routes/Web/Api.php b/Admin/Routes/Web/Api.php index 0fe81c0..ddf2310 100644 --- a/Admin/Routes/Web/Api.php +++ b/Admin/Routes/Web/Api.php @@ -15,11 +15,6 @@ return [ 'type' => PermissionType::CREATE, 'state' => PermissionState::MEDIA, ], - 'data' => [ - 'field_name' => [ - 'type' => 'string', 'default' => 'Hello', 'validation' => '[\\w]*', 'required' => false,'annotation' => [], - ], - ], ], [ 'dest' => '\Modules\Media\Controller\ApiController:apiMediaUpdate', diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index f242f1b..ad36797 100644 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -17,9 +17,9 @@ return [ ], ], ], - '^.*/media/create.*$' => [ + '^.*/media/upload.*$' => [ [ - 'dest' => '\Modules\Media\Controller\BackendController:setUpFileUploader', + 'dest' => '\Modules\Media\Controller\BackendController:viewMediaUpload', 'verb' => RouteVerb::GET, 'permission' => [ 'module' => BackendController::MODULE_NAME, @@ -27,8 +27,21 @@ return [ 'state' => PermissionState::MEDIA, ], ], + ], + '^.*/media/file/create.*$' => [ [ - 'dest' => '\Modules\Media\Controller\BackendController:viewMediaCreate', + 'dest' => '\Modules\Media\Controller\BackendController:viewMediaFileCreate', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::CREATE, + 'state' => PermissionState::MEDIA, + ], + ], + ], + '^.*/media/collection/create.*$' => [ + [ + 'dest' => '\Modules\Media\Controller\BackendController:viewMediaCollectionCreate', 'verb' => RouteVerb::GET, 'permission' => [ 'module' => BackendController::MODULE_NAME, diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 0920783..f3fc8c6 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -20,6 +20,7 @@ use Modules\Media\Models\CollectionMapper; use Modules\Media\Models\Media; use Modules\Media\Models\MediaMapper; use Modules\Media\Models\NullCollection; +use Modules\Media\Models\PathSettings; use Modules\Media\Models\PermissionState; use Modules\Media\Models\UploadFile; use Modules\Media\Models\UploadStatus; @@ -102,7 +103,8 @@ final class ApiController extends Controller (string) ($request->getData('path') ?? __DIR__ . '/../../../Modules/Media/Files'), (string) ($request->getData('virtualPath') ?? ''), (string) ($request->getData('password') ?? ''), - (string) ($request->getData('encrypt') ?? '') + (string) ($request->getData('encrypt') ?? ''), + (int) ($request->getData('pathsettings') ?? PathSettings::RANDOM_PATH) ); $ids = []; @@ -114,10 +116,19 @@ final class ApiController extends Controller } /** - * @param string $name Name - * @param array $files Files - * @param int $account Uploader - * @param string $basePath Base path + * Upload a media file + * + * @param string $name Name + * @param array $files Files + * @param int $account Uploader + * @param string $basePath Base path. The path which is used for the upload. + * @param string $virtualPath Virtual path The path which is used to visually structure the files, like directories. + * The file storage on the system can be different + * @param string $password File password. The password to protect the file (only database) + * @param string $encryptionKey Encryption key. Used to encrypt the file on the local file storage. + * @param int $pathSettings Settings which describe where the file should be uploaded to (physically) + * RANDOM_PATH = random location in the base path + * FILE_PATH = combination of base path and virtual path * * @return array * @@ -130,16 +141,28 @@ final class ApiController extends Controller string $basePath = 'Modules/Media/Files', string $virtualPath = '', string $password = '', - string $encryptionKey = '' - ) : array - { + string $encryptionKey = '', + int $pathSettings = PathSettings::RANDOM_PATH + ) : array { $mediaCreated = []; if (!empty($files)) { - $upload = new UploadFile(); - $upload->setOutputDir(self::createMediaPath($basePath)); + $outputDir = ''; + $absolute = false; - $status = $upload->upload($files, $name, $encryptionKey); + if ($pathSettings === PathSettings::RANDOM_PATH) { + $outputDir = self::createMediaPath($basePath); + } elseif ($pathSettings === PathSettings::FILE_PATH) { + $outputDir = $basePath . $virtualPath; + $absolute = true; + } else { + return $mediaCreated; + } + + $upload = new UploadFile(); + $upload->setOutputDir($outputDir); + + $status = $upload->upload($files, $name, $absolute, $encryptionKey); $mediaCreated = $this->createDbEntries($status, $account, $virtualPath); } @@ -332,6 +355,7 @@ final class ApiController extends Controller $mediaCollection->setCreatedBy($account); $mediaCollection->setSources($media); $mediaCollection->setVirtualPath('/Modules/Helper'); + $mediaCollection->setPath('/Modules/Helper'); CollectionMapper::create($mediaCollection); diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 1d8f9f6..237b77a 100644 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -14,11 +14,11 @@ declare(strict_types=1); namespace Modules\Media\Controller; +use Modules\Admin\Models\Account; use Modules\Media\Models\CollectionMapper; use Modules\Media\Models\Media; use Modules\Media\Models\MediaMapper; use Modules\Media\Views\MediaView; - use phpOMS\Asset\AssetType; use phpOMS\Contract\RenderableInterface; use phpOMS\Message\RequestAbstract; @@ -127,11 +127,34 @@ final class BackendController extends Controller { $view = new View($this->app->l11nManager, $request, $response); $view->setTemplate('/Modules/Media/Theme/Backend/media-list'); - $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1000401001, $request, $response)); $path = (string) ($request->getData('path') ?? '/'); $media = MediaMapper::getByVirtualPath($path); + $collection = CollectionMapper::getParentCollection($path); + if (!empty($collection)) { + $media += $collection->getSources(); + + $glob = \glob(__DIR__ . '/../Files' . \trim($collection->getPath(), '/') . '/' . $collection->getName() . '/*'); + + foreach ($glob as $file) { + foreach ($media as $obj) { + if ($obj->getName() . '.' . $obj->getExtension() === \basename($file)){ + continue 2; + } + } + + $pathinfo = \pathinfo($file); + + $localMedia = new Media(); + $localMedia->setName($pathinfo['filename']); + $localMedia->setExtension($pathinfo['extension']); + $localMedia->setCreatedBy(new Account()); + + $media[] = $localMedia; + } + } + $view->addData('media', $media); $view->addData('path', $path); @@ -193,11 +216,50 @@ final class BackendController extends Controller * @since 1.0.0 * @codeCoverageIgnore */ - public function viewMediaCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface + public function viewMediaUpload(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface { $view = new View($this->app->l11nManager, $request, $response); - $view->setTemplate('/Modules/Media/Theme/Backend/media-create'); - $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1000401001, $request, $response)); + $view->setTemplate('/Modules/Media/Theme/Backend/media-upload'); + + return $view; + } + + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewMediaFileCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/Media/Theme/Backend/media-file-create'); + + return $view; + } + + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewMediaCollectionCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/Media/Theme/Backend/media-collection-create'); return $view; } diff --git a/Models/Collection.php b/Models/Collection.php index 0c9a1dd..ed62aab 100644 --- a/Models/Collection.php +++ b/Models/Collection.php @@ -27,7 +27,7 @@ class Collection extends Media implements \Iterator /** * Resource id. * - * @var array + * @var array * @since 1.0.0 */ private $sources = []; diff --git a/Models/Media.php b/Models/Media.php index f34bbbc..a7f498f 100644 --- a/Models/Media.php +++ b/Models/Media.php @@ -136,6 +136,14 @@ class Media implements \JsonSerializable */ protected ?string $password = null; + /** + * Media is hidden. + * + * @var bool + * @since 1.0.0 + */ + protected bool $hidden = false; + /** * Constructor. * @@ -494,6 +502,28 @@ class Media implements \JsonSerializable $this->versioned = $versioned; } + /** + * @return bool + * + * @since 1.0.0 + */ + public function isHidden() : bool + { + return $this->hidden; + } + + /** + * @param bool $hidden File is hidden + * + * @return void + * + * @since 1.0.0 + */ + public function setHidden(bool $hidden) : void + { + $this->hidden = $hidden; + } + /** * {@inheritdoc} */ @@ -504,7 +534,9 @@ class Media implements \JsonSerializable 'name' => $this->name, 'description' => $this->description, 'extension' => $this->extension, + 'virtualpath' => $this->virtualPath, 'size' => $this->size, + 'hidden' => $this->hidden, ]; } diff --git a/Models/MediaMapper.php b/Models/MediaMapper.php index 593441e..a7e7f46 100644 --- a/Models/MediaMapper.php +++ b/Models/MediaMapper.php @@ -39,6 +39,7 @@ class MediaMapper extends DataMapperAbstract 'media_description' => ['name' => 'media_description', 'type' => 'string', 'internal' => 'description', 'autocomplete' => true], 'media_description_raw' => ['name' => 'media_description_raw', 'type' => 'string', 'internal' => 'descriptionRaw'], 'media_versioned' => ['name' => 'media_versioned', 'type' => 'bool', 'internal' => 'versioned'], + 'media_hidden' => ['name' => 'media_hidden', 'type' => 'bool', 'internal' => 'hidden'], 'media_file' => ['name' => 'media_file', 'type' => 'string', 'internal' => 'path', 'autocomplete' => true], 'media_virtual' => ['name' => 'media_virtual', 'type' => 'string', 'internal' => 'virtualPath', 'autocomplete' => true], 'media_absolute' => ['name' => 'media_absolute', 'type' => 'bool', 'internal' => 'isAbsolute'], @@ -103,16 +104,44 @@ class MediaMapper extends DataMapperAbstract * path if so desired without deleting or moving the orginal media files. * * @param string $virtualPath Virtual path + * @param string $hidden Get hidden files * * @return array * * @since 1.0.0 */ - public static function getByVirtualPath(string $virtualPath = '/') : array + public static function getByVirtualPath(string $virtualPath = '/', bool $hidden = false) : array { $query = self::getQuery(); $query->where(self::$table . '.media_virtual', '=', $virtualPath); + if ($hidden === false) { + $query->where(self::$table . '.media_hidden', '=', (int) $hidden); + } + return self::getAllByQuery($query); } + + /** + * Get parent collection + * + * @param string $path Virtual path + * + * @return mixed + * + * @since 1.0.0 + */ + public static function getParentCollection(string $path = '/') + { + $virtualPath = \substr($path, 0, \strripos($path, '/') + 1); + $name = \substr($path, \strripos($path, '/') + 1); + + $query = self::getQuery(); + $query->where(self::$table . '.media_virtual', '=', $virtualPath) + ->andWhere(self::$table . '.media_name', '=', $name); + + $objs = self::getAllByQuery($query); + + return \count($objs) === 1 ? \reset($objs) : $objs; + } } diff --git a/Models/PathSettings.php b/Models/PathSettings.php new file mode 100644 index 0000000..a537163 --- /dev/null +++ b/Models/PathSettings.php @@ -0,0 +1,32 @@ + 1) { - $this->outputDir = $this->findOutputDir($files); + if (!$absolute && \count($files) > 1) { + $this->outputDir = $this->findOutputDir(); } $path = $this->outputDir; @@ -308,13 +314,11 @@ class UploadFile /** * Find unique output path for batch of files * - * @param array $files Array of files - * * @return string * * @since 1.0.0 */ - private function findOutputDir(array $files) : string + private function findOutputDir() : string { do { $rndPath = \str_pad(\dechex(\mt_rand(0, 65535)), 4, '0', \STR_PAD_LEFT); diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index 1b4a34b..d632376 100644 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -14,6 +14,7 @@ declare(strict_types=1); return ['Media' => [ 'Account' => 'Account', + 'AddToCollection' => 'Add to collection', 'Author' => 'Author', 'Changed' => 'Changed', 'Changedby' => 'Changed by', @@ -24,11 +25,15 @@ return ['Media' => [ 'Edit' => 'Edit', 'Editability' => 'Editability', 'Extension' => 'Extension', + 'FilePath' => 'File Path', 'Files' => 'Files', 'Media' => 'Media', 'Name' => 'Name', + 'Path' => 'Path', + 'PathSettings' => 'Path Settings', 'Permission' => 'Permission', 'Preview' => 'Preview', + 'RandomPath' => 'Random Path', 'Select' => 'Select', 'Settings' => 'Settings', 'Size' => 'Size', diff --git a/Theme/Backend/media-collection-create.tpl.php b/Theme/Backend/media-collection-create.tpl.php new file mode 100644 index 0000000..e69de29 diff --git a/Theme/Backend/media-create.tpl.php b/Theme/Backend/media-create.tpl.php deleted file mode 100644 index 4382a32..0000000 --- a/Theme/Backend/media-create.tpl.php +++ /dev/null @@ -1,39 +0,0 @@ -getData('nav')->render(); ?> -
-
-
-

getHtml('Upload') ?>

-
-
- -
-
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/Theme/Backend/media-file-create.tpl.php b/Theme/Backend/media-file-create.tpl.php new file mode 100644 index 0000000..e69de29 diff --git a/Theme/Backend/media-list.tpl.php b/Theme/Backend/media-list.tpl.php index 3edffbf..8418fc0 100644 --- a/Theme/Backend/media-list.tpl.php +++ b/Theme/Backend/media-list.tpl.php @@ -12,6 +12,7 @@ */ declare(strict_types=1); +use Modules\Media\Models\Collection; use phpOMS\Uri\UriFactory; include __DIR__ . '/template-functions.php'; @@ -26,8 +27,17 @@ $mediaPath = $this->getData('path') ?? '/'; * @var \Modules\Media\Models\Media[] $media */ $media = $this->getData('media'); +?> -echo $this->getData('nav')->render(); ?> +
@@ -72,12 +82,18 @@ echo $this->getData('nav')->render(); ?> $value) : ++$count; - $url = UriFactory::build('{/prefix}media/single?id=' . $value->getId()); + + if ($value->getExtension() === 'collection') { + $url = UriFactory::build('{/prefix}media/list?path=' . \rtrim($value->getVirtualPath(), '/') . '/' . $value->getName()); + } else { + $url = UriFactory::build('{/prefix}media/single?id=' . $value->getId()); + } $icon = $fileIconFunction(\phpOMS\System\File\FileUtils::getExtensionType($value->getExtension())); ?> - printHtml($value->getName()); ?> + printHtml( + $value->getExtension() !== 'collection' ? $value->getName() . '.' . $value->getExtension() : $value->getName()); ?> printHtml($value->getExtension()); ?> printHtml($value->getSize()); ?> printHtml($value->getCreatedBy()->getName1()); ?> diff --git a/Theme/Backend/media-upload.tpl.php b/Theme/Backend/media-upload.tpl.php new file mode 100644 index 0000000..81efc0e --- /dev/null +++ b/Theme/Backend/media-upload.tpl.php @@ -0,0 +1,61 @@ + + +
+
+
+ Back +
+
+
+ +
+
+
+

getHtml('Upload') ?>

+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
\ No newline at end of file