From 7227aabc9b034d7c71db83364c3b68910d4dadfd Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 11 Mar 2022 23:13:15 +0100 Subject: [PATCH] added parsed content field, fixed directory listing bug --- Admin/Install/db.json | 25 ++++ Controller/ApiController.php | 147 +++++++++++++-------- Controller/BackendController.php | 11 +- Models/Media.php | 8 ++ Models/MediaContent.php | 70 ++++++++++ Models/MediaContentMapper.php | 63 +++++++++ Models/MediaMapper.php | 7 +- Models/NullMediaContent.php | 38 ++++++ Theme/Backend/Components/Media/pdf.tpl.php | 16 ++- Theme/Backend/media-list.tpl.php | 2 +- 10 files changed, 326 insertions(+), 61 deletions(-) create mode 100644 Models/MediaContent.php create mode 100644 Models/MediaContentMapper.php create mode 100644 Models/NullMediaContent.php diff --git a/Admin/Install/db.json b/Admin/Install/db.json index f00eda6..689249e 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -53,6 +53,23 @@ } } }, + "media_parsed": { + "name": "media_parsed", + "fields": { + "media_parsed_id": { + "name": "media_parsed_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "media_parsed_content": { + "name": "media_parsed_content", + "type": "LONGTEXT", + "null": false + } + } + }, "media": { "name": "media", "fields": { @@ -142,6 +159,14 @@ "default": null, "null": true }, + "media_content": { + "name": "media_content", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "media_parsed", + "foreignKey": "media_parsed_id" + }, "media_source": { "name": "media_source", "type": "INT", diff --git a/Controller/ApiController.php b/Controller/ApiController.php index efc4d8c..918e766 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -19,6 +19,7 @@ use Modules\Admin\Models\NullAccount; use Modules\Media\Models\Collection; use Modules\Media\Models\CollectionMapper; use Modules\Media\Models\Media; +use Modules\Media\Models\MediaContent; use Modules\Media\Models\MediaMapper; use Modules\Media\Models\NullCollection; use Modules\Media\Models\NullMedia; @@ -43,6 +44,11 @@ use phpOMS\System\File\Local\Directory; use phpOMS\System\MimeType; use phpOMS\Utils\Parser\Markdown\Markdown; use phpOMS\Views\View; +use PhpOffice\PhpWord\IOFactory; +use PhpOffice\PhpWord\Writer\HTML; +use phpOMS\Application\ApplicationAbstract; +use phpOMS\Autoloader; +use phpOMS\Utils\Parser\Pdf\PdfParser; /** * Media class. @@ -133,6 +139,7 @@ final class ApiController extends Controller * @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 + * @param bool $hasAccountRelation The uploaded files should be related to an account * * @return array * @@ -148,7 +155,8 @@ final class ApiController extends Controller int $type = null, string $password = '', string $encryptionKey = '', - int $pathSettings = PathSettings::RANDOM_PATH + int $pathSettings = PathSettings::RANDOM_PATH, + bool $hasAccountRelation = true ) : array { if (empty($files)) { @@ -174,12 +182,22 @@ final class ApiController extends Controller $sameLength = \count($names) === \count($status); $nCounter = -1; + + $created = []; foreach ($status as &$stat) { ++$nCounter; $stat['name'] = $sameLength ? $names[$nCounter] : $stat['filename']; + + $created[] = self::createDbEntry( + $stat, + $account, + $virtualPath, + $type, + app: $hasAccountRelation ? $this->app : null + ); } - return $this->createDbEntries($status, $account, $virtualPath, $type); + return $created; } /** @@ -220,54 +238,6 @@ final class ApiController extends Controller return $basePath . '/_' . $rndPath[0] . $rndPath[1] . $rndPath[2] . $rndPath[3] . '/_' . $rndPath[4] . $rndPath[5] . $rndPath[6] . $rndPath[7]; } - /** - * Create database entries for uploaded files - * - * @param array $status Files - * @param int $account Uploader - * @param string $virtualPath Virtual path - * @param null|int $type Media type (internal categorization = identifier for modules) - * @param string $ip Ip - * - * @return Media[] - * - * @since 1.0.0 - */ - public function createDbEntries( - array $status, - int $account, - string $virtualPath = '', - int $type = null, - string $ip = '127.0.0.1' - ) : array - { - $mediaCreated = []; - - foreach ($status as $uFile) { - if (($created = self::createDbEntry($uFile, $account, $virtualPath, $type)) !== null) { - $mediaCreated[] = $created; - - $this->app->moduleManager->get('Admin')->createAccountModelPermission( - new AccountPermission( - $account, - $this->app->orgId, - $this->app->appName, - self::NAME, - self::NAME, - PermissionState::MEDIA, - $created->getId(), - null, - PermissionType::READ | PermissionType::MODIFY | PermissionType::DELETE | PermissionType::PERMISSION - ), - $account, - $ip - ); - } - } - - return $mediaCreated; - } - /** * Create db entry for uploaded file * @@ -275,12 +245,20 @@ final class ApiController extends Controller * @param int $account Uploader * @param string $virtualPath Virtual path (not on the hard-drive) * @param null|int $type Media type (internal categorization) + * @param ApplicationAbstract $app Should create relation to uploader * * @return null|Media * * @since 1.0.0 */ - public static function createDbEntry(array $status, int $account, string $virtualPath = '', int $type = null) : ?Media + public static function createDbEntry( + array $status, + int $account, + string $virtualPath = '', + int $type = null, + string $ip = '127.0.0.1', + ApplicationAbstract $app = null + ) : ?Media { if ($status['status'] !== UploadStatus::OK) { return null; @@ -296,11 +274,63 @@ final class ApiController extends Controller $media->setVirtualPath($virtualPath); $media->type = $type === null ? null : new NullMediaType($type); + if (\is_file($media->getAbsolutePath())) { + $content = self::loadFileContent($media->getAbsolutePath(), $media->extension); + + if (!empty($content)) { + $media->content = new MediaContent(); + $media->content->content = $content; + } + } + MediaMapper::create()->execute($media); + if ($app !== null) { + $app->moduleManager->get('Admin')->createAccountModelPermission( + new AccountPermission( + $account, + $app->orgId, + $app->appName, + self::NAME, + self::NAME, + PermissionState::MEDIA, + $media->getId(), + null, + PermissionType::READ | PermissionType::MODIFY | PermissionType::DELETE | PermissionType::PERMISSION + ), + $account, + $ip + ); + } + return $media; } + private static function loadFileContent(string $path, string $extension) : string + { + switch ($extension) { + case 'pdf': + return PdfParser::pdf2text($path); + break; + case 'doc': + case 'docx': + Autoloader::addPath(__DIR__ . '/../../../Resources/'); + + $reader = IOFactory::createReader('Word2007'); + $doc = $reader->load($path); + + $writer = new HTML($doc); + return $writer->getContent(); + break; + case 'txt': + case 'md': + return \file_get_contents($path); + break; + default: + return ''; + }; + } + /** * Normalize the file path * @@ -624,11 +654,18 @@ final class ApiController extends Controller ], ]; - $created = $this->createDbEntries($status, $request->header->account, $virtualPath, $request->getData('type', 'int')); - $ids = []; - foreach ($created as $file) { - $ids[] = $file->getId(); + foreach ($status as $stat) { + $created = self::createDbEntry( + $status, + $request->header->account, + $virtualPath, + $request->getData('type', 'int'), + $request->getOrigin(), + $this->app + ); + + $ids[] = $created->getId(); } $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Media', 'Media successfully created.', $ids); diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 8129b19..ea53f93 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -123,7 +123,7 @@ final class BackendController extends Controller $collection = $collectionMapper->execute(); - if (\is_array($collection) && \is_dir(__DIR__ . '/../Files' . $path)) { + if ((\is_array($collection) || $collection instanceof NullCollection) && \is_dir(__DIR__ . '/../Files' . $path)) { $collection = new Collection(); $collection->name = \basename($path); $collection->setVirtualPath(\dirname($path)); @@ -149,6 +149,8 @@ final class BackendController extends Controller : \glob(__DIR__ . '/../Files/' . \trim($collection->getVirtualPath(), '/') . '/' . $collection->name . '/*'); $glob = $glob === false ? [] : $glob; + $unIndexedFiles = []; + foreach ($glob as $file) { $basename = \basename($file); if ($basename[0] === '_' && \strlen($basename) === 5) { @@ -158,7 +160,7 @@ final class BackendController extends Controller foreach ($media as $obj) { if ($obj->name === $basename || $obj->name . '.' . $obj->extension === $basename - || StringUtils::endsWith(\realpath($file), $obj->getPath()) + || ($obj->getPath() !== '' && StringUtils::endsWith(\realpath($file), $obj->getPath())) ) { continue 2; } @@ -172,8 +174,10 @@ final class BackendController extends Controller $localMedia->setVirtualPath($path); $localMedia->createdBy = new Account(); - $media[] = $localMedia; + $unIndexedFiles[] = $localMedia; } + + $media = \array_merge($media, $unIndexedFiles); } $view->addData('media', $media); @@ -221,6 +225,7 @@ final class BackendController extends Controller ->with('createdBy') ->with('tags') ->with('tags/title') + ->with('content') ->where('id', $id) ->where('tags/title/language', $request->getLanguage()) ->execute(); diff --git a/Models/Media.php b/Models/Media.php index 400f929..7419839 100755 --- a/Models/Media.php +++ b/Models/Media.php @@ -45,6 +45,14 @@ class Media implements \JsonSerializable */ public string $name = ''; + /** + * Content. + * + * @var null|MediaContent + * @since 1.0.0 + */ + public ?MediaContent $content = null; + /** * Type. * diff --git a/Models/MediaContent.php b/Models/MediaContent.php new file mode 100644 index 0000000..3102205 --- /dev/null +++ b/Models/MediaContent.php @@ -0,0 +1,70 @@ +id; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return [ + 'id' => $this->id, + 'content' => $this->content, + ]; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/MediaContentMapper.php b/Models/MediaContentMapper.php new file mode 100644 index 0000000..344625c --- /dev/null +++ b/Models/MediaContentMapper.php @@ -0,0 +1,63 @@ + + * @since 1.0.0 + */ + public const COLUMNS = [ + 'media_parsed_id' => ['name' => 'media_parsed_id', 'type' => 'int', 'internal' => 'id'], + 'media_parsed_content' => ['name' => 'media_parsed_content', 'type' => 'string', 'internal' => 'content'], + ]; + + /** + * Model to use by the mapper. + * + * @var string + * @since 1.0.0 + */ + public const MODEL = MediaContent::class; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'media_parsed'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD ='media_parsed_id'; +} diff --git a/Models/MediaMapper.php b/Models/MediaMapper.php index 3e05c4d..13f2dbd 100755 --- a/Models/MediaMapper.php +++ b/Models/MediaMapper.php @@ -41,6 +41,7 @@ class MediaMapper extends DataMapperFactory 'media_type' => ['name' => 'media_type', 'type' => 'int', 'internal' => 'type'], 'media_description' => ['name' => 'media_description', 'type' => 'string', 'internal' => 'description', 'autocomplete' => true], 'media_description_raw' => ['name' => 'media_description_raw', 'type' => 'string', 'internal' => 'descriptionRaw'], + 'media_content' => ['name' => 'media_content', 'type' => 'int', 'internal' => 'content'], 'media_versioned' => ['name' => 'media_versioned', 'type' => 'bool', 'internal' => 'isVersioned'], 'media_hidden' => ['name' => 'media_hidden', 'type' => 'bool', 'internal' => 'isHidden'], 'media_file' => ['name' => 'media_file', 'type' => 'string', 'internal' => 'path', 'autocomplete' => true], @@ -84,6 +85,10 @@ class MediaMapper extends DataMapperFactory 'mapper' => self::class, 'external' => 'media_source', ], + 'content' => [ + 'mapper' => MediaContentMapper::class, + 'external' => 'media_content', + ], ]; /** @@ -180,7 +185,7 @@ class MediaMapper extends DataMapperFactory $virtualPath = '/' . \trim(\substr($path, 0, \strripos($path, '/') + 1), '/'); $name = \substr($path, \strripos($path, '/') + 1); - return self::get() + return CollectionMapper::get() ->with('sources') ->with('source') ->where('virtualPath', $virtualPath) diff --git a/Models/NullMediaContent.php b/Models/NullMediaContent.php new file mode 100644 index 0000000..d68e053 --- /dev/null +++ b/Models/NullMediaContent.php @@ -0,0 +1,38 @@ +id = $id; + } +} diff --git a/Theme/Backend/Components/Media/pdf.tpl.php b/Theme/Backend/Components/Media/pdf.tpl.php index dbe26cc..4a9b536 100755 --- a/Theme/Backend/Components/Media/pdf.tpl.php +++ b/Theme/Backend/Components/Media/pdf.tpl.php @@ -18,6 +18,20 @@ use \phpOMS\Uri\UriFactory;
- +
+ +
+ +
+ +
+ +
+
printHtml($this->media->content->content); ?>
+
+
\ No newline at end of file diff --git a/Theme/Backend/media-list.tpl.php b/Theme/Backend/media-list.tpl.php index dfa5a4e..856588a 100755 --- a/Theme/Backend/media-list.tpl.php +++ b/Theme/Backend/media-list.tpl.php @@ -207,7 +207,7 @@ $next = empty($media) ? '{/prefix}media/list' : '{/prefix}media/list?{?}&id= size); echo $this->printHtml($value->extension !== 'collection' ? \number_format($size[0], 1, '.', ',') . $size[1] : ''); ?> - printHtml($value->createdBy->name1); ?> + printHtml($this->renderUserName('%3$s %2$s %1$s', [$value->createdBy->name1, $value->createdBy->name2, $value->createdBy->name3, $value->createdBy->login ?? ''])); ?> printHtml($value->createdAt->format('Y-m-d')); ?>