From 44b5d8997020f3e17fc363834a4af4f57b30f377 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 28 Oct 2013 14:08:59 +0100 Subject: [PATCH 01/10] Fixed iframe solution in IE8. --- js/h5p.js | 15 ++++++++------- styles/h5p.css | 5 ++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index d8b040f..75e42ee 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1,7 +1,7 @@ var H5P = H5P || {}; // 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 // Scans for ".h5p-content" @@ -80,18 +80,19 @@ H5P.init = function () { // Add scripts required for this iFrame from settings H5PIntegration.addFilesToIframe($iframe, contentId); }); - if ($h5pIframes.length > 0) { + 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? - setInterval(function h5pIframeResizer() { + setInterval(function () { $h5pIframes.each(function (idx, iframe) { - var contentHeight = iframe.contentDocument.body.offsetHeight; - var frameHeight = H5P.jQuery(iframe).innerHeight(); + var $iframe = H5P.jQuery(iframe); + var contentHeight = $iframe.contents().height(); + var frameHeight = $iframe.innerHeight(); if (frameHeight !== contentHeight) { - H5P.resizeIframe(H5P.jQuery(iframe).data('content-id'), contentHeight); + H5P.resizeIframe($iframe.data('content-id'), contentHeight); } }); - }, 100); + }, 250); } }; diff --git a/styles/h5p.css b/styles/h5p.css index 8330945..96d7b41 100644 --- a/styles/h5p.css +++ b/styles/h5p.css @@ -60,7 +60,8 @@ div.h5p-semi-fullscreen { height: 100%; } .h5p-iframe-wrapper { - width: auto; height: auto; + width: auto; + height: auto; } .h5p-fullscreen .h5p-iframe-wrapper, @@ -91,6 +92,8 @@ div.h5p-semi-fullscreen { width: 100%; height: 100%; z-index: 10; + overflow: hidden; + border: 0; } .h5p-iframe-wrapper.h5p-semi-fullscreen .buttons button:before { content: 'Exit '; From 0d97f6952c5b05398f4c5f4d698c76915fac3aaa Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Fri, 8 Nov 2013 18:16:41 +0100 Subject: [PATCH 02/10] Add iframe support and more license types --- h5p.classes.php | 6 +-- js/h5p.js | 132 +++++++++++++++++++++++++++++++++++++++++------- styles/h5p.css | 93 +++++++++++++++++++++++++++++++++- 3 files changed, 208 insertions(+), 23 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 0068902..e2beb8e 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -244,7 +244,7 @@ class H5PValidator { private $h5pOptional = array( 'contentType' => '/^.{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( 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'majorVersion' => '/^[0-9]{1,5}$/', @@ -268,7 +268,7 @@ class H5PValidator { private $libraryOptional = array( '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,}$/', 'dynamicDependencies' => array( 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', @@ -1870,4 +1870,4 @@ class H5PContentValidator { } } -?> \ No newline at end of file +?> diff --git a/js/h5p.js b/js/h5p.js index 3dca4ea..7e9b3fb 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1,6 +1,8 @@ var H5P = H5P || {}; -// +// This needs to be determined before init is run. +H5P.isFramed = (window.self !== window.top); // (window.parent !== window); + // Initialize H5P content // Scans for ".h5p-content" H5P.init = function () { @@ -11,11 +13,18 @@ H5P.init = function () { H5P.$body = H5P.jQuery('body'); } + // Is this H5P being run in a frame? + if (H5P.isFramed) { + H5P.$body.addClass('h5p-iframe-content'); + } + if (H5P.fullScreenBrowserPrefix === undefined) { if (document.documentElement.requestFullScreen) { H5P.fullScreenBrowserPrefix = ''; } - else if (document.documentElement.webkitRequestFullScreen && navigator.userAgent.indexOf('Android') === -1) { // Skip Android + else if (document.documentElement.webkitRequestFullScreen + && navigator.userAgent.indexOf('Android') === -1 // Skip Android + ) { // Safari has stopped working as of v6.0.3. (Specifying keyboard input // makes webkitRequestFullScreen silently fail.) The following code // assumes that the Safari developers figure out how to properly handle @@ -37,12 +46,17 @@ H5P.init = function () { } } + // H5Ps added in normal DIV. H5P.jQuery(".h5p-content").each(function (idx, el) { - var $el = H5P.jQuery(el); - var contentId = $el.data('content-id'); - var obj = new (H5P.classFromName($el.data('class')))(H5P.jQuery.parseJSON(H5PIntegration.getJsonContent(contentId)), contentId); + var $el = H5P.jQuery(el), + contentId = $el.data('content-id'), + mainLibrary = $el.data('class'), + obj = new (H5P.classFromName(mainLibrary))(H5P.jQuery.parseJSON(H5PIntegration.getJsonContent(contentId)), contentId); + + // Render H5P in container. obj.attach($el); + // Add Fullscreen button if relevant. if (H5PIntegration.getFullscreen(contentId)) { H5P.jQuery('').insertBefore($el).children().click(function () { H5P.fullScreen($el, obj); @@ -50,6 +64,47 @@ H5P.init = function () { }); } }); + + // H5Ps living in iframes. Note: Fullscreen button will be added + // inside iFrame if relevant + var $h5pIframes = H5P.jQuery(".h5p-iframe"); + if ($h5pIframes.length !== 0) { + $h5pIframes.each(function (idx, iframe) { + var $iframe = H5P.jQuery(iframe), + contentId = $iframe.data('content-id'), + mainLibrary = $iframe.data('class'); + + iframe.contentDocument.open(); + iframe.contentDocument.write('' + H5PIntegration.getHeadTags(contentId) + '
'); + iframe.contentDocument.close(); + }); + + // 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 () { + $h5pIframes.each(function (idx, iframe) { + var $iframe = H5P.jQuery(iframe); + var contentHeight = $iframe.contents().height(); + var frameHeight = $iframe.innerHeight(); + + if (frameHeight !== contentHeight) { + H5P.resizeIframe($iframe.data('content-id'), contentHeight); + } + }); + }, 250); + } +}; + +H5P.fullScreenIframe = function (contentId, obj, exitCallback) { + H5P.fullScreen(H5P.jQuery('#iframe-wrapper-' + contentId), obj, exitCallback); +}; + +H5P.resizeIframe = function (contentId, height) { + var iframe = document.getElementById('iframe-' + contentId); + // Don't allow iFrame to grow beyond window height; + if (height > window.innerHeight) { + height = window.innerHeight; + } + iframe.style.height = (H5P.isFullscreen) ? '100%' : height + 'px'; }; /** @@ -59,22 +114,44 @@ H5P.init = function () { * @param {object} obj H5P * @returns {undefined} */ -H5P.fullScreen = function ($el, obj) { +H5P.fullScreen = function ($el, obj, exitCallback) { + if (H5P.isFramed) { + var $classes = H5P.jQuery('html').add(H5P.$body).add($el); + $classes.addClass('h5p-fullscreen'); + window.parent.H5P.fullScreenIframe($el.data('content-id'), obj, function () { + $classes.removeClass('h5p-fullscreen'); + }); + + return; + } + if (H5P.fullScreenBrowserPrefix === undefined) { // Create semi fullscreen. $el.add(H5P.$body).addClass('h5p-semi-fullscreen'); // Move H5P content to top of body to make sure it is above other page // content. Insert placeholder in original position to be able to move it // back. - $el.after('
').prependTo(H5P.$body); + // THIS DOES NOT WORK WITH IFRAMED CONTENT, iframe will reload/fail. + // $el.after('
').prependTo(H5P.$body); + + H5P.isFullscreen = true; var $disable = H5P.jQuery('Disable fullscreen').appendTo($el); var keyup, disableSemiFullscreen = function () { $el.add(H5P.$body).removeClass('h5p-semi-fullscreen'); - $('#h5pfullscreenreplacementplaceholder').before($el).remove(); + // H5P.jQuery('#h5pfullscreenreplacementplaceholder').before($el).remove(); $disable.remove(); + H5P.isFullscreen = false; H5P.$body.unbind('keyup', keyup); + H5P.jQuery(".h5p-iframe").each(function (idx, el) { + H5P.resizeIframe(H5P.jQuery(el).data('content-id'), 0); + }); + + if (exitCallback) { + exitCallback(); + } + if (obj.resize !== undefined) { obj.resize(false); } @@ -91,12 +168,23 @@ H5P.fullScreen = function ($el, obj) { } else { var first, eventName = H5P.fullScreenBrowserPrefix + 'fullscreenchange'; + H5P.isFullscreen = true; document.addEventListener(eventName, function () { if (first === undefined) { first = false; return; } + H5P.isFullscreen = false; $el.add(H5P.$body).removeClass('h5p-fullscreen'); + + H5P.jQuery(".h5p-iframe").each(function (idx, el) { + H5P.resizeIframe(H5P.jQuery(el).data('content-id'), 0); + }); + + if (exitCallback) { + exitCallback(); + } + if (obj.resize !== undefined) { obj.resize(false); } @@ -112,7 +200,9 @@ H5P.fullScreen = function ($el, obj) { $el.add(H5P.$body).addClass('h5p-fullscreen'); } - + H5P.jQuery(".h5p-iframe").each(function (idx, el) { + H5P.resizeIframe(H5P.jQuery(el).data('content-id'), 0); + }); if (obj.resize !== undefined) { obj.resize(true); } @@ -317,10 +407,10 @@ H5P.setFinished = function (contentId, points, maxPoints) { }; // Add indexOf to browsers that lack them. (IEs) -if(!Array.prototype.indexOf) { - Array.prototype.indexOf = function(needle) { - for(var i = 0; i < this.length; i++) { - if(this[i] === needle) { +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (needle) { + for (var i = 0; i < this.length; i++) { + if (this[i] === needle) { return i; } } @@ -330,13 +420,19 @@ if(!Array.prototype.indexOf) { // Need to define trim() since this is not available on older IEs, // and trim is used in several libs -if(String.prototype.trim === undefined) { +if (String.prototype.trim === undefined) { String.prototype.trim = function () { return H5P.trim(this); }; } -// Finally, we want to run init when document is ready. -H5P.jQuery(document).ready(function(){ - H5P.init(); -}); \ No newline at end of file +// 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(). +if (H5P.jQuery) { + H5P.jQuery(document).ready(function () { + if (!H5P.initialized) { + H5P.initialized = true; + H5P.init(); + } + }); +} diff --git a/styles/h5p.css b/styles/h5p.css index 4b2b265..96d7b41 100644 --- a/styles/h5p.css +++ b/styles/h5p.css @@ -1,9 +1,15 @@ +body.h5p-iframe-content { + font-family: Arial, Helvetica, sans-serif; + margin: 0; +} + body.h5p-semi-fullscreen { overflow: hidden; } .h5p-content { - width: 100%; - height: 100%; + display: block; + width: auto; + height: auto; background: #fefefe; font-size: 16px; } @@ -48,4 +54,87 @@ div.h5p-semi-fullscreen { text-decoration: none; opacity: 0.9; filter: alpha(opacity = 90); +} +.h5p-iframe-wrapper.h5p-fullscreen { + width: 100%; + height: 100%; +} +.h5p-iframe-wrapper { + width: auto; + height: auto; +} + +.h5p-fullscreen .h5p-iframe-wrapper, +.h5p-semi-fullscreen .h5p-iframe-wrapper { + width: 100%; + height: 100%; +} + +.h5p-iframe-wrapper.h5p-semi-fullscreen { + width: 100%; + height: 100%; + background: black; + position: fixed; + top: 0; + left: 0; + z-index: 100; +} +.h5p-iframe-wrapper .buttons { + text-align: right; +} +.h5p-iframe-wrapper.h5p-semi-fullscreen .buttons { + position: absolute; + top: 0; + right: 0; + z-index: 20; +} +.h5p-iframe-wrapper .h5p-iframe { + width: 100%; + height: 100%; + z-index: 10; + overflow: hidden; + border: 0; +} +.h5p-iframe-wrapper.h5p-semi-fullscreen .buttons button:before { + content: 'Exit '; +} +.h5p-iframe-wrapper button.fullscreen { + color: #e5eef6; + text-decoration: none; + padding: 6px 12px; + background: #539ad7; + background-image: -webkit-linear-gradient(top,#4584ba,#539ad7); + background-image: -moz-linear-gradient(top,#4584ba,#539ad7); + background-image: -o-linear-gradient(top,#4584ba,#539ad7); + background-image: -ms-linear-gradient(top,#4584ba,#539ad7); + background-image: linear-gradient(to bottom,#4584ba,#539ad7); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4584ba',endColorstr='#539ad7',GradientType=0); + box-shadow: 0 0 8px #bebebe; + border: none; + opacity: 0.8; +} +.h5p-iframe-wrapper button.fullscreen:hover { + opacity: 1; + background: #539ad7; + background-image: -webkit-linear-gradient(top,#539ad7,#4584ba); + background-image: -moz-linear-gradient(top,#539ad7,#4584ba); + background-image: -o-linear-gradient(top,#539ad7,#4584ba); + background-image: -ms-linear-gradient(top,#539ad7,#4584ba); + background-image: linear-gradient(to bottom,#539ad7,#4584ba); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#539ad7',endColorstr='#4584ba',GradientType=0); + cursor: pointer; +} + +html.h5p-fullscreen, body.h5p-fullscreen { + height: 100%; + overflow: hidden; +} +.h5p-fullscreen .h5p-content-controls { + display: none; +} +body.h5p-iframe-content div.h5p-content { + overflow: hidden; +} +body.h5p-iframe-content.h5p-fullscreen div.h5p-content { + height: 100%; } \ No newline at end of file From 1a6ef6f0fa3db0a644d02a67dca640c1f9aba3d2 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Fri, 8 Nov 2013 18:24:42 +0100 Subject: [PATCH 03/10] Why are we using | instead of / around the regex? Change to / so that we may use | inside the regex --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index e2beb8e..f37a4c8 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1240,7 +1240,7 @@ class H5PContentValidator { // Check if string is according to optional regexp in semantics if (isset($semantics->regexp)) { - $pattern = '|' . $semantics->regexp->pattern . '|'; + $pattern = '/' . $semantics->regexp->pattern . '/'; $pattern .= isset($semantics->regexp->modifiers) ? $semantics->regexp->modifiers : ''; if (preg_match($pattern, $text) === 0) { // Note: explicitly ignore return value FALSE, to avoid removing text From 2454bcfa2a2ac92ecf1c7b894aebd3003ac01425 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Fri, 8 Nov 2013 18:46:47 +0100 Subject: [PATCH 04/10] BUGFIX: The strikethrough was removed when an H5P was saved --- h5p.classes.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/h5p.classes.php b/h5p.classes.php index f37a4c8..51acc40 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1225,6 +1225,9 @@ class H5PContentValidator { if (in_array('ul', $tags) || in_array('ol', $tags) && ! in_array('li', $tags)) { $tags[] = 'li'; } + if (in_array('del', $tags) || in_array('strike', $tags) && ! in_array('s', $tags)) { + $tags[] = 's'; + } // Strip invalid HTML tags. $text = $this->filter_xss($text, $tags); } From d9532a44edf5c320e70a6ece539aa63843f80f7d Mon Sep 17 00:00:00 2001 From: Frank Ronny Larsen Date: Wed, 13 Nov 2013 15:35:10 +0100 Subject: [PATCH 05/10] Escape '|' characters in regexp --- h5p.classes.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 51380ff..796a564 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1240,7 +1240,9 @@ class H5PContentValidator { // Check if string is according to optional regexp in semantics 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 : ''; if (preg_match($pattern, $text) === 0) { // Note: explicitly ignore return value FALSE, to avoid removing text From 5ca6a5f39a8eabbe09494fee0da272abeb6ec282 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Thu, 14 Nov 2013 13:48:07 +0100 Subject: [PATCH 06/10] Issue #2120227 by gafolini, Ellinokon and falcon: Add export feature to H5P --- h5p.classes.php | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ js/h5p.js | 6 ++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 5b60b58..8ace0aa 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -219,6 +219,122 @@ interface H5PFrameworkInterface { * Library Id */ public function deleteLibraryDependencies($libraryId); + + /** + * Get the paths we need to export + * + * @param int $contentId + * @param string $title + * @param string $language + * @return export object + */ + public function getExportData($contentId, $title, $language); + /** + * Check if export is enabled. + */ + public function exportEnabled(); +} + +/** +* This class is used for exporting zips +*/ +Class H5PExport { + public $h5pF; + public $h5pC; + + /** + * Constructor for the H5PStorage + * + * @param object $H5PFramework + * The frameworks implementation of the H5PFrameworkInterface + */ + public function __construct($H5PFramework, $H5PCore) { + $this->h5pF = $H5PFramework; + $this->h5pC = $H5PCore; + } + /** + * Create the h5p package + * + * @param object $exports + * The data to be exported. + * @return h5p package. + */ + public function exportToZip($exports) { + $h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR; + $tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $exports['contentId']; + $zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $exports['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 . $exports['contentId'], $tempPath . DIRECTORY_SEPARATOR . 'content'); + // Copies libraries to temp dir and create mention in h5p.json + foreach($exports['libraries'] as $library) { + $source = $h5pDir . 'libraries' . DIRECTORY_SEPARATOR . $library['machine_name'] . '-' . $library['major_version'] . '.' . $library['minor_version']; + $destination = $tempPath . DIRECTORY_SEPARATOR . $library['machine_name']; + $this->h5pC->copyTree($source, $destination); + + // Set preloaded and dynamic dependencies + if ($library['preloaded']) { + $preloadedDependencies[] = array( + 'machineName' => $library['machine_name'], + 'majorVersion' => $library['major_version'], + 'minorVersion' => $library['minor_version'], + ); + } else { + $dynamicDependencies[] = array( + 'machineName' => $library['machine_name'], + 'majorVersion' => $library['major_version'], + 'minorVersion' => $library['minor_version'], + ); + } + } + // Make embedTypes into an array + $embedTypes = explode(', ', $exports['embed_type']); + + // Build h5p.json + $h5pJson = array ( + 'title' => $exports['title'], + 'language' => $exports['language'], + 'mainLibrary' => $exports['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=' . $exports['title'] . '.h5p'); + readfile ($zipPath); + } } /** diff --git a/js/h5p.js b/js/h5p.js index 7e9b3fb..3a9b15d 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -62,6 +62,10 @@ H5P.init = function () { H5P.fullScreen($el, obj); return false; }); + }; + + if (H5PIntegration.getExport()) { + H5P.jQuery('').insertAfter($el).children(); } }); @@ -435,4 +439,4 @@ if (H5P.jQuery) { H5P.init(); } }); -} +} \ No newline at end of file From 99ee7a47aed0b20841584dee79909446e09e4c07 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Thu, 14 Nov 2013 14:05:34 +0100 Subject: [PATCH 07/10] Documentation improvements --- h5p.classes.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 8ace0aa..3335158 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -221,12 +221,26 @@ interface H5PFrameworkInterface { public function deleteLibraryDependencies($libraryId); /** - * Get the paths we need to export + * 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 - * @return export object + * 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), + * 'embed_type' => string, + * 'libraries' => array( + * 'machine_name' => string, + * 'major_version' => int, + * 'minor_version' => int, + * 'preloaded' => int(0|1), + * 'language' => string, */ public function getExportData($contentId, $title, $language); /** From f98cce49a6752b3b4990df65179d5fdde00a464e Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Thu, 14 Nov 2013 14:10:55 +0100 Subject: [PATCH 08/10] Make sure we only use camel case in the H5P php library --- h5p.classes.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 3335158..33375b9 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -234,11 +234,11 @@ interface H5PFrameworkInterface { * 'title' => string, * 'contentId' => string/int, * 'mainLibrary' => string (machine name for main library), - * 'embed_type' => string, + * 'embedType' => string, * 'libraries' => array( - * 'machine_name' => string, - * 'major_version' => int, - * 'minor_version' => int, + * 'machineName' => string, + * 'majorVersion' => int, + * 'minorVersion' => int, * 'preloaded' => int(0|1), * 'language' => string, */ @@ -284,27 +284,27 @@ Class H5PExport { $this->h5pC->copyTree($h5pDir . 'content' . DIRECTORY_SEPARATOR . $exports['contentId'], $tempPath . DIRECTORY_SEPARATOR . 'content'); // Copies libraries to temp dir and create mention in h5p.json foreach($exports['libraries'] as $library) { - $source = $h5pDir . 'libraries' . DIRECTORY_SEPARATOR . $library['machine_name'] . '-' . $library['major_version'] . '.' . $library['minor_version']; - $destination = $tempPath . DIRECTORY_SEPARATOR . $library['machine_name']; + $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['machine_name'], - 'majorVersion' => $library['major_version'], - 'minorVersion' => $library['minor_version'], + 'machineName' => $library['machineName'], + 'majorVersion' => $library['majorVersion'], + 'minorVersion' => $library['minorVersion'], ); } else { $dynamicDependencies[] = array( - 'machineName' => $library['machine_name'], - 'majorVersion' => $library['major_version'], - 'minorVersion' => $library['minor_version'], + 'machineName' => $library['machineName'], + 'majorVersion' => $library['majorVersion'], + 'minorVersion' => $library['minorVersion'], ); } } // Make embedTypes into an array - $embedTypes = explode(', ', $exports['embed_type']); + $embedTypes = explode(', ', $exports['embedType']); // Build h5p.json $h5pJson = array ( From 9dd710a8fb3f5f92ceb97d9dc8d8b07d20f208c4 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Thu, 14 Nov 2013 23:42:10 +0100 Subject: [PATCH 09/10] Renaming functions --- h5p.classes.php | 206 ++++++++++++++++++++++++------------------------ js/h5p.js | 2 +- 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 33375b9..5f22337 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -246,109 +246,7 @@ interface H5PFrameworkInterface { /** * Check if export is enabled. */ - public function exportEnabled(); -} - -/** -* This class is used for exporting zips -*/ -Class H5PExport { - public $h5pF; - public $h5pC; - - /** - * Constructor for the H5PStorage - * - * @param object $H5PFramework - * The frameworks implementation of the H5PFrameworkInterface - */ - public function __construct($H5PFramework, $H5PCore) { - $this->h5pF = $H5PFramework; - $this->h5pC = $H5PCore; - } - /** - * Create the h5p package - * - * @param object $exports - * The data to be exported. - * @return h5p package. - */ - public function exportToZip($exports) { - $h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR; - $tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $exports['contentId']; - $zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $exports['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 . $exports['contentId'], $tempPath . DIRECTORY_SEPARATOR . 'content'); - // Copies libraries to temp dir and create mention in h5p.json - foreach($exports['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(', ', $exports['embedType']); - - // Build h5p.json - $h5pJson = array ( - 'title' => $exports['title'], - 'language' => $exports['language'], - 'mainLibrary' => $exports['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=' . $exports['title'] . '.h5p'); - readfile ($zipPath); - } + public function isExportEnabled(); } /** @@ -1133,6 +1031,108 @@ class H5PStorage { } } +/** +* This class is used for exporting zips +*/ +Class H5PExport { + public $h5pF; + public $h5pC; + + /** + * Constructor for the H5PStorage + * + * @param object $H5PFramework + * The frameworks implementation of the H5PFrameworkInterface + */ + public function __construct($H5PFramework, $H5PCore) { + $this->h5pF = $H5PFramework; + $this->h5pC = $H5PCore; + } + /** + * Create the h5p package + * + * @param object $exports + * The data to be exported. + * @return h5p package. + */ + public function exportToZip($exports) { + $h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR; + $tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $exports['contentId']; + $zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $exports['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 . $exports['contentId'], $tempPath . DIRECTORY_SEPARATOR . 'content'); + // Copies libraries to temp dir and create mention in h5p.json + foreach($exports['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(', ', $exports['embedType']); + + // Build h5p.json + $h5pJson = array ( + 'title' => $exports['title'], + 'language' => $exports['language'], + 'mainLibrary' => $exports['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=' . $exports['title'] . '.h5p'); + readfile ($zipPath); + } +} + /** * Functions and storage shared by the other H5P classes */ diff --git a/js/h5p.js b/js/h5p.js index 3a9b15d..b14d893 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -64,7 +64,7 @@ H5P.init = function () { }); }; - if (H5PIntegration.getExport()) { + if (H5PIntegration.isExportEnabled()) { H5P.jQuery('').insertAfter($el).children(); } }); From cab8e258be39b4560244c902c14c710d17433008 Mon Sep 17 00:00:00 2001 From: Svein-Tore Griff With Date: Fri, 15 Nov 2013 01:13:00 +0100 Subject: [PATCH 10/10] Export improvements --- h5p.classes.php | 32 +++++++++++++++++--------------- js/h5p.js | 4 ---- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 5f22337..4ac546b 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1039,33 +1039,35 @@ Class H5PExport { public $h5pC; /** - * Constructor for the H5PStorage + * 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 + * Create the H5P package * - * @param object $exports + * @param object $exportData * The data to be exported. - * @return h5p package. + * @return H5P package. */ - public function exportToZip($exports) { + public function exportToZip($exportData) { $h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR; - $tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $exports['contentId']; - $zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $exports['contentId'] . '.h5p'; + $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 . $exports['contentId'], $tempPath . DIRECTORY_SEPARATOR . 'content'); + $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($exports['libraries'] as $library) { + 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); @@ -1086,15 +1088,15 @@ Class H5PExport { } } // Make embedTypes into an array - $embedTypes = explode(', ', $exports['embedType']); + $embedTypes = explode(', ', $exportData['embedType']); // Build h5p.json $h5pJson = array ( - 'title' => $exports['title'], - 'language' => $exports['language'], - 'mainLibrary' => $exports['mainLibrary'], + '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; } @@ -1128,7 +1130,7 @@ Class H5PExport { // Set headers for automagic download!! header('Content-Description: File Transfer'); header('Content-Type: application/zip'); - header('Content-Disposition: attachment; filename=' . $exports['title'] . '.h5p'); + header('Content-Disposition: attachment; filename="' . $exportData['title'] . '.h5p"'); readfile ($zipPath); } } diff --git a/js/h5p.js b/js/h5p.js index b14d893..dc04eb6 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -63,10 +63,6 @@ H5P.init = function () { return false; }); }; - - if (H5PIntegration.isExportEnabled()) { - H5P.jQuery('').insertAfter($el).children(); - } }); // H5Ps living in iframes. Note: Fullscreen button will be added