2016-01-11 18:31:04 +01:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:55:03 +01:00
|
|
|
|
* File info?
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:55:03 +01:00
|
|
|
|
* The default file storage class for H5P. Will carry out the requested file
|
|
|
|
|
* operations using PHP's standard file operation functions.
|
|
|
|
|
*
|
|
|
|
|
* Some implementations of H5P that doesn't use the standard file system will
|
|
|
|
|
* want to create their own implementation of the \H5P\FileStorage interface.
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*
|
2016-01-12 12:55:03 +01:00
|
|
|
|
* @package H5P
|
|
|
|
|
* @copyright 2016 Joubel AS
|
|
|
|
|
* @license MIT
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
2016-01-25 14:46:06 +01:00
|
|
|
|
class H5PDefaultStorage implements \H5PFileStorage {
|
2016-06-14 12:39:15 +02:00
|
|
|
|
private $path, $alteditorpath;
|
2016-01-11 18:31:04 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The great Constructor!
|
2016-01-12 12:55:03 +01:00
|
|
|
|
*
|
|
|
|
|
* @param string $path
|
|
|
|
|
* The base location of H5P files
|
2016-06-14 12:39:15 +02:00
|
|
|
|
* @param string $alteditorpath
|
|
|
|
|
* Optional. Use a different editor path
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
2016-06-14 12:39:15 +02:00
|
|
|
|
function __construct($path, $alteditorpath = NULL) {
|
2016-01-11 18:31:04 +01:00
|
|
|
|
// Set H5P storage path
|
|
|
|
|
$this->path = $path;
|
2016-06-14 12:39:15 +02:00
|
|
|
|
$this->alteditorpath = $alteditorpath;
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store the library folder.
|
|
|
|
|
*
|
|
|
|
|
* @param array $library
|
|
|
|
|
* Library properties
|
|
|
|
|
*/
|
|
|
|
|
public function saveLibrary($library) {
|
|
|
|
|
$dest = $this->path . '/libraries/' . \H5PCore::libraryToString($library, TRUE);
|
|
|
|
|
|
|
|
|
|
// Make sure destination dir doesn't exist
|
|
|
|
|
\H5PCore::deleteFileTree($dest);
|
|
|
|
|
|
|
|
|
|
// Move library folder
|
|
|
|
|
self::copyFileTree($library['uploadDirectory'], $dest);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store the content folder.
|
|
|
|
|
*
|
|
|
|
|
* @param string $source
|
|
|
|
|
* Path on file system to content directory.
|
2016-09-14 10:44:10 +02:00
|
|
|
|
* @param array $content
|
|
|
|
|
* Content properties
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
2016-09-14 10:44:10 +02:00
|
|
|
|
public function saveContent($source, $content) {
|
|
|
|
|
$dest = "{$this->path}/content/{$content['id']}";
|
2016-01-12 12:55:03 +01:00
|
|
|
|
|
|
|
|
|
// Remove any old content
|
|
|
|
|
\H5PCore::deleteFileTree($dest);
|
|
|
|
|
|
|
|
|
|
self::copyFileTree($source, $dest);
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove content folder.
|
|
|
|
|
*
|
2016-09-14 10:44:10 +02:00
|
|
|
|
* @param array $content
|
|
|
|
|
* Content properties
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
2016-09-14 10:44:10 +02:00
|
|
|
|
public function deleteContent($content) {
|
|
|
|
|
\H5PCore::deleteFileTree("{$this->path}/content/{$content['id']}");
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a stored copy of the content folder.
|
|
|
|
|
*
|
|
|
|
|
* @param string $id
|
|
|
|
|
* Identifier of content to clone.
|
|
|
|
|
* @param int $newId
|
|
|
|
|
* The cloned content's identifier
|
|
|
|
|
*/
|
|
|
|
|
public function cloneContent($id, $newId) {
|
|
|
|
|
$path = $this->path . '/content/';
|
2017-02-08 10:57:16 +01:00
|
|
|
|
if (file_exists($path . $id)) {
|
|
|
|
|
self::copyFileTree($path . $id, $path . $newId);
|
|
|
|
|
}
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get path to a new unique tmp folder.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
* Path
|
|
|
|
|
*/
|
|
|
|
|
public function getTmpPath() {
|
2016-01-25 14:49:54 +01:00
|
|
|
|
$temp = "{$this->path}/temp";
|
|
|
|
|
self::dirReady($temp);
|
|
|
|
|
return "{$temp}/" . uniqid('h5p-');
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fetch content folder and save in target directory.
|
|
|
|
|
*
|
|
|
|
|
* @param int $id
|
|
|
|
|
* Content identifier
|
|
|
|
|
* @param string $target
|
|
|
|
|
* Where the content folder will be saved
|
|
|
|
|
*/
|
|
|
|
|
public function exportContent($id, $target) {
|
2016-06-16 10:43:17 +02:00
|
|
|
|
$source = "{$this->path}/content/{$id}";
|
|
|
|
|
if (file_exists($source)) {
|
|
|
|
|
// Copy content folder if it exists
|
2016-06-16 20:13:51 +02:00
|
|
|
|
self::copyFileTree($source, $target);
|
2016-06-16 10:43:17 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// No contnet folder, create emty dir for content.json
|
|
|
|
|
self::dirReady($target);
|
|
|
|
|
}
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fetch library folder and save in target directory.
|
|
|
|
|
*
|
|
|
|
|
* @param array $library
|
|
|
|
|
* Library properties
|
|
|
|
|
* @param string $target
|
|
|
|
|
* Where the library folder will be saved
|
2016-04-21 11:38:14 +02:00
|
|
|
|
* @param string $developmentPath
|
|
|
|
|
* Folder that library resides in
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
2016-04-21 11:38:14 +02:00
|
|
|
|
public function exportLibrary($library, $target, $developmentPath=NULL) {
|
2016-01-12 12:55:03 +01:00
|
|
|
|
$folder = \H5PCore::libraryToString($library, TRUE);
|
2016-04-21 11:38:14 +02:00
|
|
|
|
$srcPath = ($developmentPath === NULL ? "/libraries/{$folder}" : $developmentPath);
|
|
|
|
|
self::copyFileTree("{$this->path}{$srcPath}", "{$target}/{$folder}");
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Save export in file system
|
|
|
|
|
*
|
|
|
|
|
* @param string $source
|
|
|
|
|
* Path on file system to temporary export file.
|
|
|
|
|
* @param string $filename
|
|
|
|
|
* Name of export file.
|
2016-09-26 12:05:02 +02:00
|
|
|
|
* @throws Exception Unable to save the file
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
2016-09-08 14:48:08 +02:00
|
|
|
|
public function saveExport($source, $filename) {
|
2016-01-12 12:55:03 +01:00
|
|
|
|
$this->deleteExport($filename);
|
2016-09-26 12:05:02 +02:00
|
|
|
|
|
|
|
|
|
if (!self::dirReady("{$this->path}/exports")) {
|
|
|
|
|
throw new Exception("Unable to create directory for H5P export file.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!copy($source, "{$this->path}/exports/{$filename}")) {
|
|
|
|
|
throw new Exception("Unable to save H5P export file.");
|
|
|
|
|
}
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removes given export file
|
|
|
|
|
*
|
|
|
|
|
* @param string $filename
|
|
|
|
|
*/
|
2016-09-08 14:48:08 +02:00
|
|
|
|
public function deleteExport($filename) {
|
2016-01-12 12:55:03 +01:00
|
|
|
|
$target = "{$this->path}/exports/{$filename}";
|
|
|
|
|
if (file_exists($target)) {
|
|
|
|
|
unlink($target);
|
|
|
|
|
}
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-23 10:52:40 +02:00
|
|
|
|
/**
|
|
|
|
|
* Check if the given export file exists
|
|
|
|
|
*
|
|
|
|
|
* @param string $filename
|
|
|
|
|
* @return boolean
|
|
|
|
|
*/
|
|
|
|
|
public function hasExport($filename) {
|
|
|
|
|
$target = "{$this->path}/exports/{$filename}";
|
|
|
|
|
return file_exists($target);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-13 14:23:34 +01:00
|
|
|
|
/**
|
|
|
|
|
* Will concatenate all JavaScrips and Stylesheets into two files in order
|
|
|
|
|
* to improve page performance.
|
|
|
|
|
*
|
|
|
|
|
* @param array $files
|
|
|
|
|
* A set of all the assets required for content to display
|
|
|
|
|
* @param string $key
|
|
|
|
|
* Hashed key for cached asset
|
|
|
|
|
*/
|
|
|
|
|
public function cacheAssets(&$files, $key) {
|
|
|
|
|
foreach ($files as $type => $assets) {
|
2016-01-25 14:47:48 +01:00
|
|
|
|
if (empty($assets)) {
|
|
|
|
|
continue; // Skip no assets
|
|
|
|
|
}
|
2016-01-13 14:23:34 +01:00
|
|
|
|
|
2016-01-25 14:47:48 +01:00
|
|
|
|
$content = '';
|
2016-01-13 14:23:34 +01:00
|
|
|
|
foreach ($assets as $asset) {
|
|
|
|
|
// Get content from asset file
|
|
|
|
|
$assetContent = file_get_contents($this->path . $asset->path);
|
|
|
|
|
$cssRelPath = preg_replace('/[^\/]+$/', '', $asset->path);
|
|
|
|
|
|
|
|
|
|
// Get file content and concatenate
|
|
|
|
|
if ($type === 'scripts') {
|
|
|
|
|
$content .= $assetContent . ";\n";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Rewrite relative URLs used inside stylesheets
|
|
|
|
|
$content .= preg_replace_callback(
|
|
|
|
|
'/url\([\'"]?([^"\')]+)[\'"]?\)/i',
|
|
|
|
|
function ($matches) use ($cssRelPath) {
|
2016-01-26 15:57:10 +01:00
|
|
|
|
if (preg_match("/^(data:|([a-z0-9]+:)?\/)/i", $matches[1]) === 1) {
|
2016-01-25 14:49:29 +01:00
|
|
|
|
return $matches[0]; // Not relative, skip
|
|
|
|
|
}
|
|
|
|
|
return 'url("../' . $cssRelPath . $matches[1] . '")';
|
2016-01-13 14:23:34 +01:00
|
|
|
|
},
|
2016-01-25 14:49:29 +01:00
|
|
|
|
$assetContent) . "\n";
|
2016-01-13 14:23:34 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self::dirReady("{$this->path}/cachedassets");
|
|
|
|
|
$ext = ($type === 'scripts' ? 'js' : 'css');
|
2016-01-25 14:49:54 +01:00
|
|
|
|
$outputfile = "/cachedassets/{$key}.{$ext}";
|
|
|
|
|
file_put_contents($this->path . $outputfile, $content);
|
|
|
|
|
$files[$type] = array((object) array(
|
|
|
|
|
'path' => $outputfile,
|
|
|
|
|
'version' => ''
|
|
|
|
|
));
|
2016-01-13 14:23:34 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Will check if there are cache assets available for content.
|
|
|
|
|
*
|
|
|
|
|
* @param string $key
|
|
|
|
|
* Hashed key for cached asset
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getCachedAssets($key) {
|
2016-01-25 14:49:54 +01:00
|
|
|
|
$files = array();
|
|
|
|
|
|
|
|
|
|
$js = "/cachedassets/{$key}.js";
|
|
|
|
|
if (file_exists($this->path . $js)) {
|
|
|
|
|
$files['scripts'] = array((object) array(
|
|
|
|
|
'path' => $js,
|
|
|
|
|
'version' => ''
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$css = "/cachedassets/{$key}.css";
|
|
|
|
|
if (file_exists($this->path . $css)) {
|
|
|
|
|
$files['styles'] = array((object) array(
|
|
|
|
|
'path' => $css,
|
|
|
|
|
'version' => ''
|
|
|
|
|
));
|
2016-01-13 14:23:34 +01:00
|
|
|
|
}
|
2016-01-25 14:49:54 +01:00
|
|
|
|
|
|
|
|
|
return empty($files) ? NULL : $files;
|
2016-01-13 14:23:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove the aggregated cache files.
|
|
|
|
|
*
|
|
|
|
|
* @param array $keys
|
|
|
|
|
* The hash keys of removed files
|
|
|
|
|
*/
|
|
|
|
|
public function deleteCachedAssets($keys) {
|
|
|
|
|
foreach ($keys as $hash) {
|
|
|
|
|
foreach (array('js', 'css') as $ext) {
|
2016-01-25 14:49:54 +01:00
|
|
|
|
$path = "{$this->path}/cachedassets/{$hash}.{$ext}";
|
2016-01-13 14:23:34 +01:00
|
|
|
|
if (file_exists($path)) {
|
|
|
|
|
unlink($path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-22 11:35:23 +02:00
|
|
|
|
/**
|
|
|
|
|
* Read file content of given file and then return it.
|
|
|
|
|
*
|
|
|
|
|
* @param string $file_path
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function getContent($file_path) {
|
2017-03-02 10:17:16 +01:00
|
|
|
|
return file_get_contents($file_path);
|
2016-04-22 11:35:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-25 14:31:25 +02:00
|
|
|
|
/**
|
2016-06-14 12:39:15 +02:00
|
|
|
|
* Save files uploaded through the editor.
|
|
|
|
|
* The files must be marked as temporary until the content form is saved.
|
|
|
|
|
*
|
|
|
|
|
* @param \H5peditorFile $file
|
|
|
|
|
* @param int $contentid
|
2016-04-25 14:31:25 +02:00
|
|
|
|
*/
|
|
|
|
|
public function saveFile($file, $contentId) {
|
2016-06-13 12:47:44 +02:00
|
|
|
|
// Prepare directory
|
2016-06-14 12:39:15 +02:00
|
|
|
|
if (empty($contentId)) {
|
|
|
|
|
// Should be in editor tmp folder
|
2017-03-11 18:43:08 +01:00
|
|
|
|
$path = $this->getEditorPath();
|
2016-06-14 12:39:15 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Should be in content folder
|
|
|
|
|
$path = $this->path . '/content/' . $contentId;
|
|
|
|
|
}
|
|
|
|
|
$path .= '/' . $file->getType() . 's';
|
2016-04-25 14:31:25 +02:00
|
|
|
|
self::dirReady($path);
|
|
|
|
|
|
2016-06-13 12:47:44 +02:00
|
|
|
|
// Add filename to path
|
|
|
|
|
$path .= '/' . $file->getName();
|
|
|
|
|
|
2018-12-20 16:17:14 +01:00
|
|
|
|
copy($_FILES['file']['tmp_name'], $path);
|
2017-01-26 14:56:36 +01:00
|
|
|
|
|
2017-03-14 15:00:14 +01:00
|
|
|
|
return $file;
|
2016-04-25 14:31:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-04 16:52:02 +02:00
|
|
|
|
/**
|
|
|
|
|
* Copy a file from another content or editor tmp dir.
|
|
|
|
|
* Used when copy pasting content in H5P Editor.
|
|
|
|
|
*
|
|
|
|
|
* @param string $file path + name
|
|
|
|
|
* @param string|int $fromid Content ID or 'editor' string
|
|
|
|
|
* @param int $toid Target Content ID
|
|
|
|
|
*/
|
|
|
|
|
public function cloneContentFile($file, $fromId, $toId) {
|
|
|
|
|
// Determine source path
|
2016-06-14 12:39:15 +02:00
|
|
|
|
if ($fromId === 'editor') {
|
2017-03-11 18:43:08 +01:00
|
|
|
|
$sourcepath = $this->getEditorPath();
|
2016-05-04 16:52:02 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$sourcepath = "{$this->path}/content/{$fromId}";
|
|
|
|
|
}
|
|
|
|
|
$sourcepath .= '/' . $file;
|
|
|
|
|
|
|
|
|
|
// Determine target path
|
2016-06-14 12:39:15 +02:00
|
|
|
|
$filename = basename($file);
|
|
|
|
|
$filedir = str_replace($filename, '', $file);
|
|
|
|
|
$targetpath = "{$this->path}/content/{$toId}/{$filedir}";
|
2016-05-04 16:52:02 +02:00
|
|
|
|
|
|
|
|
|
// Make sure it's ready
|
|
|
|
|
self::dirReady($targetpath);
|
|
|
|
|
|
2016-06-14 12:39:15 +02:00
|
|
|
|
$targetpath .= $filename;
|
2016-05-04 16:52:02 +02:00
|
|
|
|
|
|
|
|
|
// Check to see if source exist and if target doesn't
|
|
|
|
|
if (!file_exists($sourcepath) || file_exists($targetpath)) {
|
|
|
|
|
return; // Nothing to copy from or target already exists
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy($sourcepath, $targetpath);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-11 18:43:08 +01:00
|
|
|
|
/**
|
|
|
|
|
* Copy a content from one directory to another. Defaults to cloning
|
|
|
|
|
* content from the current temporary upload folder to the editor path.
|
|
|
|
|
*
|
|
|
|
|
* @param string $source path to source directory
|
2019-03-15 12:52:54 +01:00
|
|
|
|
* @param string $contentId Id of contentarray
|
2017-03-11 18:43:08 +01:00
|
|
|
|
*/
|
2017-04-06 16:30:26 +02:00
|
|
|
|
public function moveContentDirectory($source, $contentId = NULL) {
|
2017-03-11 18:43:08 +01:00
|
|
|
|
if ($source === NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 12:52:54 +01:00
|
|
|
|
// TODO: Remove $contentId and never copy temporary files into content folder. JI-366
|
2017-04-09 17:43:56 +02:00
|
|
|
|
if ($contentId === NULL || $contentId == 0) {
|
2017-03-11 18:43:08 +01:00
|
|
|
|
$target = $this->getEditorPath();
|
|
|
|
|
}
|
2017-04-06 16:30:26 +02:00
|
|
|
|
else {
|
|
|
|
|
// Use content folder
|
|
|
|
|
$target = "{$this->path}/content/{$contentId}";
|
|
|
|
|
}
|
2017-03-11 18:43:08 +01:00
|
|
|
|
|
2019-09-27 11:15:10 +02:00
|
|
|
|
$contentSource = $source . '/' . 'content';
|
2017-03-12 17:56:36 +01:00
|
|
|
|
$contentFiles = array_diff(scandir($contentSource), array('.','..', 'content.json'));
|
|
|
|
|
foreach ($contentFiles as $file) {
|
2017-03-14 15:00:14 +01:00
|
|
|
|
if (is_dir("{$contentSource}/{$file}")) {
|
|
|
|
|
self::copyFileTree("{$contentSource}/{$file}", "{$target}/{$file}");
|
2017-03-11 18:43:08 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2017-03-14 15:00:14 +01:00
|
|
|
|
copy("{$contentSource}/{$file}", "{$target}/{$file}");
|
2017-03-11 18:43:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 12:52:54 +01:00
|
|
|
|
// TODO: Return list of all files so that they can be marked as temporary. JI-366
|
2017-03-11 18:43:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-04 16:52:02 +02:00
|
|
|
|
/**
|
|
|
|
|
* Checks to see if content has the given file.
|
|
|
|
|
* Used when saving content.
|
|
|
|
|
*
|
|
|
|
|
* @param string $file path + name
|
|
|
|
|
* @param int $contentId
|
|
|
|
|
* @return string File ID or NULL if not found
|
|
|
|
|
*/
|
|
|
|
|
public function getContentFile($file, $contentId) {
|
|
|
|
|
$path = "{$this->path}/content/{$contentId}/{$file}";
|
|
|
|
|
return file_exists($path) ? $path : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks to see if content has the given file.
|
|
|
|
|
* Used when saving content.
|
|
|
|
|
*
|
|
|
|
|
* @param string $file path + name
|
|
|
|
|
* @param int $contentid
|
|
|
|
|
* @return string|int File ID or NULL if not found
|
|
|
|
|
*/
|
|
|
|
|
public function removeContentFile($file, $contentId) {
|
|
|
|
|
$path = "{$this->path}/content/{$contentId}/{$file}";
|
|
|
|
|
if (file_exists($path)) {
|
|
|
|
|
unlink($path);
|
2017-07-07 10:51:57 +02:00
|
|
|
|
|
|
|
|
|
// Clean up any empty parent directories to avoid cluttering the file system
|
|
|
|
|
$parts = explode('/', $path);
|
|
|
|
|
while (array_pop($parts) !== NULL) {
|
|
|
|
|
$dir = implode('/', $parts);
|
|
|
|
|
if (is_dir($dir) && count(scandir($dir)) === 2) { // empty contains '.' and '..'
|
|
|
|
|
rmdir($dir); // Remove empty parent
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return; // Not empty
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-04 16:52:02 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-23 14:20:56 +01:00
|
|
|
|
/**
|
|
|
|
|
* Check if server setup has write permission to
|
|
|
|
|
* the required folders
|
|
|
|
|
*
|
2017-02-24 09:47:44 +01:00
|
|
|
|
* @return bool True if site can write to the H5P files folder
|
2017-02-23 14:20:56 +01:00
|
|
|
|
*/
|
|
|
|
|
public function hasWriteAccess() {
|
2017-02-24 09:47:44 +01:00
|
|
|
|
return self::dirReady($this->path);
|
2017-02-23 14:20:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-18 11:24:04 +02:00
|
|
|
|
/**
|
|
|
|
|
* Check if the file presave.js exists in the root of the library
|
|
|
|
|
*
|
|
|
|
|
* @param string $libraryFolder
|
|
|
|
|
* @param string $developmentPath
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2018-02-01 12:18:31 +01:00
|
|
|
|
public function hasPresave($libraryFolder, $developmentPath = null) {
|
2019-09-27 11:15:10 +02:00
|
|
|
|
$path = is_null($developmentPath) ? 'libraries' . '/' . $libraryFolder : $developmentPath;
|
|
|
|
|
$filePath = realpath($this->path . '/' . $path . '/' . 'presave.js');
|
2018-01-19 14:56:07 +01:00
|
|
|
|
return file_exists($filePath);
|
2018-01-19 14:42:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-22 15:27:27 +01:00
|
|
|
|
/**
|
|
|
|
|
* Check if upgrades script exist for library.
|
|
|
|
|
*
|
|
|
|
|
* @param string $machineName
|
|
|
|
|
* @param int $majorVersion
|
|
|
|
|
* @param int $minorVersion
|
|
|
|
|
* @return string Relative path
|
|
|
|
|
*/
|
|
|
|
|
public function getUpgradeScript($machineName, $majorVersion, $minorVersion) {
|
|
|
|
|
$upgrades = "/libraries/{$machineName}-{$majorVersion}.{$minorVersion}/upgrades.js";
|
|
|
|
|
if (file_exists($this->path . $upgrades)) {
|
|
|
|
|
return $upgrades;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 12:52:54 +01:00
|
|
|
|
/**
|
|
|
|
|
* Store the given stream into the given file.
|
|
|
|
|
*
|
|
|
|
|
* @param string $path
|
|
|
|
|
* @param string $file
|
|
|
|
|
* @param resource $stream
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function saveFileFromZip($path, $file, $stream) {
|
|
|
|
|
$filePath = $path . '/' . $file;
|
|
|
|
|
|
|
|
|
|
// Make sure the directory exists first
|
|
|
|
|
$matches = array();
|
|
|
|
|
preg_match('/(.+)\/[^\/]*$/', $filePath, $matches);
|
|
|
|
|
self::dirReady($matches[1]);
|
|
|
|
|
|
|
|
|
|
// Store in local storage folder
|
|
|
|
|
return file_put_contents($filePath, $stream);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-11 18:31:04 +01:00
|
|
|
|
/**
|
|
|
|
|
* Recursive function for copying directories.
|
|
|
|
|
*
|
|
|
|
|
* @param string $source
|
|
|
|
|
* From path
|
|
|
|
|
* @param string $destination
|
|
|
|
|
* To path
|
|
|
|
|
* @return boolean
|
|
|
|
|
* Indicates if the directory existed.
|
2016-04-21 11:38:14 +02:00
|
|
|
|
*
|
|
|
|
|
* @throws Exception Unable to copy the file
|
2016-01-11 18:31:04 +01:00
|
|
|
|
*/
|
2017-03-11 18:43:08 +01:00
|
|
|
|
private static function copyFileTree($source, $destination) {
|
2016-01-11 18:31:04 +01:00
|
|
|
|
if (!self::dirReady($destination)) {
|
|
|
|
|
throw new \Exception('unabletocopy');
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-23 15:46:52 +02:00
|
|
|
|
$ignoredFiles = self::getIgnoredFiles("{$source}/.h5pignore");
|
|
|
|
|
|
2016-01-11 18:31:04 +01:00
|
|
|
|
$dir = opendir($source);
|
|
|
|
|
if ($dir === FALSE) {
|
|
|
|
|
trigger_error('Unable to open directory ' . $source, E_USER_WARNING);
|
|
|
|
|
throw new \Exception('unabletocopy');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (false !== ($file = readdir($dir))) {
|
2016-09-23 15:46:52 +02:00
|
|
|
|
if (($file != '.') && ($file != '..') && $file != '.git' && $file != '.gitignore' && !in_array($file, $ignoredFiles)) {
|
2016-01-12 12:55:03 +01:00
|
|
|
|
if (is_dir("{$source}/{$file}")) {
|
|
|
|
|
self::copyFileTree("{$source}/{$file}", "{$destination}/{$file}");
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2016-01-12 12:55:03 +01:00
|
|
|
|
copy("{$source}/{$file}", "{$destination}/{$file}");
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
closedir($dir);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-23 15:46:52 +02:00
|
|
|
|
/**
|
|
|
|
|
* Retrieve array of file names from file.
|
|
|
|
|
*
|
|
|
|
|
* @param string $file
|
|
|
|
|
* @return array Array with files that should be ignored
|
|
|
|
|
*/
|
|
|
|
|
private static function getIgnoredFiles($file) {
|
|
|
|
|
if (file_exists($file) === FALSE) {
|
|
|
|
|
return array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$contents = file_get_contents($file);
|
|
|
|
|
if ($contents === FALSE) {
|
|
|
|
|
return array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return preg_split('/\s+/', $contents);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-11 18:31:04 +01:00
|
|
|
|
/**
|
|
|
|
|
* Recursive function that makes sure the specified directory exists and
|
|
|
|
|
* is writable.
|
|
|
|
|
*
|
|
|
|
|
* @param string $path
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2016-04-25 14:30:12 +02:00
|
|
|
|
private static function dirReady($path) {
|
2016-01-11 18:31:04 +01:00
|
|
|
|
if (!file_exists($path)) {
|
|
|
|
|
$parent = preg_replace("/\/[^\/]+\/?$/", '', $path);
|
|
|
|
|
if (!self::dirReady($parent)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mkdir($path, 0777, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_dir($path)) {
|
|
|
|
|
trigger_error('Path is not a directory ' . $path, E_USER_WARNING);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_writable($path)) {
|
|
|
|
|
trigger_error('Unable to write to ' . $path . ' – check directory permissions –', E_USER_WARNING);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2017-03-09 17:36:20 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Easy helper function for retrieving the editor path
|
|
|
|
|
*
|
2017-03-11 18:43:08 +01:00
|
|
|
|
* @return string Path to editor files
|
2017-03-09 17:36:20 +01:00
|
|
|
|
*/
|
2017-03-11 18:43:08 +01:00
|
|
|
|
private function getEditorPath() {
|
|
|
|
|
return ($this->alteditorpath !== NULL ? $this->alteditorpath : "{$this->path}/editor");
|
2017-03-09 17:36:20 +01:00
|
|
|
|
}
|
2016-01-11 18:31:04 +01:00
|
|
|
|
}
|