Merge branch 'master' of ssh://stash.amendor.com:7999/h5p/h5p-core-php

namespaces
Frode Petterson 2013-11-15 16:29:35 +01:00
commit f60bc3939b
3 changed files with 163 additions and 24 deletions

View File

@ -219,6 +219,34 @@ interface H5PFrameworkInterface {
* Library Id * Library Id
*/ */
public function deleteLibraryDependencies($libraryId); public function deleteLibraryDependencies($libraryId);
/**
* Get all the data we need to export H5P
*
* @param int $contentId
* ContentID of the node we are going to export
* @param string $title
* Title of the node to export
* @param string $language
* Language of the node to export
* @return array
* An array with all the data needed to export the h5p in the following format:
* 'title' => string,
* 'contentId' => string/int,
* 'mainLibrary' => string (machine name for main library),
* 'embedType' => string,
* 'libraries' => array(
* 'machineName' => string,
* 'majorVersion' => int,
* 'minorVersion' => int,
* 'preloaded' => int(0|1),
* 'language' => string,
*/
public function getExportData($contentId, $title, $language);
/**
* Check if export is enabled.
*/
public function isExportEnabled();
} }
/** /**
@ -244,7 +272,7 @@ class H5PValidator {
private $h5pOptional = array( private $h5pOptional = array(
'contentType' => '/^.{1,255}$/', 'contentType' => '/^.{1,255}$/',
'author' => '/^.{1,255}$/', 'author' => '/^.{1,255}$/',
'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT)$/', 'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT|GPL1|GPL2|GPL3|MPL|MPL2)$/',
'dynamicDependencies' => array( 'dynamicDependencies' => array(
'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'machineName' => '/^[\w0-9\-\.]{1,255}$/i',
'majorVersion' => '/^[0-9]{1,5}$/', 'majorVersion' => '/^[0-9]{1,5}$/',
@ -268,7 +296,7 @@ class H5PValidator {
private $libraryOptional = array( private $libraryOptional = array(
'author' => '/^.{1,255}$/', 'author' => '/^.{1,255}$/',
'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT|GPL1|GPL2|GPL3)$/', 'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT|GPL1|GPL2|GPL3|MPL|MPL2)$/',
'description' => '/^.{1,}$/', 'description' => '/^.{1,}$/',
'dynamicDependencies' => array( 'dynamicDependencies' => array(
'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'machineName' => '/^[\w0-9\-\.]{1,255}$/i',
@ -1003,6 +1031,110 @@ class H5PStorage {
} }
} }
/**
* This class is used for exporting zips
*/
Class H5PExport {
public $h5pF;
public $h5pC;
/**
* Constructor for the H5PExport
*
* @param object $H5PFramework
* The frameworks implementation of the H5PFrameworkInterface
* @param H5PCore
* Reference to an insance of H5PCore
*/
public function __construct($H5PFramework, $H5PCore) {
$this->h5pF = $H5PFramework;
$this->h5pC = $H5PCore;
}
/**
* Create the H5P package
*
* @param object $exportData
* The data to be exported.
* @return H5P package.
*/
public function exportToZip($exportData) {
$h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR;
$tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $exportData['contentId'];
$zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $exportData['contentId'] . '.h5p';
// Check if h5p-package already exists.
if (!file_exists($zipPath) == true) {
// Temp dir to put the h5p files in
@mkdir($tempPath);
$this->h5pC->copyTree($h5pDir . 'content' . DIRECTORY_SEPARATOR . $exportData['contentId'], $tempPath . DIRECTORY_SEPARATOR . 'content');
// Copies libraries to temp dir and create mention in h5p.json
foreach($exportData['libraries'] as $library) {
$source = $h5pDir . 'libraries' . DIRECTORY_SEPARATOR . $library['machineName'] . '-' . $library['majorVersion'] . '.' . $library['minorVersion'];
$destination = $tempPath . DIRECTORY_SEPARATOR . $library['machineName'];
$this->h5pC->copyTree($source, $destination);
// Set preloaded and dynamic dependencies
if ($library['preloaded']) {
$preloadedDependencies[] = array(
'machineName' => $library['machineName'],
'majorVersion' => $library['majorVersion'],
'minorVersion' => $library['minorVersion'],
);
} else {
$dynamicDependencies[] = array(
'machineName' => $library['machineName'],
'majorVersion' => $library['majorVersion'],
'minorVersion' => $library['minorVersion'],
);
}
}
// Make embedTypes into an array
$embedTypes = explode(', ', $exportData['embedType']);
// Build h5p.json
$h5pJson = array (
'title' => $exportData['title'],
'language' => $exportData['language'],
'mainLibrary' => $exportData['mainLibrary'],
'embedTypes' => $embedTypes,
);
// Add preloaded and dynamic dependencies if they exist
if ($preloadedDependencies) { $h5pJson['preloadedDependencies'] = $preloadedDependencies; }
if ($dynamicDependencies) { $h5pJson['dynamicDependencies'] = $dynamicDependencies; }
// Save h5p.json
$results = print_r(json_encode($h5pJson), true);
file_put_contents($tempPath . DIRECTORY_SEPARATOR . 'h5p.json', $results);
// Create new zip instance.
$zip = new ZipArchive();
$zip->open($zipPath, ZIPARCHIVE::CREATE);
// Get all files and folders in $tempPath
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tempPath . DIRECTORY_SEPARATOR));
// Add files to zip
foreach ($iterator as $key=>$value) {
$test = '.';
// Do not add the folders '.' and '..' to the zip. This will make zip invalid.
if (substr_compare($key, $test, -strlen($test), strlen($test)) !== 0) {
// Get files path in $tempPath
$filePath = explode($tempPath . DIRECTORY_SEPARATOR, $key);
// Add files to the zip with the intended file-structure
$zip->addFile($key, $filePath[1]);
}
}
// Close zip and remove temp dir
$zip->close();
@rmdir($tempPath);
}
// Set headers for automagic download!!
header('Content-Description: File Transfer');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $exportData['title'] . '.h5p"');
readfile ($zipPath);
}
}
/** /**
* Functions and storage shared by the other H5P classes * Functions and storage shared by the other H5P classes
*/ */
@ -1225,6 +1357,9 @@ class H5PContentValidator {
if (in_array('ul', $tags) || in_array('ol', $tags) && ! in_array('li', $tags)) { if (in_array('ul', $tags) || in_array('ol', $tags) && ! in_array('li', $tags)) {
$tags[] = 'li'; $tags[] = 'li';
} }
if (in_array('del', $tags) || in_array('strike', $tags) && ! in_array('s', $tags)) {
$tags[] = 's';
}
// Strip invalid HTML tags. // Strip invalid HTML tags.
$text = $this->filter_xss($text, $tags); $text = $this->filter_xss($text, $tags);
} }
@ -1240,7 +1375,9 @@ class H5PContentValidator {
// Check if string is according to optional regexp in semantics // Check if string is according to optional regexp in semantics
if (isset($semantics->regexp)) { if (isset($semantics->regexp)) {
$pattern = '|' . $semantics->regexp->pattern . '|'; // Note: '|' used as regexp fence, to allow / in actual patterns.
// But also escaping '|' found in patterns, so that is valid too.
$pattern = '|' . str_replace('|', '\\|', $semantics->regexp->pattern) . '|';
$pattern .= isset($semantics->regexp->modifiers) ? $semantics->regexp->modifiers : ''; $pattern .= isset($semantics->regexp->modifiers) ? $semantics->regexp->modifiers : '';
if (preg_match($pattern, $text) === 0) { if (preg_match($pattern, $text) === 0) {
// Note: explicitly ignore return value FALSE, to avoid removing text // Note: explicitly ignore return value FALSE, to avoid removing text

View File

@ -1,7 +1,7 @@
var H5P = H5P || {}; var H5P = H5P || {};
// This needs to be determined before init is run. // This needs to be determined before init is run.
H5P.isFramed = (window.parent !== window); H5P.isFramed = (window.self !== window.top); // (window.parent !== window);
// Initialize H5P content // Initialize H5P content
// Scans for ".h5p-content" // Scans for ".h5p-content"
@ -62,36 +62,35 @@ H5P.init = function () {
H5P.fullScreen($el, obj); H5P.fullScreen($el, obj);
return false; return false;
}); });
} };
}); });
// H5Ps living in iframes. Note: Fullscreen button will be added // H5Ps living in iframes. Note: Fullscreen button will be added
// inside iFrame if relevant // inside iFrame if relevant
var $h5pIframes = H5P.jQuery(".h5p-iframe"); var $h5pIframes = H5P.jQuery(".h5p-iframe");
$h5pIframes.each(function (idx, iframe) { if ($h5pIframes.length !== 0) {
var $iframe = H5P.jQuery(iframe), $h5pIframes.each(function (idx, iframe) {
contentId = $iframe.data('content-id'), var $iframe = H5P.jQuery(iframe),
mainLibrary = $iframe.data('class'); contentId = $iframe.data('content-id'),
mainLibrary = $iframe.data('class');
// Get iFrame body, and reset it to contain only the normal H5P DIV. iframe.contentDocument.open();
$iframe.contents().find('body') iframe.contentDocument.write('<!doctype html><html><head>' + H5PIntegration.getHeadTags(contentId) + '</head><body><div class="h5p-content" data-class="' + mainLibrary + '" data-content-id="' + contentId + '"/></body></html>');
.html('<div class="h5p-content" data-class="' + mainLibrary + '" data-content-id="' + contentId + '"/>'); iframe.contentDocument.close();
});
// Add scripts required for this iFrame from settings
H5PIntegration.addFilesToIframe($iframe, contentId);
});
if ($h5pIframes.length > 0) {
// TODO: This seems very hacky... why can't we just use the resize event? What happens if we ain't done before the next interval starts? // TODO: This seems very hacky... why can't we just use the resize event? What happens if we ain't done before the next interval starts?
setInterval(function h5pIframeResizer() { setInterval(function () {
$h5pIframes.each(function (idx, iframe) { $h5pIframes.each(function (idx, iframe) {
var contentHeight = iframe.contentDocument.body.offsetHeight; var $iframe = H5P.jQuery(iframe);
var frameHeight = H5P.jQuery(iframe).innerHeight(); var contentHeight = $iframe.contents().height();
var frameHeight = $iframe.innerHeight();
if (frameHeight !== contentHeight) { if (frameHeight !== contentHeight) {
H5P.resizeIframe(H5P.jQuery(iframe).data('content-id'), contentHeight); H5P.resizeIframe($iframe.data('content-id'), contentHeight);
} }
}); });
}, 100); }, 250);
} }
}; };
@ -429,11 +428,11 @@ if (String.prototype.trim === undefined) {
// Finally, we want to run init when document is ready. But not if we're // Finally, we want to run init when document is ready. But not if we're
// in an iFrame. Then we wait for parent to start init(). // in an iFrame. Then we wait for parent to start init().
if (H5P.jQuery && !H5P.isFramed) { if (H5P.jQuery) {
H5P.jQuery(document).ready(function () { H5P.jQuery(document).ready(function () {
if (!H5P.initialized) { if (!H5P.initialized) {
H5P.initialized = true; H5P.initialized = true;
H5P.init(); H5P.init();
} }
}); });
} }

View File

@ -60,7 +60,8 @@ div.h5p-semi-fullscreen {
height: 100%; height: 100%;
} }
.h5p-iframe-wrapper { .h5p-iframe-wrapper {
width: auto; height: auto; width: auto;
height: auto;
} }
.h5p-fullscreen .h5p-iframe-wrapper, .h5p-fullscreen .h5p-iframe-wrapper,
@ -91,6 +92,8 @@ div.h5p-semi-fullscreen {
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 10; z-index: 10;
overflow: hidden;
border: 0;
} }
.h5p-iframe-wrapper.h5p-semi-fullscreen .buttons button:before { .h5p-iframe-wrapper.h5p-semi-fullscreen .buttons button:before {
content: 'Exit '; content: 'Exit ';