added parsed content field, fixed directory listing bug

This commit is contained in:
Dennis Eichhorn 2022-03-11 23:13:15 +01:00
parent 2e067f63f0
commit 7227aabc9b
10 changed files with 326 additions and 61 deletions

View File

@ -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",

View File

@ -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);

View File

@ -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();

View File

@ -45,6 +45,14 @@ class Media implements \JsonSerializable
*/
public string $name = '';
/**
* Content.
*
* @var null|MediaContent
* @since 1.0.0
*/
public ?MediaContent $content = null;
/**
* Type.
*

70
Models/MediaContent.php Normal file
View File

@ -0,0 +1,70 @@
<?php
/**
* Karaka
*
* PHP Version 8.0
*
* @package Modules\Media\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://karaka.app
*/
declare(strict_types=1);
namespace Modules\Media\Models;
use Modules\Admin\Models\Account;
use Modules\Admin\Models\NullAccount;
use Modules\Tag\Models\NullTag;
use Modules\Tag\Models\Tag;
/**
* Media class.
*
* @package Modules\Media\Models
* @license OMS License 1.0
* @link https://karaka.app
* @since 1.0.0
*/
class MediaContent implements \JsonSerializable
{
/**
* ID.
*
* @var int
* @since 1.0.0
*/
protected int $id = 0;
public string $content = '';
/**
* @return int
*
* @since 1.0.0
*/
public function getId() : int
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function toArray() : array
{
return [
'id' => $this->id,
'content' => $this->content,
];
}
/**
* {@inheritdoc}
*/
public function jsonSerialize()
{
return $this->toArray();
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* Karaka
*
* PHP Version 8.0
*
* @package Modules\Media\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://karaka.app
*/
declare(strict_types=1);
namespace Modules\Media\Models;
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
/**
* Media mapper class.
*
* @package Modules\Media\Models
* @license OMS License 1.0
* @link https://karaka.app
* @since 1.0.0
*/
class MediaContentMapper extends DataMapperFactory
{
/**
* Columns.
*
* @var array<string, array{name:string, type:string, internal:string, autocomplete?:bool, readonly?:bool, writeonly?:bool, annotations?:array}>
* @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';
}

View File

@ -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)

View File

@ -0,0 +1,38 @@
<?php
/**
* Karaka
*
* PHP Version 8.0
*
* @package Modules\Media\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://karaka.app
*/
declare(strict_types=1);
namespace Modules\Media\Models;
/**
* Media class.
*
* @package Modules\Media\Models
* @license OMS License 1.0
* @link https://karaka.app
* @since 1.0.0
*/
final class NullMediaContent extends MediaContent
{
/**
* Constructor
*
* @param int $id Model id
*
* @since 1.0.0
*/
public function __construct(int $id = 0)
{
$this->id = $id;
}
}

View File

@ -18,6 +18,20 @@ use \phpOMS\Uri\UriFactory;
<section id="mediaFile" class="portlet">
<div class="portlet-body">
<iframe style="min-height: 600px;" data-form="iUiSettings" data-name="iframeHelper" id="iHelperFrame" src="<?= UriFactory::build('{/backend}Resources/mozilla/Pdf/web/viewer.html?{?}&file=' . \urlencode(($this->media->isAbsolute ? '' : '/../../../../') . $this->media->getPath())); ?>" allowfullscreen></iframe>
<div id="media" class="tabview tab-2 m-editor">
<ul class="tab-links">
<li><label tabindex="0" for="media-c-tab-1"><?= $this->getHtml('Preview', 'Media'); ?></label>
<li><label tabindex="0" for="media-c-tab-2"><?= $this->getHtml('Content', 'Media'); ?></label>
</ul>
<div class="tab-content">
<input type="radio" id="media-c-tab-1" name="tabular-1" checked>
<div class="tab">
<iframe style="min-height: 600px;" data-form="iUiSettings" data-name="iframeHelper" id="iHelperFrame" src="<?= UriFactory::build('{/backend}Resources/mozilla/Pdf/web/viewer.html?{?}&file=' . \urlencode(($this->media->isAbsolute ? '' : '/../../../../') . $this->media->getPath())); ?>" allowfullscreen></iframe>
</div>
<input type="radio" id="media-c-tab-2" name="tabular-1">
<div class="tab">
<pre class="textContent" data-tpl-text="/media/content" data-tpl-value="/media/content"><?= $this->printHtml($this->media->content->content); ?></pre>
</div>
</div>
</div>
</section>

View File

@ -207,7 +207,7 @@ $next = empty($media) ? '{/prefix}media/list' : '{/prefix}media/list?{?}&id=
<td data-label="<?= $this->getHtml('Size'); ?>"><a href="<?= $url; ?>"><?php
$size = FileSizeType::autoFormat($value->size);
echo $this->printHtml($value->extension !== 'collection' ? \number_format($size[0], 1, '.', ',') . $size[1] : ''); ?></a>
<td data-label="<?= $this->getHtml('Creator'); ?>"><a class="content" href="<?= UriFactory::build('{/prefix}profile/single?{?}&for=' . $value->createdBy->getId()); ?>"><?= $this->printHtml($value->createdBy->name1); ?></a>
<td data-label="<?= $this->getHtml('Creator'); ?>"><a class="content" href="<?= UriFactory::build('{/prefix}profile/single?{?}&for=' . $value->createdBy->getId()); ?>"><?= $this->printHtml($this->renderUserName('%3$s %2$s %1$s', [$value->createdBy->name1, $value->createdBy->name2, $value->createdBy->name3, $value->createdBy->login ?? ''])); ?></a>
<td data-label="<?= $this->getHtml('Created'); ?>"><a href="<?= $url; ?>"><?= $this->printHtml($value->createdAt->format('Y-m-d')); ?></a>
<?php endforeach; ?>
<?php if ($count === 0) : ?>