tao-test/app/taoMediaManager/model/MediaSource.php

447 lines
14 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2014-2020 (original work) Open Assessment Technologies SA;
*/
namespace oat\taoMediaManager\model;
use oat\generis\model\OntologyAwareTrait;
use oat\oatbox\Configurable;
use oat\oatbox\log\LoggerAwareTrait;
use oat\oatbox\service\ServiceManager;
use oat\tao\model\accessControl\AccessControlEnablerInterface;
use oat\tao\model\media\MediaManagement;
use oat\tao\model\media\mediaSource\DirectorySearchQuery;
use oat\tao\model\media\ProcessedFileStreamAware;
use oat\taoMediaManager\model\mapper\MediaSourcePermissionsMapper;
use oat\taoMediaManager\model\export\service\MediaResourcePreparer;
use oat\taoMediaManager\model\fileManagement\FileManagement;
use oat\taoMediaManager\model\fileManagement\FileSourceUnserializer;
use Psr\Http\Message\StreamInterface;
use tao_helpers_Uri;
use tao_models_classes_FileNotFoundException;
use function GuzzleHttp\Psr7\stream_for;
class MediaSource extends Configurable implements MediaManagement, ProcessedFileStreamAware, AccessControlEnablerInterface
{
use LoggerAwareTrait;
use OntologyAwareTrait;
public const SCHEME_NAME = 'taomedia://mediamanager/';
/** @var MediaService */
protected $mediaService;
/** @var FileManagement */
protected $fileManagementService;
/** @var MediaSourcePermissionsMapper */
private $permissionsMapper;
/** @var string[] */
private $tmpFiles = [];
public function enableAccessControl(): AccessControlEnablerInterface
{
$this->getPermissionsMapper()->enableAccessControl();
return $this;
}
/**
* Returns the language URI to be used
* @return string
*/
protected function getLanguage()
{
return $this->hasOption('lang')
? $this->getOption('lang')
: '';
}
public function getRootClass()
{
return $this->getClass($this->getRootClassUri());
}
/**
* (non-PHPdoc)
*
* @see \oat\tao\model\media\MediaManagement::add
*/
public function add($source, $fileName, $parent, $mimetype = null)
{
if (!file_exists($source)) {
throw new tao_models_classes_FileNotFoundException($source);
}
$clazz = $this->getOrCreatePath($parent);
$service = $this->getMediaService();
$instanceUri = $service->createMediaInstance($source, $clazz->getUri(), $this->getLang(), $fileName, $mimetype);
return $this->getFileInfo($instanceUri);
}
/**
* (non-PHPdoc)
*
* @see \oat\tao\model\media\MediaManagement::delete
*/
public function delete($link)
{
return $this->getMediaService()->deleteResource($this->getResource(tao_helpers_Uri::decode($link)));
}
public function getDirectories(DirectorySearchQuery $params): array
{
return $this->searchDirectories(
$params->getParentLink(),
$params->getFilter(),
$params->getDepth(),
$params->getChildrenLimit(),
$params->getChildrenOffset()
);
}
/**
* @inheritDoc
*/
public function getDirectory($parentLink = '', $acceptableMime = [], $depth = 1)
{
return $this->searchDirectories($parentLink, $acceptableMime, $depth, 0, 0);
}
/**
* (non-PHPdoc)
*
* @see \oat\tao\model\media\MediaBrowser::getFileInfo
*/
public function getFileInfo($link)
{
// get the media link from the resource
$resource = $this->getResource(tao_helpers_Uri::decode($this->removeSchemaFromUriOrLink($link)));
$properties = [
$this->getProperty(MediaService::PROPERTY_LINK),
$this->getProperty(MediaService::PROPERTY_MIME_TYPE),
$this->getProperty(MediaService::PROPERTY_ALT_TEXT)
];
$propertiesValues = $resource->getPropertiesValues($properties);
$fileLink = $propertiesValues[MediaService::PROPERTY_LINK][0] ?? null;
$mime = $propertiesValues[MediaService::PROPERTY_MIME_TYPE][0] ?? null;
$fileLink = $fileLink instanceof \core_kernel_classes_Resource ? $fileLink->getUri() : (string)$fileLink;
$fileLink = $this->getFileSourceUnserializer()->unserialize($fileLink);
if (!isset($mime, $fileLink)) {
throw new tao_models_classes_FileNotFoundException($link);
}
// add the alt text to file array
$altArray = $propertiesValues[MediaService::PROPERTY_ALT_TEXT] ?? null;
$alt = $resource->getLabel();
if (count($altArray) > 0) {
$alt = (string)$altArray[0];
}
return $this->getPermissionsMapper()->map(
[
'name' => $resource->getLabel(),
'uri' => self::SCHEME_NAME . tao_helpers_Uri::encode($link),
'mime' => (string)$mime,
'size' => $this->getFileManagement()->getFileSize($fileLink),
'alt' => $alt,
'link' => $fileLink
],
$resource->getUri()
);
}
/**
* @param string $link
* @return \Psr\Http\Message\StreamInterface
* @throws \core_kernel_persistence_Exception
* @throws tao_models_classes_FileNotFoundException
*/
public function getFileStream($link)
{
$resource = $this->getResource(tao_helpers_Uri::decode($link));
$fileLink = $resource->getOnePropertyValue($this->getProperty(MediaService::PROPERTY_LINK));
if (is_null($fileLink)) {
throw new tao_models_classes_FileNotFoundException($link);
}
$fileLink = $fileLink instanceof \core_kernel_classes_Resource ? $fileLink->getUri() : (string)$fileLink;
$fileLink = $this->getFileSourceUnserializer()->unserialize($fileLink);
return $this->getFileManagement()->getFileStream($fileLink);
}
/**
* (non-PHPdoc)
*
* @see \oat\tao\model\media\MediaBrowser::download
* @deprecated
*/
public function download($link)
{
$this->logInfo('Deprecated, creates tmpfiles');
$stream = $this->getFileStream($link);
$filename = tempnam(sys_get_temp_dir(), 'media');
$fh = fopen($filename, 'w');
while (!$stream->eof()) {
fwrite($fh, $stream->read(1048576));
}
fclose($fh);
$this->tmpFiles[] = $filename;
return $filename;
}
/**
* @param string $link
* @return string
* @throws \core_kernel_persistence_Exception
* @throws tao_models_classes_FileNotFoundException
*/
public function getBaseName($link)
{
$stream = $this->getFileStream($link);
$filename = $stream->getMetadata('uri');
if ($filename === 'php://temp') {
// We are currently retrieving a remote resource (e.g. on Amazon S3).
$fileinfo = $this->getFileInfo($link);
$filename = $fileinfo['link'];
}
return basename($filename);
}
/**
* Force the mime-type of a resource
*
* @param string $link
* @param string $mimeType
* @return boolean
*/
public function forceMimeType($link, $mimeType)
{
$resource = $this->getResource(tao_helpers_Uri::decode($link));
return $resource->editPropertyValues($this->getProperty(MediaService::PROPERTY_MIME_TYPE), $mimeType);
}
/**
*
* @param string $path
* @return \core_kernel_classes_Class
*/
private function getOrCreatePath($path)
{
$rootClass = $this->getRootClass();
if ($path === '') {
return $rootClass;
}
// If the path is a class URI, returns the existing class.
$class = $this->getClass(tao_helpers_Uri::decode($path));
if ($class->isSubClassOf($rootClass) || $class->equals($rootClass) || $class->exists()) {
return $class;
}
// If the given path is a json-encoded array, creates the full path from root class.
$labels = $this->getArrayFromJson($path);
if ($labels) {
return $rootClass->createSubClassPathByLabel($labels);
}
// Retrieve or create a direct subclass of the root class.
return $rootClass->retrieveOrCreateSubClassByLabel($path);
}
/**
* Tries to find a json-encoded array in the given string.
*
* If string is actually a json string and a json-encoded array, returns the array.
* Else, returns false.
*
* @param string $string
* @return array|bool
*/
private function getArrayFromJson($string)
{
$decoded = json_decode($string);
return $decoded !== null && is_array($decoded)
? $decoded
: false;
}
/**
* Get the service Locator
*
* @return ServiceManager
*/
protected function getServiceLocator()
{
return ServiceManager::getServiceManager();
}
protected function getRootClassUri()
{
return $this->hasOption('rootClass') ? $this->getOption('rootClass') : MediaService::singleton()->getRootClass();
}
protected function getLang()
{
return $this->hasOption('lang') ? $this->getOption('lang') : '';
}
/**
* @return MediaService
*/
protected function getMediaService()
{
if (!$this->mediaService) {
$this->mediaService = MediaService::singleton();
}
return $this->mediaService;
}
/**
* @return FileManagement
*/
protected function getFileManagement()
{
if (!$this->fileManagementService) {
$this->fileManagementService = $this->getServiceLocator()->get(FileManagement::SERVICE_ID);
}
return $this->fileManagementService;
}
private function removeSchemaFromUriOrLink(string $uriOrLink): string
{
return str_replace(self::SCHEME_NAME, '', $uriOrLink);
}
public function getProcessedFileStream(string $link): StreamInterface
{
return stream_for(
$this->getPreparer()->prepare(
$this->getResource(tao_helpers_Uri::decode($link)),
$this->getFileStream($link)
)
);
}
private function getPreparer(): MediaResourcePreparer
{
return $this->getServiceLocator()->get(MediaResourcePreparer::class);
}
private function getFileSourceUnserializer(): FileSourceUnserializer
{
return $this->getServiceLocator()->get(FileSourceUnserializer::class);
}
private function searchDirectories(
string $parentLink = '',
array $acceptableMime = [],
int $depth = 1,
int $childrenLimit = 0,
int $childrenOffset = 0
): array {
$class = $this->getClass($parentLink == '' ? $this->getRootClassUri() : tao_helpers_Uri::decode($parentLink));
$data = $this->getPermissionsMapper()->map(
[
'path' => self::SCHEME_NAME . tao_helpers_Uri::encode($class->getUri()),
'label' => $class->getLabel(),
'childrenLimit' => $childrenLimit,
],
$class->getUri()
);
if ($depth > 0) {
$children = [];
foreach ($class->getSubClasses() as $subclass) {
$children[] = $this->searchDirectories(
$subclass->getUri(),
$acceptableMime,
$depth - 1,
$childrenLimit,
$childrenOffset
);
}
$filter = [];
if (!empty($acceptableMime)) {
$filter = array_merge($filter, [MediaService::PROPERTY_MIME_TYPE => $acceptableMime]);
}
$options = array_filter([
'limit' => $childrenLimit,
'offset' => $childrenOffset,
]);
foreach ($class->searchInstances($filter, $options) as $instance) {
try {
$children[] = $this->getFileInfo($instance->getUri());
} catch (tao_models_classes_FileNotFoundException $e) {
$this->logEmergency(
sprintf(
'Encountered issues "%s" while fetching details for %s',
$e->getMessage(),
$instance->getUri()
)
);
}
}
$data['children'] = $children;
$data['total'] = $class->countInstances($filter);
} else {
$data['parent'] = $parentLink;
}
return $data;
}
private function getPermissionsMapper(): MediaSourcePermissionsMapper
{
if (!$this->permissionsMapper) {
$this->permissionsMapper = $this->getServiceLocator()->get(MediaSourcePermissionsMapper::class);
}
return $this->permissionsMapper;
}
public function __destruct()
{
foreach ($this->tmpFiles as $tmpFile) {
if (is_writable($tmpFile)) {
unlink($tmpFile);
}
}
}
}