Actualización
This commit is contained in:
375
plugin/h5pimport/src/H5pImplementation.php
Normal file
375
plugin/h5pimport/src/H5pImplementation.php
Normal file
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportLibrary;
|
||||
|
||||
class H5pImplementation implements \H5PFrameworkInterface
|
||||
{
|
||||
private $h5pImport;
|
||||
private $h5pImportLibraries;
|
||||
|
||||
public function __construct(H5pImport $h5pImport)
|
||||
{
|
||||
$this->h5pImport = $h5pImport;
|
||||
$this->h5pImportLibraries = $h5pImport->getLibraries();
|
||||
}
|
||||
|
||||
public function getPlatformInfo()
|
||||
{
|
||||
// TODO: Implement getPlatformInfo() method.
|
||||
}
|
||||
|
||||
public function fetchExternalData($url, $data = null, $blocking = true, $stream = null)
|
||||
{
|
||||
// TODO: Implement fetchExternalData() method.
|
||||
}
|
||||
|
||||
public function setLibraryTutorialUrl($machineName, $tutorialUrl)
|
||||
{
|
||||
// TODO: Implement setLibraryTutorialUrl() method.
|
||||
}
|
||||
|
||||
public function setErrorMessage($message, $code = null)
|
||||
{
|
||||
// TODO: Implement setErrorMessage() method.
|
||||
}
|
||||
|
||||
public function setInfoMessage($message)
|
||||
{
|
||||
// TODO: Implement setInfoMessage() method.
|
||||
}
|
||||
|
||||
public function getMessages($type)
|
||||
{
|
||||
// TODO: Implement getMessages() method.
|
||||
}
|
||||
|
||||
public function t($message, $replacements = [])
|
||||
{
|
||||
return get_lang($message);
|
||||
}
|
||||
|
||||
public function getLibraryFileUrl($libraryFolderName, $fileName)
|
||||
{
|
||||
// TODO: Implement getLibraryFileUrl() method.
|
||||
}
|
||||
|
||||
public function getUploadedH5pFolderPath()
|
||||
{
|
||||
// TODO: Implement getUploadedH5pFolderPath() method.
|
||||
}
|
||||
|
||||
public function getUploadedH5pPath()
|
||||
{
|
||||
// TODO: Implement getUploadedH5pPath() method.
|
||||
}
|
||||
|
||||
public function loadAddons()
|
||||
{
|
||||
$addons = [];
|
||||
$sql = "
|
||||
SELECT l1.machine_name, l1.major_version, l1.minor_version, l1.patch_version,
|
||||
l1.iid, l1.preloaded_js, l1.preloaded_css
|
||||
FROM plugin_h5p_import_library AS l1
|
||||
LEFT JOIN plugin_h5p_import_library AS l2
|
||||
ON l1.machine_name = l2.machine_name AND
|
||||
(l1.major_version < l2.major_version OR
|
||||
(l1.major_version = l2.major_version AND
|
||||
l1.minor_version < l2.minor_version))
|
||||
WHERE l2.machine_name IS null
|
||||
";
|
||||
|
||||
$result = \Database::query($sql);
|
||||
while ($row = \Database::fetch_array($result)) {
|
||||
$addons[] = \H5PCore::snakeToCamel($row);
|
||||
}
|
||||
|
||||
return $addons;
|
||||
}
|
||||
|
||||
public function getLibraryConfig($libraries = null)
|
||||
{
|
||||
// TODO: Implement getLibraryConfig() method.
|
||||
}
|
||||
|
||||
public function loadLibraries()
|
||||
{
|
||||
// TODO: Implement loadLibraries() method.
|
||||
}
|
||||
|
||||
public function getAdminUrl()
|
||||
{
|
||||
// TODO: Implement getAdminUrl() method.
|
||||
}
|
||||
|
||||
public function getLibraryId($machineName, $majorVersion = null, $minorVersion = null)
|
||||
{
|
||||
// TODO: Implement getLibraryId() method.
|
||||
}
|
||||
|
||||
public function getWhitelist($isLibrary, $defaultContentWhitelist, $defaultLibraryWhitelist)
|
||||
{
|
||||
// TODO: Implement getWhitelist() method.
|
||||
}
|
||||
|
||||
public function isPatchedLibrary($library)
|
||||
{
|
||||
// TODO: Implement isPatchedLibrary() method.
|
||||
}
|
||||
|
||||
public function isInDevMode()
|
||||
{
|
||||
// TODO: Implement isInDevMode() method.
|
||||
}
|
||||
|
||||
public function mayUpdateLibraries()
|
||||
{
|
||||
// TODO: Implement mayUpdateLibraries() method.
|
||||
}
|
||||
|
||||
public function saveLibraryData(&$libraryData, $new = true)
|
||||
{
|
||||
// TODO: Implement saveLibraryData() method.
|
||||
}
|
||||
|
||||
public function insertContent($content, $contentMainId = null)
|
||||
{
|
||||
// TODO: Implement insertContent() method.
|
||||
}
|
||||
|
||||
public function updateContent($content, $contentMainId = null)
|
||||
{
|
||||
// TODO: Implement updateContent() method.
|
||||
}
|
||||
|
||||
public function resetContentUserData($contentId)
|
||||
{
|
||||
// TODO: Implement resetContentUserData() method.
|
||||
}
|
||||
|
||||
public function saveLibraryDependencies($libraryId, $dependencies, $dependency_type)
|
||||
{
|
||||
// TODO: Implement saveLibraryDependencies() method.
|
||||
}
|
||||
|
||||
public function copyLibraryUsage($contentId, $copyFromId, $contentMainId = null)
|
||||
{
|
||||
// TODO: Implement copyLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function deleteContentData($contentId)
|
||||
{
|
||||
// TODO: Implement deleteContentData() method.
|
||||
}
|
||||
|
||||
public function deleteLibraryUsage($contentId)
|
||||
{
|
||||
// TODO: Implement deleteLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function saveLibraryUsage($contentId, $librariesInUse)
|
||||
{
|
||||
// TODO: Implement saveLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function getLibraryUsage($libraryId, $skipContent = false)
|
||||
{
|
||||
// TODO: Implement getLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function loadLibrary($machineName, $majorVersion, $minorVersion)
|
||||
{
|
||||
if ($this->h5pImportLibraries) {
|
||||
$foundLibrary = $this->h5pImportLibraries->filter(
|
||||
function (H5pImportLibrary $library) use ($machineName, $majorVersion, $minorVersion) {
|
||||
return $library->getLibraryByMachineNameAndVersions($machineName, $majorVersion, $minorVersion);
|
||||
}
|
||||
)->first();
|
||||
if ($foundLibrary) {
|
||||
return [
|
||||
'libraryId' => $foundLibrary->getIid(),
|
||||
'title' => $foundLibrary->getTitle(),
|
||||
'machineName' => $foundLibrary->getMachineName(),
|
||||
'majorVersion' => $foundLibrary->getMajorVersion(),
|
||||
'minorVersion' => $foundLibrary->getMinorVersion(),
|
||||
'patchVersion' => $foundLibrary->getPatchVersion(),
|
||||
'runnable' => $foundLibrary->getRunnable(),
|
||||
'preloadedJs' => $foundLibrary->getPreloadedJsFormatted(),
|
||||
'preloadedCss' => $foundLibrary->getPreloadedCssFormatted(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function loadLibrarySemantics($machineName, $majorVersion, $minorVersion)
|
||||
{
|
||||
// TODO: Implement loadLibrarySemantics() method.
|
||||
}
|
||||
|
||||
public function alterLibrarySemantics(&$semantics, $machineName, $majorVersion, $minorVersion)
|
||||
{
|
||||
// TODO: Implement alterLibrarySemantics() method.
|
||||
}
|
||||
|
||||
public function deleteLibraryDependencies($libraryId)
|
||||
{
|
||||
// TODO: Implement deleteLibraryDependencies() method.
|
||||
}
|
||||
|
||||
public function lockDependencyStorage()
|
||||
{
|
||||
// TODO: Implement lockDependencyStorage() method.
|
||||
}
|
||||
|
||||
public function unlockDependencyStorage()
|
||||
{
|
||||
// TODO: Implement unlockDependencyStorage() method.
|
||||
}
|
||||
|
||||
public function deleteLibrary($library)
|
||||
{
|
||||
// TODO: Implement deleteLibrary() method.
|
||||
}
|
||||
|
||||
public function loadContent($id): array
|
||||
{
|
||||
$contentJson = H5pPackageTools::getJson($this->h5pImport->getPath().'/content.json');
|
||||
$h5pJson = H5pPackageTools::getJson($this->h5pImport->getPath().'/h5p.json');
|
||||
|
||||
if ($contentJson && $h5pJson) {
|
||||
$params = json_encode($contentJson);
|
||||
$embedType = implode(',', $h5pJson->embedTypes);
|
||||
$title = $this->h5pImport->getName();
|
||||
$language = $h5pJson->language;
|
||||
$libraryId = $this->h5pImport->getMainLibrary()->getIid();
|
||||
$libraryName = $this->h5pImport->getMainLibrary()->getMachineName();
|
||||
$libraryMajorVersion = $this->h5pImport->getMainLibrary()->getMajorVersion();
|
||||
$libraryMinorVersion = $this->h5pImport->getMainLibrary()->getMinorVersion();
|
||||
$libraryEmbedTypes = $this->h5pImport->getMainLibrary()->getEmbedTypesFormatted();
|
||||
|
||||
// Create the associative array with the loaded content information. Use the unique folder name as id.
|
||||
return [
|
||||
'contentId' => basename($this->h5pImport->getPath()),
|
||||
'params' => $params,
|
||||
'embedType' => $embedType,
|
||||
'title' => $title,
|
||||
'language' => $language,
|
||||
'libraryId' => $libraryId,
|
||||
'libraryName' => $libraryName,
|
||||
'libraryMajorVersion' => $libraryMajorVersion,
|
||||
'libraryMinorVersion' => $libraryMinorVersion,
|
||||
'libraryEmbedTypes' => $libraryEmbedTypes,
|
||||
'libraryFullscreen' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function loadContentDependencies($id, $type = null): array
|
||||
{
|
||||
$h5pImportLibraries = $this->h5pImportLibraries;
|
||||
$dependencies = [];
|
||||
|
||||
/** @var H5pImportLibrary|null $library */
|
||||
foreach ($h5pImportLibraries as $library) {
|
||||
$dependencies[] = [
|
||||
'libraryId' => $library->getIid(),
|
||||
'machineName' => $library->getMachineName(),
|
||||
'majorVersion' => $library->getMajorVersion(),
|
||||
'minorVersion' => $library->getMinorVersion(),
|
||||
'patchVersion' => $library->getPatchVersion(),
|
||||
'preloadedJs' => $library->getPreloadedJsFormatted(),
|
||||
'preloadedCss' => $library->getPreloadedCssFormatted(),
|
||||
];
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
public function getOption($name, $default = null)
|
||||
{
|
||||
return api_get_course_plugin_setting('h5pimport', $name);
|
||||
}
|
||||
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
// TODO: Implement setOption() method.
|
||||
}
|
||||
|
||||
public function updateContentFields($id, $fields)
|
||||
{
|
||||
// TODO: Implement updateContentFields() method.
|
||||
}
|
||||
|
||||
public function clearFilteredParameters($library_ids)
|
||||
{
|
||||
// TODO: Implement clearFilteredParameters() method.
|
||||
}
|
||||
|
||||
public function getNumNotFiltered()
|
||||
{
|
||||
// TODO: Implement getNumNotFiltered() method.
|
||||
}
|
||||
|
||||
public function getNumContent($libraryId, $skip = null)
|
||||
{
|
||||
// TODO: Implement getNumContent() method.
|
||||
}
|
||||
|
||||
public function isContentSlugAvailable($slug)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getLibraryStats($type)
|
||||
{
|
||||
// TODO: Implement getLibraryStats() method.
|
||||
}
|
||||
|
||||
public function getNumAuthors()
|
||||
{
|
||||
// TODO: Implement getNumAuthors() method.
|
||||
}
|
||||
|
||||
public function saveCachedAssets($key, $libraries)
|
||||
{
|
||||
// TODO: Implement saveCachedAssets() method.
|
||||
}
|
||||
|
||||
public function deleteCachedAssets($library_id)
|
||||
{
|
||||
// TODO: Implement deleteCachedAssets() method.
|
||||
}
|
||||
|
||||
public function getLibraryContentCount()
|
||||
{
|
||||
// TODO: Implement getLibraryContentCount() method.
|
||||
}
|
||||
|
||||
public function afterExportCreated($content, $filename)
|
||||
{
|
||||
// TODO: Implement afterExportCreated() method.
|
||||
}
|
||||
|
||||
public function hasPermission($permission, $id = null)
|
||||
{
|
||||
// TODO: Implement hasPermission() method.
|
||||
}
|
||||
|
||||
public function replaceContentTypeCache($contentTypeCache)
|
||||
{
|
||||
// TODO: Implement replaceContentTypeCache() method.
|
||||
}
|
||||
|
||||
public function libraryHasUpgrade($library)
|
||||
{
|
||||
// TODO: Implement libraryHasUpgrade() method.
|
||||
}
|
||||
}
|
||||
78
plugin/h5pimport/src/H5pPackageImporter.php
Normal file
78
plugin/h5pimport/src/H5pPackageImporter.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Chamilo\CoreBundle\Entity\Course;
|
||||
|
||||
/**
|
||||
* Class H5pPackageImporter.
|
||||
*/
|
||||
abstract class H5pPackageImporter
|
||||
{
|
||||
/**
|
||||
* @var Course
|
||||
*/
|
||||
protected $course;
|
||||
|
||||
/**
|
||||
* Path to course directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $courseDirectoryPath;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $packageFileInfo;
|
||||
|
||||
/**
|
||||
* The package type is usually a MIME type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $packageType;
|
||||
protected $h5pJsonContent;
|
||||
|
||||
/**
|
||||
* H5pPackageImporter constructor.
|
||||
*/
|
||||
protected function __construct(array $fileInfo, Course $course)
|
||||
{
|
||||
$this->packageFileInfo = $fileInfo;
|
||||
$this->course = $course;
|
||||
$this->courseDirectoryPath = api_get_path(SYS_COURSE_PATH).$this->course->getDirectory();
|
||||
$this->packageType = $fileInfo['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function create(array $fileInfo, Course $course): ZipPackageImporter
|
||||
{
|
||||
if (
|
||||
'application/octet-stream' !== $fileInfo['type']
|
||||
&& 'h5p' !== pathinfo($fileInfo['name'], PATHINFO_EXTENSION)
|
||||
) {
|
||||
throw new \Exception('Not a H5P package');
|
||||
}
|
||||
|
||||
return new ZipPackageImporter($fileInfo, $course);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the package and unzip it, checking if it has the 'h5p.json' file or some php script.
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function import(): string;
|
||||
|
||||
public function getPackageType(): string
|
||||
{
|
||||
return $this->packageType;
|
||||
}
|
||||
}
|
||||
327
plugin/h5pimport/src/H5pPackageTools.php
Normal file
327
plugin/h5pimport/src/H5pPackageTools.php
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Chamilo\CoreBundle\Entity\Course;
|
||||
use Chamilo\CoreBundle\Entity\Session;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportLibrary;
|
||||
use Database;
|
||||
use H5PCore;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
class H5pPackageTools
|
||||
{
|
||||
/**
|
||||
* Help read JSON from the archive.
|
||||
*
|
||||
* @return mixed JSON content if valid or FALSE for invalid
|
||||
*/
|
||||
public static function getJson(string $file, bool $assoc = false)
|
||||
{
|
||||
$fs = new Filesystem();
|
||||
$json = false;
|
||||
|
||||
if ($fs->exists($file)) {
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
// Decode the data
|
||||
$json = json_decode($contents, $assoc);
|
||||
if (null === $json) {
|
||||
// JSON cannot be decoded or the recursion limit has been reached.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the integrity of an H5P package by verifying the existence of libraries
|
||||
* and moves them to the "libraries" directory.
|
||||
*
|
||||
* @param object $h5pJson the H5P JSON object
|
||||
* @param string $extractedDir the path to the extracted directory
|
||||
*
|
||||
* @return bool true if the package integrity is valid, false otherwise
|
||||
*/
|
||||
public static function checkPackageIntegrity(object $h5pJson, string $extractedDir): bool
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$h5pDir = dirname($extractedDir, 2);
|
||||
$sharedLibrariesDir = $h5pDir.'/libraries';
|
||||
|
||||
// Move 'content' directory one level back (H5P specification)
|
||||
$filesystem->mirror($extractedDir.'/content', $extractedDir, null, ['override' => true]);
|
||||
$filesystem->remove($extractedDir.'/content');
|
||||
// Get the list of preloaded dependencies
|
||||
$preloadedDependencies = $h5pJson->preloadedDependencies;
|
||||
|
||||
// Check the existence of each library in the extracted directory
|
||||
foreach ($preloadedDependencies as $dependency) {
|
||||
$libraryName = $dependency->machineName;
|
||||
$majorVersion = $dependency->majorVersion;
|
||||
$minorVersion = $dependency->minorVersion;
|
||||
|
||||
$libraryFolderName = api_replace_dangerous_char($libraryName.'-'.$majorVersion.'.'.$minorVersion);
|
||||
$libraryPath = $extractedDir.'/'.$libraryFolderName;
|
||||
|
||||
// Check if the library folder exists
|
||||
if (!$filesystem->exists($libraryPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the entire folder to the "libraries" directory
|
||||
$targetPath = $sharedLibrariesDir.'/'.$libraryFolderName;
|
||||
|
||||
$filesystem->rename($libraryPath, $targetPath, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the H5P package information in the database.
|
||||
*
|
||||
* @param string $packagePath the path to the H5P package file
|
||||
* @param object $h5pJson the parsed H5P JSON object
|
||||
* @param Course $course the course entity related to the package
|
||||
* @param Session|null $session the session entity related to the package
|
||||
* @param array|null $values the advance options in upload form
|
||||
*/
|
||||
public static function storeH5pPackage(
|
||||
string $packagePath,
|
||||
object $h5pJson,
|
||||
Course $course,
|
||||
Session $session = null,
|
||||
array $values = null
|
||||
) {
|
||||
$entityManager = \Database::getManager();
|
||||
// Go back 2 directories
|
||||
$h5pDir = dirname($packagePath, 2);
|
||||
$sharedLibrariesDir = $h5pDir.'/libraries';
|
||||
|
||||
$mainLibraryName = $h5pJson->mainLibrary;
|
||||
$relativePath = api_get_path(REL_COURSE_PATH).$course->getDirectory().'/h5p/';
|
||||
|
||||
$h5pImport = new H5pImport();
|
||||
$h5pImport->setName($h5pJson->title);
|
||||
$h5pImport->setPath($packagePath);
|
||||
if ($values) {
|
||||
$h5pImport->setDescription($values['description']);
|
||||
}
|
||||
$h5pImport->setRelativePath($relativePath);
|
||||
$h5pImport->setCourse($course);
|
||||
$h5pImport->setSession($session);
|
||||
$entityManager->persist($h5pImport);
|
||||
|
||||
$libraries = $h5pJson->preloadedDependencies;
|
||||
|
||||
foreach ($libraries as $libraryData) {
|
||||
$library = $entityManager
|
||||
->getRepository(H5pImportLibrary::class)
|
||||
->findOneBy(
|
||||
[
|
||||
'machineName' => $libraryData->machineName,
|
||||
'majorVersion' => $libraryData->majorVersion,
|
||||
'minorVersion' => $libraryData->minorVersion,
|
||||
'course' => $course,
|
||||
]
|
||||
)
|
||||
;
|
||||
|
||||
if (null === $library) {
|
||||
$auxFullName = $libraryData->machineName.'-'.$libraryData->majorVersion.'.'.$libraryData->minorVersion;
|
||||
$libraryOwnJson = self::getJson($sharedLibrariesDir.'/'.$auxFullName.'/library.json');
|
||||
|
||||
$library = new H5pImportLibrary();
|
||||
$library->setMachineName($libraryData->machineName);
|
||||
$library->setTitle($libraryOwnJson->title);
|
||||
$library->setMajorVersion($libraryData->majorVersion);
|
||||
$library->setMinorVersion($libraryData->minorVersion);
|
||||
$library->setPatchVersion($libraryOwnJson->patchVersion);
|
||||
$library->setRunnable($libraryOwnJson->runnable);
|
||||
$library->setEmbedTypes($libraryOwnJson->embedTypes);
|
||||
$library->setPreloadedJs($libraryOwnJson->preloadedJs);
|
||||
$library->setPreloadedCss($libraryOwnJson->preloadedCss);
|
||||
$library->setLibraryPath($sharedLibrariesDir.'/'.$auxFullName);
|
||||
$library->setCourse($course);
|
||||
$entityManager->persist($library);
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
||||
$h5pImport->addLibraries($library);
|
||||
if ($mainLibraryName === $libraryData->machineName) {
|
||||
$h5pImport->setMainLibrary($library);
|
||||
}
|
||||
$entityManager->persist($h5pImport);
|
||||
$entityManager->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an H5P package from the database and the disk.
|
||||
*
|
||||
* @param H5pImport $h5pImport the H5P import entity representing the package to delete
|
||||
*
|
||||
* @return bool true if the package was successfully deleted, false otherwise
|
||||
*/
|
||||
public static function deleteH5pPackage(H5pImport $h5pImport): bool
|
||||
{
|
||||
$packagePath = $h5pImport->getPath();
|
||||
$entityManager = \Database::getManager();
|
||||
$entityManager->remove($h5pImport);
|
||||
$entityManager->flush();
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
if ($filesystem->exists($packagePath)) {
|
||||
try {
|
||||
$filesystem->remove($packagePath);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get core settings for H5P content.
|
||||
*
|
||||
* @param H5pImport $h5pImport the H5pImport object
|
||||
* @param \H5PCore $h5pCore the H5PCore object
|
||||
*
|
||||
* @return array the core settings for H5P content
|
||||
*/
|
||||
public static function getCoreSettings(H5pImport $h5pImport, H5PCore $h5pCore): array
|
||||
{
|
||||
$originIsLearnpath = 'learnpath' === api_get_origin();
|
||||
|
||||
$settings = [
|
||||
'baseUrl' => api_get_path(WEB_PATH),
|
||||
'url' => $h5pImport->getRelativePath(),
|
||||
'postUserStatistics' => true,
|
||||
'ajax' => [
|
||||
'setFinished' => api_get_path(WEB_PLUGIN_PATH).'h5pimport/src/ajax.php?action=set_finished&h5pId='.$h5pImport->getIid().'&learnpath='.$originIsLearnpath.'&token='.\H5PCore::createToken('result'),
|
||||
'contentUserData' => api_get_path(WEB_PLUGIN_PATH).'h5pimport/src/ajax.php?action=content_user_data&h5pId='.$h5pImport->getIid().'&token='.\H5PCore::createToken('content'),
|
||||
],
|
||||
'saveFreq' => false,
|
||||
'l10n' => [
|
||||
'H5P' => $h5pCore->getLocalization(),
|
||||
],
|
||||
// 'hubIsEnabled' => variable_get('h5p_hub_is_enabled', TRUE) ? TRUE : FALSE,
|
||||
'crossorigin' => false,
|
||||
// 'crossoriginCacheBuster' => variable_get('h5p_crossorigin_cache_buster', NULL),
|
||||
// 'libraryConfig' => $core->h5pF->getLibraryConfig(),
|
||||
'pluginCacheBuster' => '?0',
|
||||
'libraryUrl' => $h5pImport->getMainLibrary()->getLibraryPath().'/js',
|
||||
];
|
||||
|
||||
$loggedUser = api_get_user_info();
|
||||
if ($loggedUser) {
|
||||
$settings['user'] = [
|
||||
'name' => $loggedUser['complete_name'],
|
||||
'mail' => $loggedUser['email'],
|
||||
];
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the core assets.
|
||||
*
|
||||
* @return array[]|bool an array containing CSS and JS assets or false if some core assets missing
|
||||
*/
|
||||
public static function getCoreAssets()
|
||||
{
|
||||
$assets = [
|
||||
'css' => [],
|
||||
'js' => [],
|
||||
];
|
||||
|
||||
// Add CSS assets
|
||||
foreach (\H5PCore::$styles as $style) {
|
||||
$auxAssetPath = 'vendor/h5p/h5p-core/'.$style;
|
||||
$assets['css'][] = api_get_path(WEB_PATH).$auxAssetPath;
|
||||
if (!file_exists(api_get_path(SYS_PATH).$auxAssetPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add JS assets
|
||||
foreach (\H5PCore::$scripts as $script) {
|
||||
$auxAssetPath = 'vendor/h5p/h5p-core/'.$script;
|
||||
$auxUrl = api_get_path(WEB_PATH).$auxAssetPath;
|
||||
$assets['js'][] = $auxUrl;
|
||||
if (!file_exists(api_get_path(SYS_PATH).$auxAssetPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content body for the H5PIntegration javascript object.
|
||||
*
|
||||
* @param mixed $h5pNode
|
||||
*/
|
||||
public static function getContentSettings($h5pNode, H5PCore $h5pCore): array
|
||||
{
|
||||
$filtered = $h5pCore->filterParameters($h5pNode);
|
||||
$contentUserData = [
|
||||
0 => [
|
||||
'state' => '{}',
|
||||
],
|
||||
];
|
||||
|
||||
// ToDo Use $h5pCore->getDisplayOptionsForView() function
|
||||
$displayOptions = [
|
||||
'frame' => api_get_course_plugin_setting('h5pimport', 'frame'),
|
||||
'embed' => api_get_course_plugin_setting('h5pimport', 'embed'),
|
||||
'copyright' => api_get_course_plugin_setting('h5pimport', 'copyright'),
|
||||
'icon' => api_get_course_plugin_setting('h5pimport', 'icon'),
|
||||
];
|
||||
|
||||
return [
|
||||
'library' => \H5PCore::libraryToString($h5pNode['library']),
|
||||
'jsonContent' => $h5pNode['params'],
|
||||
'fullScreen' => $h5pNode['library']['fullscreen'],
|
||||
'exportUrl' => '',
|
||||
'language' => 'en',
|
||||
'filtered' => $filtered,
|
||||
'embedCode' => '<iframe src="'.api_get_course_url().'h5p/embed/'.$h5pNode['mainId'].'" width=":w" height=":h" frameborder="0" allowfullscreen="allowfullscreen" allow="geolocation *; microphone *; camera *; midi *; encrypted-media *" title="'.$h5pNode['title'].'"></iframe>',
|
||||
'resizeCode' => '',
|
||||
'mainId' => $h5pNode['mainId'],
|
||||
'url' => $h5pNode['url'],
|
||||
'contentUserData' => $contentUserData,
|
||||
'displayOptions' => $displayOptions,
|
||||
'metadata' => $h5pNode['metadata'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert H5P dependencies to a library list.
|
||||
*
|
||||
* @param array $dependencies the H5P dependencies
|
||||
*
|
||||
* @return array the library list with machine names as keys and version information as values
|
||||
*/
|
||||
public static function h5pDependenciesToLibraryList(array $dependencies): array
|
||||
{
|
||||
$libraryList = [];
|
||||
|
||||
foreach ($dependencies as $dependency) {
|
||||
$libraryList[$dependency['machineName']] = [
|
||||
'majorVersion' => $dependency['majorVersion'],
|
||||
'minorVersion' => $dependency['minorVersion'],
|
||||
];
|
||||
}
|
||||
|
||||
return $libraryList;
|
||||
}
|
||||
}
|
||||
183
plugin/h5pimport/src/ZipPackageImporter.php
Normal file
183
plugin/h5pimport/src/ZipPackageImporter.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* Class ZipPackageImporter.
|
||||
*/
|
||||
class ZipPackageImporter extends H5pPackageImporter
|
||||
{
|
||||
/*
|
||||
* Allowed file extensions
|
||||
* List obtained from H5P: https://h5p.org/allowed-file-extensions
|
||||
* */
|
||||
private const ALLOWED_EXTENSIONS = [
|
||||
'json',
|
||||
'png',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'bmp',
|
||||
'tif',
|
||||
'tiff',
|
||||
'svg',
|
||||
'eot',
|
||||
'ttf',
|
||||
'woff',
|
||||
'woff2',
|
||||
'otf',
|
||||
'webm',
|
||||
'mp4',
|
||||
'ogg',
|
||||
'mp3',
|
||||
'm4a',
|
||||
'wav',
|
||||
'txt',
|
||||
'pdf',
|
||||
'rtf',
|
||||
'doc',
|
||||
'docx',
|
||||
'xls',
|
||||
'xlsx',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'odt',
|
||||
'ods',
|
||||
'odp',
|
||||
'xml',
|
||||
'csv',
|
||||
'diff',
|
||||
'patch',
|
||||
'swf',
|
||||
'md',
|
||||
'textile',
|
||||
'vtt',
|
||||
'webvtt',
|
||||
'gltf',
|
||||
'gl',
|
||||
'js',
|
||||
'css',
|
||||
];
|
||||
|
||||
/**
|
||||
* Import an H5P package. No DB change.
|
||||
*
|
||||
* @throws Exception When the H5P package is invalid.
|
||||
*
|
||||
* @return string The path to the extracted package directory.
|
||||
*/
|
||||
public function import(): string
|
||||
{
|
||||
$zipFile = new \PclZip($this->packageFileInfo['tmp_name']);
|
||||
$zipContent = $zipFile->listContent();
|
||||
|
||||
if ($this->validateH5pPackageContent($zipContent)) {
|
||||
$packageSize = array_reduce(
|
||||
$zipContent,
|
||||
function ($accumulator, $zipEntry) {
|
||||
return $accumulator + $zipEntry['size'];
|
||||
}
|
||||
);
|
||||
|
||||
$this->validateEnoughSpace($packageSize);
|
||||
|
||||
$pathInfo = pathinfo($this->packageFileInfo['name']);
|
||||
|
||||
$packageDirectoryPath = $this->generatePackageDirectory($pathInfo['filename']);
|
||||
$zipFile->extract($packageDirectoryPath);
|
||||
|
||||
return "{$packageDirectoryPath}";
|
||||
}
|
||||
|
||||
throw new Exception('Invalid H5P package');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function validateEnoughSpace(int $packageSize)
|
||||
{
|
||||
$courseSpaceQuota = \DocumentManager::get_course_quota($this->course->getCode());
|
||||
|
||||
if (!enough_size($packageSize, $this->courseDirectoryPath, $courseSpaceQuota)) {
|
||||
throw new Exception('Not enough space to store package.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an H5P package.
|
||||
* Check if 'h5p.json' or 'content/content.json' files exist
|
||||
* and if the files are in a file whitelist (ALLOWED_EXTENSIONS).
|
||||
*
|
||||
* @param array $h5pPackageContent the content of the H5P package
|
||||
*
|
||||
* @return bool whether the H5P package is valid or not
|
||||
*/
|
||||
private function validateH5pPackageContent(array $h5pPackageContent): bool
|
||||
{
|
||||
$validPackage = false;
|
||||
|
||||
if (!empty($h5pPackageContent)) {
|
||||
foreach ($h5pPackageContent as $content) {
|
||||
$filename = $content['filename'];
|
||||
|
||||
if (0 !== preg_match('/(^[\._]|\/[\._]|\\\[\._])/', $filename)) {
|
||||
// Skip any file or folder starting with a . or _
|
||||
continue;
|
||||
}
|
||||
|
||||
$fileExtension = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
|
||||
if (in_array($fileExtension, self::ALLOWED_EXTENSIONS)) {
|
||||
$validPackage = 'h5p.json' === $filename || 'content/content.json' === $filename;
|
||||
if ($validPackage) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $validPackage;
|
||||
}
|
||||
|
||||
private function generatePackageDirectory(string $name): string
|
||||
{
|
||||
$baseDirectory = $this->courseDirectoryPath.'/h5p/content/';
|
||||
$safeName = api_replace_dangerous_char($name);
|
||||
$directoryPath = $baseDirectory.$safeName;
|
||||
|
||||
$fs = new Filesystem();
|
||||
|
||||
if ($fs->exists($directoryPath)) {
|
||||
$counter = 1;
|
||||
|
||||
// Add numeric suffix to the name until a unique directory name is found
|
||||
while ($fs->exists($directoryPath)) {
|
||||
$modifiedName = $safeName.'_'.$counter;
|
||||
$directoryPath = $baseDirectory.$modifiedName;
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
$fs->mkdir(
|
||||
$directoryPath,
|
||||
api_get_permissions_for_new_directories()
|
||||
);
|
||||
|
||||
$sharedLibrariesDir = $this->courseDirectoryPath.'/h5p/libraries';
|
||||
|
||||
if (!$fs->exists($sharedLibrariesDir)) {
|
||||
$fs->mkdir(
|
||||
$sharedLibrariesDir,
|
||||
api_get_permissions_for_new_directories()
|
||||
);
|
||||
}
|
||||
|
||||
return $directoryPath;
|
||||
}
|
||||
}
|
||||
90
plugin/h5pimport/src/ajax.php
Normal file
90
plugin/h5pimport/src/ajax.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
use Chamilo\CourseBundle\Entity\CLpItem;
|
||||
use Chamilo\CourseBundle\Entity\CLpItemView;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportResults;
|
||||
use ChamiloSession as Session;
|
||||
|
||||
require_once __DIR__.'/../../../main/inc/global.inc.php';
|
||||
|
||||
$action = $_REQUEST['action'] ?? null;
|
||||
$h5pId = isset($_REQUEST['h5pId']) ? intval($_REQUEST['h5pId']) : 0;
|
||||
|
||||
$course = api_get_course_entity(api_get_course_int_id());
|
||||
$session = api_get_session_entity(api_get_session_id());
|
||||
|
||||
$plugin = H5pImportPlugin::create();
|
||||
$em = Database::getManager();
|
||||
$h5pImportRepo = $em->getRepository('ChamiloPluginBundle:H5pImport\H5pImport');
|
||||
$user = api_get_user_entity(api_get_user_id());
|
||||
|
||||
if ('set_finished' === $action && 0 !== $h5pId) {
|
||||
if (!H5PCore::validToken('result', filter_input(INPUT_GET, 'token'))) {
|
||||
H5PCore::ajaxError($plugin->get_lang('h5p_error_invalid_token'));
|
||||
}
|
||||
|
||||
if (is_numeric($_POST['score']) && is_numeric($_POST['maxScore'])) {
|
||||
/** @var H5pImport|null $h5pImport */
|
||||
$h5pImport = $h5pImportRepo->find($h5pId);
|
||||
$entityManager = Database::getManager();
|
||||
|
||||
$h5pImportResults = new H5pImportResults();
|
||||
$h5pImportResults->setH5pImport($h5pImport);
|
||||
$h5pImportResults->setCourse($course);
|
||||
$h5pImportResults->setSession($session);
|
||||
$h5pImportResults->setUser($user);
|
||||
$h5pImportResults->setScore((int) $_POST['score']);
|
||||
$h5pImportResults->setMaxScore((int) $_POST['maxScore']);
|
||||
$h5pImportResults->setStartTime((int) $_POST['opened']);
|
||||
$h5pImportResults->setTotalTime(time() - $_POST['opened']);
|
||||
|
||||
$entityManager->persist($h5pImportResults);
|
||||
|
||||
// If it comes from an LP, update in c_lp_item_view
|
||||
if (1 == $_REQUEST['learnpath'] && Session::has('oLP')) {
|
||||
$lpObject = Session::read('oLP');
|
||||
$clpItemViewRepo = $em->getRepository('ChamiloCourseBundle:CLpItemView');
|
||||
|
||||
/** @var CLpItemView|null $lpItemView */
|
||||
$lpItemView = $clpItemViewRepo->findOneBy(
|
||||
[
|
||||
'lpViewId' => $lpObject->lp_view_id,
|
||||
'lpItemId' => $lpObject->current,
|
||||
]
|
||||
);
|
||||
|
||||
/** @var CLpItem|null $lpItem */
|
||||
$lpItem = $entityManager->find('ChamiloCourseBundle:CLpItem', $lpItemView->getLpItemId());
|
||||
if ('h5p' !== $lpItem->getItemType()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lpItemView->setScore($_POST['score']);
|
||||
$lpItemView->setMaxScore($_POST['maxScore']);
|
||||
$lpItemView->setStatus('completed');
|
||||
$lpItemView->setTotalTime($lpItemView->getTotalTime() + $h5pImportResults->getTotalTime());
|
||||
$lpItem->setMaxScore($_POST['maxScore']);
|
||||
$h5pImportResults->setCLpItemView($lpItemView);
|
||||
$entityManager->persist($h5pImportResults);
|
||||
$entityManager->persist($lpItem);
|
||||
$entityManager->persist($lpItemView);
|
||||
}
|
||||
$entityManager->flush();
|
||||
|
||||
H5PCore::ajaxSuccess();
|
||||
} else {
|
||||
H5PCore::ajaxError();
|
||||
}
|
||||
} elseif ('content_user_data' === $action && 0 !== $h5pId) {
|
||||
if (!H5PCore::validToken('content', filter_input(INPUT_GET, 'token'))) {
|
||||
H5PCore::ajaxError($plugin->get_lang('h5p_error_invalid_token'));
|
||||
}
|
||||
|
||||
/** @var H5pImport|null $h5pImport */
|
||||
$h5pImport = $h5pImportRepo->find($h5pId);
|
||||
} else {
|
||||
H5PCore::ajaxError(get_lang('InvalidAction'));
|
||||
}
|
||||
Reference in New Issue
Block a user