tao-test/app/taoDeliveryRdf/model/export/AssemblyExporterService.php

202 lines
10 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) 2019 (original work) Open Assessment Technologies SA;
*/
namespace oat\taoDeliveryRdf\model\export;
use oat\taoDeliveryRdf\model\assembly\CompiledTestConverterFactory;
use ZipArchive;
use Exception;
use InvalidArgumentException;
use common_Exception;
use core_kernel_classes_EmptyProperty;
use tao_helpers_Display;
use tao_helpers_Export;
use tao_helpers_File;
use core_kernel_classes_Resource;
use common_ext_ExtensionsManager;
use tao_models_classes_service_ServiceCall;
use tao_models_classes_export_RdfExporter;
use oat\generis\model\OntologyAwareTrait;
use oat\oatbox\log\LoggerAwareTrait;
use oat\oatbox\service\ConfigurableService;
use oat\tao\model\service\ServiceFileStorage;
use oat\taoDeliveryRdf\model\assembly\AssemblyFilesReaderInterface;
use oat\taoDeliveryRdf\model\assembly\UnsupportedCompiledTestFormatException;
use oat\taoDeliveryRdf\model\DeliveryAssemblyService;
class AssemblyExporterService extends ConfigurableService
{
use LoggerAwareTrait;
use OntologyAwareTrait;
const SERVICE_ID = 'taoDeliveryRdf/AssemblyExporterService';
const OPTION_ASSEMBLY_FILES_READER = 'assembly_files_reader';
const OPTION_RDF_EXPORTER = 'rdf_exporter';
const MANIFEST_FILENAME = 'manifest.json';
const DELIVERY_RDF_FILENAME = 'delivery.rdf';
/**
* @var AssemblyFilesReaderInterface
*/
private $assemblyFilesReader;
/**
* @var tao_models_classes_export_RdfExporter
*/
private $rdfExporter;
/**
* AssemblyExporterService constructor.
* @param array $options
*/
public function __construct($options = [])
{
parent::__construct($options);
if (!$this->getOption(self::OPTION_ASSEMBLY_FILES_READER) instanceof AssemblyFilesReaderInterface) {
throw new InvalidArgumentException(sprintf('%s option value must be an instance of %s', self::OPTION_ASSEMBLY_FILES_READER, AssemblyFilesReaderInterface::class));
}
$this->rdfExporter = $this->getOption(self::OPTION_RDF_EXPORTER);
if (!$this->rdfExporter instanceof tao_models_classes_export_RdfExporter) {
throw new InvalidArgumentException('%s option value must be an instance of %s', self::OPTION_RDF_EXPORTER, tao_models_classes_export_RdfExporter::class);
}
}
/**
* Export Compiled Delivery
*
* Exports a delivery into its compiled form. In case of the $fsExportPath argument is set,
* the compiled delivery will be stored in the 'taoDelivery' shared file system, at $fsExportPath location.
*
* @param core_kernel_classes_Resource $compiledDelivery
* @param string $outputTestFormat Format compiled test file in output assembly package.
*
* @return string The path to the compiled delivery on the local file system OR the 'taoDelivery' shared file system, depending on whether $fsExportPath is set.
*
* @throws common_Exception
* @throws core_kernel_classes_EmptyProperty
*/
public function exportCompiledDelivery(core_kernel_classes_Resource $compiledDelivery, $outputTestFormat)
{
$this->logDebug("Exporting Delivery Assembly '" . $compiledDelivery->getUri() . "'...");
$fileName = tao_helpers_Display::textCleaner($compiledDelivery->getLabel()) . '.zip';
$path = tao_helpers_File::concat([tao_helpers_Export::getExportPath(), $fileName]);
if (!tao_helpers_File::securityCheck($path, true)) {
throw new Exception('Unauthorized file name');
}
// If such a target zip file exists, remove it from local filesystem. It prevents some synchronicity issues
// to occur while dealing with ZIP Archives (not explained yet).
if (file_exists($path)) {
unlink($path);
}
$zipArchive = new ZipArchive();
if ($zipArchive->open($path, ZipArchive::CREATE) !== true) {
throw new Exception('Unable to create archive at ' . $path);
}
$this->setupCompiledTestConverter($outputTestFormat);
$this->doExportCompiledDelivery($path, $compiledDelivery, $zipArchive);
$zipArchive->close();
return $path;
}
/**
* Do Export Compiled Delivery
*
* Method containing the main behavior of exporting a compiled delivery into a ZIP archive.
*
* For developers wanting to override this method, the following information has to be taken into account:
*
* - The value of the $zipArgive argument is an already open ZipArchive object.
* - The method must keep the archive open after its execution (calling code will take care of it).
*
* @param $path
* @param core_kernel_classes_Resource $compiledDelivery
* @param ZipArchive $zipArchive
* @throws common_Exception
* @throws core_kernel_classes_EmptyProperty
*/
protected function doExportCompiledDelivery($path, core_kernel_classes_Resource $compiledDelivery, ZipArchive $zipArchive)
{
$taoDeliveryVersion = common_ext_ExtensionsManager::singleton()->getInstalledVersion('taoDelivery');
$data = [
'dir' => [],
'label' => $compiledDelivery->getLabel(),
'version' => $taoDeliveryVersion
];
$directories = $compiledDelivery->getPropertyValues($this->getProperty(DeliveryAssemblyService::PROPERTY_DELIVERY_DIRECTORY));
foreach ($directories as $id) {
$directory = $this->getServiceLocator()->get(ServiceFileStorage::SERVICE_ID)->getDirectoryById($id);
foreach ($this->getAssemblyFilesReader()->getFiles($directory) as $filePath => $fileStream) {
tao_helpers_File::addFilesToZip($zipArchive, $fileStream, $filePath);
}
$data['dir'][$id] = $directory->getPrefix();
}
$runtime = $compiledDelivery->getOnePropertyValue(
$this->getProperty(DeliveryAssemblyService::PROPERTY_DELIVERY_RUNTIME)
);
$serviceCall = $runtime instanceof core_kernel_classes_Resource
? tao_models_classes_service_ServiceCall::fromResource($runtime)
: tao_models_classes_service_ServiceCall::fromString((string)$runtime);
$data['runtime'] = base64_encode(json_encode($serviceCall));
$rdfData = $this->rdfExporter->getRdfString([$compiledDelivery]);
if (!$zipArchive->addFromString(self::DELIVERY_RDF_FILENAME, $rdfData)) {
throw new common_Exception('Unable to add metadata to exported delivery assembly');
}
$data['meta'] = self::DELIVERY_RDF_FILENAME;
$content = json_encode($data);
if (!$zipArchive->addFromString(self::MANIFEST_FILENAME, $content)) {
$zipArchive->close();
unlink($path);
throw new common_Exception('Unable to add manifest to exported delivery assembly');
}
}
/**
* @param string $outputTestFormat
* @return void
*
* @throws UnsupportedCompiledTestFormatException
*/
private function setupCompiledTestConverter($outputTestFormat)
{
/** @var CompiledTestConverterFactory $compiledTestConverterFactory */
$compiledTestConverterFactory = $this->getServiceLocator()->get(CompiledTestConverterFactory::class);
$converter = $compiledTestConverterFactory->createConverter($outputTestFormat);
if ($converter) {
$this->getAssemblyFilesReader()->setCompiledTestConverter($converter);
}
}
/**
* @return AssemblyFilesReaderInterface
*/
private function getAssemblyFilesReader()
{
if (!$this->assemblyFilesReader instanceof AssemblyFilesReaderInterface) {
$this->assemblyFilesReader = $this->getOption(self::OPTION_ASSEMBLY_FILES_READER);
}
return $this->assemblyFilesReader;
}
}