From 7b38c3571e89ea3880d7d95bd41c72538ab9fafa Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 25 Apr 2016 14:30:12 +0200 Subject: [PATCH 01/36] Made dir handling private to prevent abuse h5p/h5p-moodle-plugin#48 HFJ-1845 --- h5p-default-storage.class.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 9f11f1c..1c564ad 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -297,13 +297,10 @@ class H5PDefaultStorage implements \H5PFileStorage { * Recursive function that makes sure the specified directory exists and * is writable. * - * TODO: Will be made private when the editor file handling is done by this - * class! - * * @param string $path * @return bool */ - public static function dirReady($path) { + private static function dirReady($path) { if (!file_exists($path)) { $parent = preg_replace("/\/[^\/]+\/?$/", '', $path); if (!self::dirReady($parent)) { From 325ea275622f16327ea2bd8318c50961cd2b0551 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 25 Apr 2016 14:31:25 +0200 Subject: [PATCH 02/36] Added func. for saving uploaded files h5p/h5p-moodle-plugin#48 HFJ-1845 --- h5p-default-storage.class.php | 20 ++++++++++++++++++++ h5p-file-storage.interface.php | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 1c564ad..f78b829 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -257,6 +257,26 @@ class H5PDefaultStorage implements \H5PFileStorage { return file_get_contents($this->path . $file_path); } + /** + * 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 + */ + public function saveFile($file, $contentId) { + $path = $this->path . '/' . ($contentId === 0 ? 'editor' : 'content') . '/' . $file->getType() . 's/' . $file->getName(); + self::dirReady($path); + + $fileData = $file->getData(); + if ($fileData) { + file_put_contents($path, $fileData); + } + else { + copy($_FILES['file']['tmp_name'], $path); + } + } + /** * Recursive function for copying directories. * diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index b75625e..9f44f94 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -125,4 +125,13 @@ interface H5PFileStorage { * @return string contents */ public function getContent($file_path); + + /** + * 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 + */ + public function saveFile($file, $contentId); } From e056b6776a17d7d7d15e19c6de507d7b62d427c1 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 4 May 2016 16:52:02 +0200 Subject: [PATCH 03/36] More functions for handling content files Added three more functions to File Storage Interface h5p/h5p-moodle-plugin#49 HFJ-1846 --- h5p-default-storage.class.php | 62 ++++++++++++++++++++++++++++++++++ h5p-file-storage.interface.php | 30 ++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index f78b829..fb58f30 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -277,6 +277,68 @@ class H5PDefaultStorage implements \H5PFileStorage { } } + /** + * 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 + if ($formId === 'editor') { + $sourcepath = (empty($this->editorpath) ? "{$this->path}/editor" : $this->editorpath); + } + else { + $sourcepath = "{$this->path}/content/{$fromId}"; + } + $sourcepath .= '/' . $file; + + // Determine target path + $targetpath = "{$this->path}/content/{$toId}"; + + // Make sure it's ready + self::dirReady($targetpath); + + $targetpath .= '/' . $file; + + // 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); + } + + /** + * 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); + } + } + /** * Recursive function for copying directories. * diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 9f44f94..1ae1b47 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -134,4 +134,34 @@ interface H5PFileStorage { * @param int $contentid */ public function saveFile($file, $contentId); + + /** + * Copy a file from another content or editor tmp dir. + * Used when copy pasting content in H5P. + * + * @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); + + /** + * 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 getContentFile($file, $contentId); + + /** + * Remove content files that are no longer used. + * 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); } From a36b2f12124c2997b5e7cb0b3218a0bc1798d1e2 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 6 May 2016 10:40:28 +0200 Subject: [PATCH 04/36] Removed TODO which makes no sense --- styles/h5p-admin.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/h5p-admin.css b/styles/h5p-admin.css index bcd72d5..64c025c 100644 --- a/styles/h5p-admin.css +++ b/styles/h5p-admin.css @@ -204,7 +204,7 @@ button.h5p-admin.disabled:hover { line-height: 130%; border: none; background: none; - font-family: 'H5P'; /* TODO: Find content */ + font-family: 'H5P'; font-size: 1.4em; } .h5p-content-pager > button:focus { From 51698d325b32625b469a28fecf6cc344f3e9a57d Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 9 May 2016 11:53:12 +0200 Subject: [PATCH 05/36] Corrected file storage interface --- h5p-file-storage.interface.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 1ae1b47..a003256 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -150,7 +150,7 @@ interface H5PFileStorage { * Used when saving content. * * @param string $file path + name - * @param int $contentid + * @param int $contentId * @return string|int File ID or NULL if not found */ public function getContentFile($file, $contentId); @@ -160,8 +160,7 @@ interface H5PFileStorage { * Used when saving content. * * @param string $file path + name - * @param int $contentid - * @return string|int File ID or NULL if not found + * @param int $contentId */ public function removeContentFile($file, $contentId); } From 417af36c81186793c575867df11130600305d080 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 12 May 2016 15:52:37 +0200 Subject: [PATCH 06/36] Bugfix: Must be able to run from CLI --- h5p.classes.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 93c4213..7f87f7c 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2469,7 +2469,9 @@ class H5PCore { // Determine remote/visitor origin if ($type === 'network' || - ($type === 'local' && !preg_match('/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/i', $_SERVER['REMOTE_ADDR']))) { + ($type === 'local' && + isset($_SERVER['REMOTE_ADDR']) && + !preg_match('/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/i', $_SERVER['REMOTE_ADDR']))) { if (isset($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) { // Internet $this->h5pF->setOption('site_type', 'internet'); From 73a23d4679e436e62056981f114799add03f2b84 Mon Sep 17 00:00:00 2001 From: Thomas Marstrander Date: Mon, 30 May 2016 10:35:06 +0200 Subject: [PATCH 07/36] Send context id together with post data in order to determine our context. HFJ-1964 --- js/h5p.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index 5a1c9e1..95f2caa 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1799,7 +1799,8 @@ H5P.createTitle = function (rawTitle, maxLength) { data: (data === null ? 0 : data), preload: (preload ? 1 : 0), invalidate: (invalidate ? 1 : 0), - token: H5PIntegration.tokens.contentUserData + token: H5PIntegration.tokens.contentUserData, + contextId: H5PIntegration.editor.uploadParams.contextId }; } else { From b102a7de21486d23508a9e1790343304a07308b1 Mon Sep 17 00:00:00 2001 From: Thomas Marstrander Date: Tue, 31 May 2016 14:43:12 +0200 Subject: [PATCH 08/36] Only add editor context when in editor. HFJ-1972 --- js/h5p.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 95f2caa..71484ae 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1799,9 +1799,13 @@ H5P.createTitle = function (rawTitle, maxLength) { data: (data === null ? 0 : data), preload: (preload ? 1 : 0), invalidate: (invalidate ? 1 : 0), - token: H5PIntegration.tokens.contentUserData, - contextId: H5PIntegration.editor.uploadParams.contextId + token: H5PIntegration.tokens.contentUserData }; + + // Set editor context + if (H5PIntegration.editor && H5PIntegration.editor.uploadParams) { + options.data.contextId = H5PIntegration.editor.uploadParams.contextId; + } } else { options.type = 'GET'; From fca0537a4daa8df549629084caf887fd97920c31 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 13 Jun 2016 10:31:09 +0200 Subject: [PATCH 09/36] Use context and token in URL prefix --- js/h5p.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 551bde0..e666736 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1655,8 +1655,7 @@ H5P.setFinished = function (contentId, score, maxScore, time) { maxScore: maxScore, opened: toUnix(H5P.opened[contentId]), finished: toUnix(new Date()), - time: time, - token: H5PIntegration.tokens.result + time: time }); } }; @@ -1800,14 +1799,8 @@ H5P.createTitle = function (rawTitle, maxLength) { options.data = { data: (data === null ? 0 : data), preload: (preload ? 1 : 0), - invalidate: (invalidate ? 1 : 0), - token: H5PIntegration.tokens.contentUserData + invalidate: (invalidate ? 1 : 0) }; - - // Set editor context - if (H5PIntegration.editor && H5PIntegration.editor.uploadParams) { - options.data.contextId = H5PIntegration.editor.uploadParams.contextId; - } } else { options.type = 'GET'; From 0ee4cc57fc505dc5a59c47a585c8c5f06ac5ea1f Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 13 Jun 2016 12:47:44 +0200 Subject: [PATCH 10/36] Fixed saveFile not working for default storage --- h5p-default-storage.class.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index fb58f30..aa9c80a 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -265,9 +265,13 @@ class H5PDefaultStorage implements \H5PFileStorage { * @param int $contentid */ public function saveFile($file, $contentId) { - $path = $this->path . '/' . ($contentId === 0 ? 'editor' : 'content') . '/' . $file->getType() . 's/' . $file->getName(); + // Prepare directory + $path = $this->path . '/' . (empty($contentId) ? 'editor' : 'content/' . $contentId) . '/' . $file->getType() . 's'; self::dirReady($path); + // Add filename to path + $path .= '/' . $file->getName(); + $fileData = $file->getData(); if ($fileData) { file_put_contents($path, $fileData); From 89541acfa83628051431fd2667f591818e55a3f7 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 14 Jun 2016 12:39:15 +0200 Subject: [PATCH 11/36] Allow for alternative editor path (used by Drupal) --- h5p-default-storage.class.php | 37 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index aa9c80a..a3c70f6 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -16,17 +16,20 @@ * @license MIT */ class H5PDefaultStorage implements \H5PFileStorage { - private $path; + private $path, $alteditorpath; /** * The great Constructor! * * @param string $path * The base location of H5P files + * @param string $alteditorpath + * Optional. Use a different editor path */ - function __construct($path) { + function __construct($path, $alteditorpath = NULL) { // Set H5P storage path $this->path = $path; + $this->alteditorpath = $alteditorpath; } /** @@ -258,15 +261,23 @@ class H5PDefaultStorage implements \H5PFileStorage { } /** - * 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 + * 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 */ public function saveFile($file, $contentId) { // Prepare directory - $path = $this->path . '/' . (empty($contentId) ? 'editor' : 'content/' . $contentId) . '/' . $file->getType() . 's'; + if (empty($contentId)) { + // Should be in editor tmp folder + $path = ($this->alteditorpath !== NULL ? $this->alteditorpath : $path . '/editor'); + } + else { + // Should be in content folder + $path = $this->path . '/content/' . $contentId; + } + $path .= '/' . $file->getType() . 's'; self::dirReady($path); // Add filename to path @@ -291,8 +302,8 @@ class H5PDefaultStorage implements \H5PFileStorage { */ public function cloneContentFile($file, $fromId, $toId) { // Determine source path - if ($formId === 'editor') { - $sourcepath = (empty($this->editorpath) ? "{$this->path}/editor" : $this->editorpath); + if ($fromId === 'editor') { + $sourcepath = ($this->alteditorpath !== NULL ? $this->alteditorpath : "{$this->path}/editor"); } else { $sourcepath = "{$this->path}/content/{$fromId}"; @@ -300,12 +311,14 @@ class H5PDefaultStorage implements \H5PFileStorage { $sourcepath .= '/' . $file; // Determine target path - $targetpath = "{$this->path}/content/{$toId}"; + $filename = basename($file); + $filedir = str_replace($filename, '', $file); + $targetpath = "{$this->path}/content/{$toId}/{$filedir}"; // Make sure it's ready self::dirReady($targetpath); - $targetpath .= '/' . $file; + $targetpath .= $filename; // Check to see if source exist and if target doesn't if (!file_exists($sourcepath) || file_exists($targetpath)) { From a9a99afb9b0330d45de375ddf537a74765391604 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 16 Jun 2016 10:43:17 +0200 Subject: [PATCH 12/36] Handle non-existing content dir --- h5p-default-storage.class.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index a3c70f6..2f446c1 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -109,7 +109,15 @@ class H5PDefaultStorage implements \H5PFileStorage { * Where the content folder will be saved */ public function exportContent($id, $target) { - self::copyFileTree("{$this->path}/content/{$id}", $target); + $source = "{$this->path}/content/{$id}"; + if (file_exists($source)) { + // Copy content folder if it exists + self::copyFileTree("{$this->path}/content/{$id}", $target); + } + else { + // No contnet folder, create emty dir for content.json + self::dirReady($target); + } } /** From bcdaf59e5e61c403bb154f886e1c2122c9f81878 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Thu, 16 Jun 2016 14:13:51 -0400 Subject: [PATCH 13/36] Minor cleanup --- h5p-default-storage.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 2f446c1..e019990 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -112,7 +112,7 @@ class H5PDefaultStorage implements \H5PFileStorage { $source = "{$this->path}/content/{$id}"; if (file_exists($source)) { // Copy content folder if it exists - self::copyFileTree("{$this->path}/content/{$id}", $target); + self::copyFileTree($source, $target); } else { // No contnet folder, create emty dir for content.json From 7fca1d100d57710be4d37ffb87c5523ca4fe56dc Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 8 Sep 2016 13:32:57 +0200 Subject: [PATCH 14/36] Added content ID to export handlers This is to enable for proper access control. h5p/h5p-moodle-plugin#112 --- h5p-default-storage.class.php | 4 ++-- h5p-file-storage.interface.php | 6 ++++-- h5p.classes.php | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index e019990..75bfde0 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -144,7 +144,7 @@ class H5PDefaultStorage implements \H5PFileStorage { * @param string $filename * Name of export file. */ - public function saveExport($source, $filename) { + public function saveExport($source, $filename, $contentId) { $this->deleteExport($filename); self::dirReady("{$this->path}/exports"); copy($source, "{$this->path}/exports/{$filename}"); @@ -155,7 +155,7 @@ class H5PDefaultStorage implements \H5PFileStorage { * * @param string $filename */ - public function deleteExport($filename) { + public function deleteExport($filename, $contentId) { $target = "{$this->path}/exports/{$filename}"; if (file_exists($target)) { unlink($target); diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index a003256..0aea9fc 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -80,15 +80,17 @@ interface H5PFileStorage { * Path on file system to temporary export file. * @param string $filename * Name of export file. + * @param int $contentId */ - public function saveExport($source, $filename); + public function saveExport($source, $filename, $contentId); /** * Removes given export file * * @param string $filename + * @param int $contentId */ - public function deleteExport($filename); + public function deleteExport($filename, $contentId); /** * Will concatenate all JavaScrips and Stylesheets into two files in order diff --git a/h5p.classes.php b/h5p.classes.php index 64c2ab5..6cdfb18 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1434,7 +1434,7 @@ class H5PStorage { */ public function deletePackage($content) { $this->h5pC->fs->deleteContent($content['id']); - $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p'); + $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p', $content['id']); $this->h5pF->deleteContentData($content['id']); } @@ -1587,7 +1587,7 @@ Class H5PExport { try { // Save export - $this->h5pC->fs->saveExport($tmpFile, $content['slug'] . '-' . $content['id'] . '.h5p'); + $this->h5pC->fs->saveExport($tmpFile, $content['slug'] . '-' . $content['id'] . '.h5p', $content['id']); } catch (Exception $e) { $this->h5pF->setErrorMessage($this->h5pF->t($e->getMessage())); @@ -1633,7 +1633,7 @@ Class H5PExport { * @param array $content object */ public function deleteExport($content) { - $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p'); + $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p', $content['id']); } /** @@ -1837,7 +1837,7 @@ class H5PCore { $content['slug'] = $this->generateContentSlug($content); // Remove old export file - $this->fs->deleteExport($content['id'] . '.h5p'); + $this->fs->deleteExport($content['id'] . '.h5p', $content['id']); } if ($this->exportEnabled) { From 2896c9fdaba4d190aa48bd3017860a9fac37bf66 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 8 Sep 2016 14:48:08 +0200 Subject: [PATCH 15/36] Revert "Added content ID to export handlers" This reverts commit 7fca1d100d57710be4d37ffb87c5523ca4fe56dc. Holding off on the exports for now --- h5p-default-storage.class.php | 4 ++-- h5p-file-storage.interface.php | 6 ++---- h5p.classes.php | 8 ++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 75bfde0..e019990 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -144,7 +144,7 @@ class H5PDefaultStorage implements \H5PFileStorage { * @param string $filename * Name of export file. */ - public function saveExport($source, $filename, $contentId) { + public function saveExport($source, $filename) { $this->deleteExport($filename); self::dirReady("{$this->path}/exports"); copy($source, "{$this->path}/exports/{$filename}"); @@ -155,7 +155,7 @@ class H5PDefaultStorage implements \H5PFileStorage { * * @param string $filename */ - public function deleteExport($filename, $contentId) { + public function deleteExport($filename) { $target = "{$this->path}/exports/{$filename}"; if (file_exists($target)) { unlink($target); diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 0aea9fc..a003256 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -80,17 +80,15 @@ interface H5PFileStorage { * Path on file system to temporary export file. * @param string $filename * Name of export file. - * @param int $contentId */ - public function saveExport($source, $filename, $contentId); + public function saveExport($source, $filename); /** * Removes given export file * * @param string $filename - * @param int $contentId */ - public function deleteExport($filename, $contentId); + public function deleteExport($filename); /** * Will concatenate all JavaScrips and Stylesheets into two files in order diff --git a/h5p.classes.php b/h5p.classes.php index 6cdfb18..64c2ab5 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1434,7 +1434,7 @@ class H5PStorage { */ public function deletePackage($content) { $this->h5pC->fs->deleteContent($content['id']); - $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p', $content['id']); + $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p'); $this->h5pF->deleteContentData($content['id']); } @@ -1587,7 +1587,7 @@ Class H5PExport { try { // Save export - $this->h5pC->fs->saveExport($tmpFile, $content['slug'] . '-' . $content['id'] . '.h5p', $content['id']); + $this->h5pC->fs->saveExport($tmpFile, $content['slug'] . '-' . $content['id'] . '.h5p'); } catch (Exception $e) { $this->h5pF->setErrorMessage($this->h5pF->t($e->getMessage())); @@ -1633,7 +1633,7 @@ Class H5PExport { * @param array $content object */ public function deleteExport($content) { - $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p', $content['id']); + $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p'); } /** @@ -1837,7 +1837,7 @@ class H5PCore { $content['slug'] = $this->generateContentSlug($content); // Remove old export file - $this->fs->deleteExport($content['id'] . '.h5p', $content['id']); + $this->fs->deleteExport($content['id'] . '.h5p'); } if ($this->exportEnabled) { From e8f90c91167886ed4ef8007c5a186c7075c50998 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 8 Sep 2016 16:34:53 +0200 Subject: [PATCH 16/36] Allow overriding of content URLs h5p/h5p-moodle-plugin#112 --- js/h5p.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index 1fd545c..4d61169 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -691,7 +691,11 @@ H5P.getPath = function (path, contentId) { var prefix; if (contentId !== undefined) { - prefix = H5PIntegration.url + '/content/' + contentId; + // Check for custom override URL + prefix = H5PIntegration.contents['cid-' + contentId].contentUrl; + if (!prefix) { + prefix = H5PIntegration.url + '/content/' + contentId; + } } else if (window.H5PEditor !== undefined) { prefix = H5PEditor.filesPath; From c88c049438257b30e61801a940167d8805732ff0 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 9 Sep 2016 09:19:55 +0200 Subject: [PATCH 17/36] Check if contents is set before using h5p/h5p-moodle-plugin#112 --- js/h5p.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index 4d61169..3644d37 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -692,7 +692,9 @@ H5P.getPath = function (path, contentId) { var prefix; if (contentId !== undefined) { // Check for custom override URL - prefix = H5PIntegration.contents['cid-' + contentId].contentUrl; + if (H5PIntegration.contents !== undefined) { + prefix = H5PIntegration.contents['cid-' + contentId].contentUrl; + } if (!prefix) { prefix = H5PIntegration.url + '/content/' + contentId; } From fd34af6964857e8a2f9f49423d6bf1a109576ac6 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 14 Sep 2016 10:44:10 +0200 Subject: [PATCH 18/36] Use content array instead of just id Makes it easier when additional properties are needed. Also, don't try to translate PHP exception messages. h5p/h5p-moodle-plugin#112 --- h5p-default-storage.class.php | 16 ++++++++-------- h5p-file-storage.interface.php | 12 ++++++------ h5p.classes.php | 10 +++++----- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index e019990..9055266 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -53,11 +53,11 @@ class H5PDefaultStorage implements \H5PFileStorage { * * @param string $source * Path on file system to content directory. - * @param int $id - * What makes this content unique. + * @param array $content + * Content properties */ - public function saveContent($source, $id) { - $dest = "{$this->path}/content/{$id}"; + public function saveContent($source, $content) { + $dest = "{$this->path}/content/{$content['id']}"; // Remove any old content \H5PCore::deleteFileTree($dest); @@ -68,11 +68,11 @@ class H5PDefaultStorage implements \H5PFileStorage { /** * Remove content folder. * - * @param int $id - * Content identifier + * @param array $content + * Content properties */ - public function deleteContent($id) { - \H5PCore::deleteFileTree("{$this->path}/content/{$id}"); + public function deleteContent($content) { + \H5PCore::deleteFileTree("{$this->path}/content/{$content['id']}"); } /** diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index a003256..abee27a 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -22,18 +22,18 @@ interface H5PFileStorage { * * @param string $source * Path on file system to content directory. - * @param int $id - * What makes this content unique. + * @param array $content + * Content properties */ - public function saveContent($source, $id); + public function saveContent($source, $content); /** * Remove content folder. * - * @param int $id - * Content identifier + * @param array $content + * Content properties */ - public function deleteContent($id); + public function deleteContent($content); /** * Creates a stored copy of the content folder. diff --git a/h5p.classes.php b/h5p.classes.php index 64c2ab5..103ec09 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1311,15 +1311,15 @@ class H5PStorage { if (isset($options['disable'])) { $content['disable'] = $options['disable']; } - $contentId = $this->h5pC->saveContent($content, $contentMainId); - $this->contentId = $contentId; + $content['id'] = $this->h5pC->saveContent($content, $contentMainId); + $this->contentId = $content['id']; try { // Save content folder contents - $this->h5pC->fs->saveContent($current_path, $contentId); + $this->h5pC->fs->saveContent($current_path, $content); } catch (Exception $e) { - $this->h5pF->setErrorMessage($this->h5pF->t($e->getMessage())); + $this->h5pF->setErrorMessage($e->getMessage()); } // Remove temp content folder @@ -1433,7 +1433,7 @@ class H5PStorage { * @param $content */ public function deletePackage($content) { - $this->h5pC->fs->deleteContent($content['id']); + $this->h5pC->fs->deleteContent($content); $this->h5pC->fs->deleteExport(($content['slug'] ? $content['slug'] . '-' : '') . $content['id'] . '.h5p'); $this->h5pF->deleteContentData($content['id']); } From dcb02810f07eaf5e32b2e85de60db0f9e4dde527 Mon Sep 17 00:00:00 2001 From: Tom Arild Jakobsen Date: Fri, 30 Dec 2016 14:13:05 +0100 Subject: [PATCH 19/36] Change confirmation dialogs button color. Relates to: #HFP-413 --- styles/h5p-confirmation-dialog.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/styles/h5p-confirmation-dialog.css b/styles/h5p-confirmation-dialog.css index 4fd45c0..b902b0a 100644 --- a/styles/h5p-confirmation-dialog.css +++ b/styles/h5p-confirmation-dialog.css @@ -109,6 +109,11 @@ button.h5p-confirmation-dialog-exit:hover { .h5p-core-button.h5p-confirmation-dialog-confirm-button { padding-left: 0.75em; margin-bottom: 0; + background-color: #2579c6; +} + +.h5p-core-button.h5p-confirmation-dialog-confirm-button:hover { + background-color: #1f67a8; } .h5p-core-button.h5p-confirmation-dialog-confirm-button:before { From 2cc6c53da151363d518bd667931eb76b79bb5b10 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 2 Jan 2017 15:30:42 +0100 Subject: [PATCH 20/36] bg colors on h5p core buttons changed [HFP-413] --- styles/h5p-confirmation-dialog.css | 5 ----- styles/h5p-core-button.css | 27 ++++++++++----------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/styles/h5p-confirmation-dialog.css b/styles/h5p-confirmation-dialog.css index b902b0a..4fd45c0 100644 --- a/styles/h5p-confirmation-dialog.css +++ b/styles/h5p-confirmation-dialog.css @@ -109,11 +109,6 @@ button.h5p-confirmation-dialog-exit:hover { .h5p-core-button.h5p-confirmation-dialog-confirm-button { padding-left: 0.75em; margin-bottom: 0; - background-color: #2579c6; -} - -.h5p-core-button.h5p-confirmation-dialog-confirm-button:hover { - background-color: #1f67a8; } .h5p-core-button.h5p-confirmation-dialog-confirm-button:before { diff --git a/styles/h5p-core-button.css b/styles/h5p-core-button.css index eb4e08d..e6511dc 100644 --- a/styles/h5p-core-button.css +++ b/styles/h5p-core-button.css @@ -8,7 +8,7 @@ button.h5p-core-button { padding: 0.5em 1.25em; border-radius: 2em; - background: #488ac9; + background: #2579c6; color: #fff; cursor: pointer; @@ -21,24 +21,19 @@ button.h5p-core-button { text-shadow: none; vertical-align: baseline; text-decoration: none; -} -button.h5p-core-button:hover, -button.h5p-core-button:focus { - background: #3b71a5; - color: #fff; - text-decoration: none; + -webkit-transition: initial; transition: initial; } -button.h5p-core-button:active { - position: relative; - background: #104888; - - -webkit-box-shadow: inset 0 4px 0 #0e407a; - -moz-box-shadow: inset 0 4px 0 #0e407a; - box-shadow: inset 0 4px 0 #0e407a; +button.h5p-core-button:focus { + background: #1f67a8; +} +button.h5p-core-button:hover { + background: rgba(31, 103, 168, 0.83); +} +button.h5p-core-button:active { + background: #104888; } - button.h5p-core-button:before { font-family: 'H5P'; padding-right: 0.15em; @@ -46,7 +41,6 @@ button.h5p-core-button:before { vertical-align: middle; line-height: 0.7; } - button.h5p-core-cancel-button:visited, button.h5p-core-cancel-button:link, button.h5p-core-cancel-button { @@ -58,7 +52,6 @@ button.h5p-core-cancel-button { text-decoration: none; cursor: pointer; } - button.h5p-core-cancel-button:hover, button.h5p-core-cancel-button:focus { background: none; From 738dced2861dc8467e86ffa53843296659818716 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Wed, 4 Jan 2017 16:16:28 +0100 Subject: [PATCH 21/36] Added utility function for figuriong out if event is from child [HFP-436] --- js/h5p-x-api-event.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/js/h5p-x-api-event.js b/js/h5p-x-api-event.js index 212fe0b..cc7fd66 100644 --- a/js/h5p-x-api-event.js +++ b/js/h5p-x-api-event.js @@ -248,6 +248,16 @@ H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) { return xAPIId; }; +/** + * Check if this event is sent from a child (i.e not from grandchild) + * + * @return {Boolean} + */ +H5P.XAPIEvent.prototype.isFromChild = function () { + var parentId = this.getVerifiedStatementValue(['context', 'contextActivities', 'parent', 0, 'id']); + return !parentId || parentId.indexOf('subContentId') === -1; +} + /** * Figure out if a property exists in the statement and return it * From 79fb0e310c9928a48a9e2ddea2320452a41db0e6 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 13 Jan 2017 13:06:22 +0100 Subject: [PATCH 22/36] Change confirm dialog overlay color tint HFP-457 --- styles/h5p-confirmation-dialog.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/h5p-confirmation-dialog.css b/styles/h5p-confirmation-dialog.css index 4fd45c0..47f727a 100644 --- a/styles/h5p-confirmation-dialog.css +++ b/styles/h5p-confirmation-dialog.css @@ -5,7 +5,7 @@ left: 0; top: 0; - background: rgba(28, 34, 41, 0.9); + background: rgba(44, 44, 44, 0.9); opacity: 1; visibility: visible; -webkit-transition: opacity 0.1s, linear 0s, visibility 0s linear 0s; From e02df35bb1752f87bd6fe742c4fcc167299c6155 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 26 Jan 2017 10:37:26 +0100 Subject: [PATCH 23/36] Fix compability with PHP <5.4 Big thanks to andyrandom at drupal.org for providing the fix. --- h5p.classes.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index ac10ebb..0bfd37a 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2512,9 +2512,9 @@ class H5PCore { * @return array */ public function getDisplayOptionsForEdit($disable = NULL) { - $display_options = []; + $display_options = array(); - $current_display_options = $disable === NULL ? [] : $this->getDisplayOptionsAsArray($disable); + $current_display_options = $disable === NULL ? array() : $this->getDisplayOptionsAsArray($disable); if ($this->h5pF->getOption(self::DISPLAY_OPTION_FRAME, TRUE)) { $display_options[self::DISPLAY_OPTION_FRAME] = @@ -3267,9 +3267,11 @@ class H5PContentValidator { if (!in_array($value->library, $semantics->options)) { $message = NULL; // Create an understandable error message: - $machineName = explode(' ', $value->library)[0]; + $machineNameArray = explode(' ', $value->library); + $machineName = $machineNameArray[0]; foreach ($semantics->options as $semanticsLibrary) { - $semanticsMachineName = explode(' ', $semanticsLibrary)[0]; + $semanticsMachineNameArray = explode(' ', $semanticsLibrary); + $semanticsMachineName = $semanticsMachineNameArray[0]; if ($machineName === $semanticsMachineName) { // Using the wrong version of the library in the content $message = $this->h5pF->t('The version of the H5P library %machineName used in this content is not valid. Content contains %contentLibrary, but it should be %semanticsLibrary.', array( From 03271418f09f258df131581a99bb522edc4b3516 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 26 Jan 2017 14:56:28 +0100 Subject: [PATCH 24/36] Use correct variable --- h5p-default-storage.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 1141bda..1fcada2 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -297,7 +297,7 @@ class H5PDefaultStorage implements \H5PFileStorage { // Prepare directory if (empty($contentId)) { // Should be in editor tmp folder - $path = ($this->alteditorpath !== NULL ? $this->alteditorpath : $path . '/editor'); + $path = ($this->alteditorpath !== NULL ? $this->alteditorpath : $this->path . '/editor'); } else { // Should be in content folder From bdec1319ed48f04dc716c4d4d1cbb74e5747e9e0 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 26 Jan 2017 14:56:36 +0100 Subject: [PATCH 25/36] Always return fileId for tracking tmp files --- h5p-default-storage.class.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 1fcada2..696e8de 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -316,6 +316,8 @@ class H5PDefaultStorage implements \H5PFileStorage { else { copy($_FILES['file']['tmp_name'], $path); } + + return $path; } /** From 5dc9e3ddd10d6b6ab6464aebd5192effea02e9af Mon Sep 17 00:00:00 2001 From: Timothy Lim Date: Thu, 26 Jan 2017 15:31:51 +0100 Subject: [PATCH 26/36] HFP-580 Handle language files with hyphens --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 0bfd37a..ff89b5e 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -580,7 +580,7 @@ class H5PValidator { // Schemas used to validate the h5p files private $h5pRequired = array( 'title' => '/^.{1,255}$/', - 'language' => '/^[a-z]{1,5}$/', + 'language' => '/^[/-,a-z]{1,5}$/', 'preloadedDependencies' => array( 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'majorVersion' => '/^[0-9]{1,5}$/', From 6ab68eb6e24f2fa3b7ef6d81e36d42d61f07c328 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 30 Jan 2017 08:43:47 +0100 Subject: [PATCH 27/36] Minor cleaning --- js/h5p-action-bar.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/h5p-action-bar.js b/js/h5p-action-bar.js index 8e34eb7..261082d 100644 --- a/js/h5p-action-bar.js +++ b/js/h5p-action-bar.js @@ -19,6 +19,9 @@ H5P.ActionBar = (function ($, EventDispatcher) { * @param {string} customClass Instead of type class */ var addActionButton = function (type, customClass) { + /** + * Handles selection of action + */ var handler = function () { self.trigger(type); }; @@ -78,8 +81,8 @@ H5P.ActionBar = (function ($, EventDispatcher) { */ self.hasActions = function () { return hasActions; - } - }; + }; + } ActionBar.prototype = Object.create(EventDispatcher.prototype); ActionBar.prototype.constructor = ActionBar; From ef4165a11dc1c1319e803099c2c9fe0cb397d6ad Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 30 Jan 2017 11:38:34 +0100 Subject: [PATCH 28/36] Fixed developer mode for Drupal --- h5p.classes.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 1eea282..9ca296a 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1725,7 +1725,7 @@ class H5PCore { public static $defaultContentWhitelist = 'json png jpg jpeg gif bmp tif tiff svg eot ttf woff woff2 otf webm mp4 ogg mp3 txt pdf rtf doc docx xls xlsx ppt pptx odt ods odp xml csv diff patch swf md textile'; public static $defaultLibraryWhitelistExtras = 'js css'; - public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $fs, $development_mode, $h5pD, $disableFileCheck; + public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $fs, $h5pD, $disableFileCheck; const SECONDS_IN_WEEK = 604800; private $exportEnabled; @@ -1761,23 +1761,17 @@ class H5PCore { * @param string $url To file storage directory. * @param string $language code. Defaults to english. * @param boolean $export enabled? - * @param int $development_mode mode. */ - public function __construct(H5PFrameworkInterface $H5PFramework, $path, $url, $language = 'en', $export = FALSE, $development_mode = H5PDevelopment::MODE_NONE) { + public function __construct(H5PFrameworkInterface $H5PFramework, $path, $url, $language = 'en', $export = FALSE) { $this->h5pF = $H5PFramework; $this->fs = ($path instanceof \H5PFileStorage ? $path : new \H5PDefaultStorage($path)); $this->url = $url; $this->exportEnabled = $export; - $this->development_mode = $development_mode; $this->aggregateAssets = FALSE; // Off by default.. for now - if ($development_mode & H5PDevelopment::MODE_LIBRARY) { - $this->h5pD = new H5PDevelopment($this->h5pF, $path . '/', $language); - } - $this->detectSiteType(); $this->fullPluginPath = preg_replace('/\/[^\/]+[\/]?$/', '' , dirname(__FILE__)); @@ -1785,6 +1779,8 @@ class H5PCore { $this->relativePathRegExp = '/^((\.\.\/){1,2})(.*content\/)?(\d+|editor)\/(.+)$/'; } + + /** * Save content and clear cache. * @@ -1827,7 +1823,7 @@ class H5PCore { unset($content['libraryId'], $content['libraryName'], $content['libraryEmbedTypes'], $content['libraryFullscreen']); // // TODO: Move to filterParameters? -// if ($this->development_mode & H5PDevelopment::MODE_CONTENT) { +// if (isset($this->h5pD)) { // // TODO: Remove Drupal specific stuff // $json_content_path = file_create_path(file_directory_path() . '/' . variable_get('h5p_default_path', 'h5p') . '/content/' . $id . '/content.json'); // if (file_exists($json_content_path) === TRUE) { @@ -1937,7 +1933,7 @@ class H5PCore { public function loadContentDependencies($id, $type = NULL) { $dependencies = $this->h5pF->loadContentDependencies($id, $type); - if ($this->development_mode & H5PDevelopment::MODE_LIBRARY) { + if (isset($this->h5pD)) { $developmentLibraries = $this->h5pD->getLibraries(); foreach ($dependencies as $key => $dependency) { @@ -2086,7 +2082,7 @@ class H5PCore { */ public function loadLibrarySemantics($name, $majorVersion, $minorVersion) { $semantics = NULL; - if ($this->development_mode & H5PDevelopment::MODE_LIBRARY) { + if (isset($this->h5pD)) { // Try to load from dev lib $semantics = $this->h5pD->getSemantics($name, $majorVersion, $minorVersion); } @@ -2114,7 +2110,7 @@ class H5PCore { */ public function loadLibrary($name, $majorVersion, $minorVersion) { $library = NULL; - if ($this->development_mode & H5PDevelopment::MODE_LIBRARY) { + if (isset($this->h5pD)) { // Try to load from dev $library = $this->h5pD->getLibrary($name, $majorVersion, $minorVersion); if ($library !== NULL) { From 3b9dd956838713ebbfe8d29c7b3330d1ca774ed9 Mon Sep 17 00:00:00 2001 From: Timothy Lim Date: Mon, 30 Jan 2017 14:49:09 +0100 Subject: [PATCH 29/36] HFP-580 Update regex --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 9ca296a..97ee2de 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -580,7 +580,7 @@ class H5PValidator { // Schemas used to validate the h5p files private $h5pRequired = array( 'title' => '/^.{1,255}$/', - 'language' => '/^[/-,a-z]{1,5}$/', + 'language' => '/^[-a-zA-Z]{1,10}$/', 'preloadedDependencies' => array( 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'majorVersion' => '/^[0-9]{1,5}$/', From e548b3575f0396440f42453abb11dd6f0a95f1cc Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 31 Jan 2017 13:35:23 +0100 Subject: [PATCH 30/36] Add custom xAPI events for action toolbar buttons --- js/h5p-x-api-event.js | 7 ++++++- js/h5p.js | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/js/h5p-x-api-event.js b/js/h5p-x-api-event.js index cc7fd66..e5d6368 100644 --- a/js/h5p-x-api-event.js +++ b/js/h5p-x-api-event.js @@ -308,5 +308,10 @@ H5P.XAPIEvent.allowedXAPIVerbs = [ 'shared', 'suspended', 'terminated', - 'voided' + 'voided', + + // Custom verbs used for action toolbar below content + 'downloaded', + 'accessed-embed', + 'accessed-copyright' ]; diff --git a/js/h5p.js b/js/h5p.js index e4d0138..88a9666 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -158,16 +158,19 @@ H5P.init = function (target) { actionBar.on('download', function () { window.location.href = contentData.exportUrl; + instance.triggerXAPI('downloaded'); }); actionBar.on('copyrights', function () { var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $container); dialog.open(); + instance.triggerXAPI('accessed-copyright'); }); actionBar.on('embed', function () { H5P.openEmbedDialog($actions, contentData.embedCode, contentData.resizeCode, { width: $element.width(), height: $element.height() }); + instance.triggerXAPI('accessed-embed'); }); if (actionBar.hasActions()) { From 26d0a3a341115bf74ec6f68fb6715a654f51d1ad Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 6 Feb 2017 14:39:59 +0100 Subject: [PATCH 31/36] Double check that contents settings exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … before using them. HFP-714 --- js/h5p.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index e4d0138..6a28573 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -661,7 +661,8 @@ H5P.getPath = function (path, contentId) { var prefix; if (contentId !== undefined) { // Check for custom override URL - if (H5PIntegration.contents !== undefined) { + if (H5PIntegration.contents !== undefined && + H5PIntegration.contents['cid-' + contentId]) { prefix = H5PIntegration.contents['cid-' + contentId].contentUrl; } if (!prefix) { From ea3b86dcfe08fd7c24463863d16e3e6f07b3e04b Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 7 Feb 2017 12:55:53 +0100 Subject: [PATCH 32/36] Development mode must be set by default HFP-724 --- h5p.classes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/h5p.classes.php b/h5p.classes.php index 97ee2de..b0ad889 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1769,6 +1769,7 @@ class H5PCore { $this->url = $url; $this->exportEnabled = $export; + $this->development_mode = H5PDevelopment::MODE_NONE; $this->aggregateAssets = FALSE; // Off by default.. for now From bb71ea20a36a9e31237c180e953abfac0997810d Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 7 Feb 2017 13:48:42 +0100 Subject: [PATCH 33/36] Update JSDoc --- js/h5p-action-bar.js | 12 ++++++++++-- js/h5p-content-type.js | 13 +++++++++---- js/h5p.js | 12 +++++++++--- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/js/h5p-action-bar.js b/js/h5p-action-bar.js index 261082d..9a0f441 100644 --- a/js/h5p-action-bar.js +++ b/js/h5p-action-bar.js @@ -1,9 +1,19 @@ +/** + * @class + * @augments H5P.EventDispatcher + * @param {Object} displayOptions + * @param {boolean} displayOptions.export Triggers the display of the 'Download' button + * @param {boolean} displayOptions.copyright Triggers the display of the 'Copyright' button + * @param {boolean} displayOptions.embed Triggers the display of the 'Embed' button + * @param {boolean} displayOptions.icon Triggers the display of the 'H5P icon' link + */ H5P.ActionBar = (function ($, EventDispatcher) { "use strict"; function ActionBar(displayOptions) { EventDispatcher.call(this); + /** @alias H5P.ActionBar# */ var self = this; var hasActions = false; @@ -66,7 +76,6 @@ H5P.ActionBar = (function ($, EventDispatcher) { /** * Returns a reference to the dom element * - * @method getDOMElement * @return {H5P.jQuery} */ self.getDOMElement = function () { @@ -76,7 +85,6 @@ H5P.ActionBar = (function ($, EventDispatcher) { /** * Does the actionbar contain actions? * - * @method hasActions * @return {Boolean} */ self.hasActions = function () { diff --git a/js/h5p-content-type.js b/js/h5p-content-type.js index afe4136..8be8fcd 100644 --- a/js/h5p-content-type.js +++ b/js/h5p-content-type.js @@ -3,10 +3,17 @@ * * Functions here may be overridable by the libraries. In special cases, * it is also possible to override H5P.ContentType on a global level. - * */ + * + * NOTE that this doesn't actually 'extend' the event dispatcher but instead + * it creates a single instance which all content types shares as their base + * prototype. (in some cases this may be the root of strange event behavior) + * + * @class + * @augments H5P.EventDispatcher + */ H5P.ContentType = function (isRootLibrary, library) { - function ContentType() {}; + function ContentType() {} // Inherit from EventDispatcher. ContentType.prototype = new H5P.EventDispatcher(); @@ -15,7 +22,6 @@ H5P.ContentType = function (isRootLibrary, library) { * Is library standalone or not? Not beeing standalone, means it is * included in another library * - * @method isStandalone * @return {Boolean} */ ContentType.prototype.isRoot = function () { @@ -24,7 +30,6 @@ H5P.ContentType = function (isRootLibrary, library) { /** * Returns the file path of a file in the current library - * @method getLibraryFilePath * @param {string} filePath The path to the file relative to the library folder * @return {string} The full path to the file */ diff --git a/js/h5p.js b/js/h5p.js index 08a6586..36e2709 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -438,7 +438,6 @@ H5P.communicator = (function () { /** * Enter semi fullscreen for the given H5P instance * - * @method semiFullScreen * @param {H5P.jQuery} $element Content container. * @param {Object} instance * @param {function} exitCallback Callback function called when user exits fullscreen. @@ -896,6 +895,7 @@ H5P.t = function (key, vars, ns) { * Which DOM element the dialog should be inserted after. */ H5P.Dialog = function (name, title, content, $element) { + /** @alias H5P.Dialog# */ var self = this; var $dialog = H5P.jQuery('
\
\ @@ -924,7 +924,10 @@ H5P.Dialog = function (name, title, content, $element) { .end() .end(); - this.open = function () { + /** + * Opens the dialog. + */ + self.open = function () { setTimeout(function () { $dialog.addClass('h5p-open'); // Fade in // Triggering an event, in case something has to be done after dialog has been opened. @@ -932,7 +935,10 @@ H5P.Dialog = function (name, title, content, $element) { }, 1); }; - this.close = function () { + /** + * Closes the dialog. + */ + self.close = function () { $dialog.removeClass('h5p-open'); // Fade out setTimeout(function () { $dialog.remove(); From e66e6c5d76fb87435195cda7ce78ca001e09e506 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 8 Feb 2017 10:57:16 +0100 Subject: [PATCH 34/36] Only clone content folder if it exists HFP-731 --- h5p-default-storage.class.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 696e8de..0c67341 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -85,7 +85,9 @@ class H5PDefaultStorage implements \H5PFileStorage { */ public function cloneContent($id, $newId) { $path = $this->path . '/content/'; - self::copyFileTree($path . $id, $path . $newId); + if (file_exists($path . $id)) { + self::copyFileTree($path . $id, $path . $newId); + } } /** From 3e9277c0f85a431014262f520101db9891d6e637 Mon Sep 17 00:00:00 2001 From: Andreas Nergaard Date: Thu, 9 Feb 2017 12:45:51 +0100 Subject: [PATCH 35/36] HFP-750 Updated h5p core font from 16 to 17. --- fonts/h5p-core-16.eot | Bin 8096 -> 0 bytes fonts/h5p-core-16.svg | 58 ----------------------------------------- fonts/h5p-core-16.ttf | Bin 7932 -> 0 bytes fonts/h5p-core-16.woff | Bin 8008 -> 0 bytes fonts/h5p-core-17.eot | Bin 0 -> 6188 bytes fonts/h5p-core-17.svg | 50 +++++++++++++++++++++++++++++++++++ fonts/h5p-core-17.ttf | Bin 0 -> 6040 bytes fonts/h5p-core-17.woff | Bin 0 -> 6116 bytes styles/h5p.css | 10 +++---- 9 files changed, 55 insertions(+), 63 deletions(-) delete mode 100644 fonts/h5p-core-16.eot delete mode 100644 fonts/h5p-core-16.svg delete mode 100644 fonts/h5p-core-16.ttf delete mode 100644 fonts/h5p-core-16.woff create mode 100755 fonts/h5p-core-17.eot create mode 100755 fonts/h5p-core-17.svg create mode 100755 fonts/h5p-core-17.ttf create mode 100755 fonts/h5p-core-17.woff mode change 100644 => 100755 styles/h5p.css diff --git a/fonts/h5p-core-16.eot b/fonts/h5p-core-16.eot deleted file mode 100644 index 239079bac7fcb443abd2063703ed61bab0a9b3b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8096 zcma)B3vgU#b^agsxqJ8S-hEwZpVF?hYdySL?P_-u+1A6dCC8TPmrNWSJ62@bvK?E- z53t=NBp6~RW{e?Dz%7tW6DZJ@fhmwol4e}Uq-7|SrnC&5rXB1;9W%5H6dJ%<_xtbe z+OlJY-rfKH=bZnX^S{qI|9_uN5^^;`2qlc%{7`b13d;Dpo9TkL_Rx=RJaB6nAtu>P zrpX-Hhcrj_gTW*l$PC#$Aj}T7s zYd7{3ynf|0D6b%GpWZih;JZKnAVg^l_y>1Q9TIKw0e=>`w(EgoJExvmBLn{&A(6@1 znW-Jd+J{~yBzhI)%Rp)i8>xTFn8!M+KDIeucDlAX(K&LP(M4*{tlFn)N=Y0 zQlz@zTkudc`{rkekhe^J;o1c<#F0?g7<|FkB3@2yqQ)4?YB_hfVniadpocLUoG75R z{AlNoKJsIF`H9OPzI^U2^VV{-Wv=x5#{4xL`R6nQ3cG@{ubuphM_ zu|v?Yux>}N{-T6JlXpQm7E|R;hy8}k@1tFzG7qz;ScrjEdWCgYudrzK3hJQrA@T$} z!JZan?G{j~Ji(0WZ=5nhn>k^d#2D*T&8k_3hAe&UwWVy+H7AEW{@__Ac&M$@ip^eI zdX0!;whVyahc1JjAYt|p--EuYSaDtwAU27TBuSGj=^{OlE{4(-B=!H9(-UK3fBF0V ze9zd}*d~!yty(pSUgv^}fZG)rGUGWkuqY?7WnvI+SnbnfHioBT<3 z0qrC-WwKB(!R%5xl^LYvLXw7+E*cu(rM?_XvvYl0S7sCC)!ldNlKhzLRX!w3`rX~D z%gIG6x0cuiy!&z+moL`8<5E7Os-IC@-_aK@-@&P1C~qiiH6t zY7iq+3ZzTL5>lubVowW6p|y0c$nV`cIr;uC77C9oVy^-#)u~S2yL@@|L&ax`Zxjim zC&>%^Q|#L$K{_$(*#YXbzuvoIp&gKcetlX>LrcIc1<4nx{7H7a;UrwDP; zIQc8dVBa7Hc2JruCjDduSx>f*?J!-0mNPvxrG)GR9i(c#B|TIQm(iq5S~Y2sW>qDK z!AB!iC7h&%au3Z>FRf%VDK8a8?HMJc2E)b50PUf6rQFv;MG?1JtyYR;(VuZ99gt-M zTVr<3FxMrU^I`e}E>kyEqyrM;vnz%|Ar~mEdBE;tRi;P4=IsG*&}2Txr5w` z(aTY+$h0~@E9EdI4x@;7ZKGHEI6%eOrBc~cMTx~0W?{FYzW^{7wvsAopoN`A|tKHrquWTmr~Eh~zmR{zD< ztnSL~IDYi#@uxht?(!;%F6~mAeW7GB#HPHyiDi3c(J`X<6um{tWDofd>c&Wf z>)pm?&Ek*FWKwC2f>MLUK;UmXBYhuOwb zELUoyRo4P3=kK6SeL1Hx%!WfRqOzrRZP;O=;)VzSNM-F{zBe2c6=N?rZ@{9C>pg%9 zo7bcYc^l+Tnnrm>@zS894kX}}eA$*%%BU}6yD(pdLOCt$EK-U3+XI1&-(|{N(^`j; zu8^B+JUN)85)bEX#%WbEBW@Sfydh1)R@b@P$0bPxAmA34y(nll8H!4|j5XlNxS{&# zn5RwSR@%a>V!vQg+_oEk#izNwGTPBHoMaMZCfd|8(gFkQ2{YEdx*eNLLj7D}(NaYB zsiJ{?H47!t5Mye(8T!n=2v;T9t%N##s#jCoD*bFzuBo}VnK874GS<2>#W-K5%1mO4 zTQMwpkEPSUV2oKoQ9J&*EjcRdvV>yGrTLi4Hn@bA8@55IVk%(ydrdZ6O+M~2Jk18> zrrQ-zWlhH4#oXaSv(;j$UYFvQRj=xieHgIs#SO{lbX%3Mo6!c*?XaOjv*E^Awhp#% zRaRvQZ9u>A4tTiec!tU7H!78w$|9wx=GD;;-s(qVJc=F1_GpSjL32pQxXC(m>0VT; z7?9C4pDOs#Fn8ERDSb)xD5zfXsNO{2pA2|)*!z#euYAn$D|g)RVhyhn2nS&^Q$5%i zcxCzqv4rpv-mT)eI27A8-p z?Qn}HDOJi)kV4T7PZ9{VmRe<`5M9BUJSQvEkR``Aa9fgwna$xX6cjM2?izGyc#YIx z<+H1Yuq%WcnZGPGSo!S8ums-%*RYoEt3FAGsz0RxR=J5mxEbggzg@M}*WqPq)xskU z)pfx))LN+2yq9R<5FL8^?Qebh)0lG1`5`R9ud#;_yWWX8PtZ6GD#A-HqkY1Wh6P^t zmBL5X3X*k36l7|QY%`Z-{-%WIO#z*kr1R9zsdWApI$h6nX^F%U!01vzaLRIt~9@m&|zNUDkamgdA znyShkfL`STs;;Z7NzrxXMD(^!t?a4eTK&hPxhnT#rqz$1aU1i~Z+bRn4nG^?|*35*cSX1U## zs`+)78^()sN}r7S{gDU~=ax6!wzZ;rl+pd8k|t-icr>r0f>T$pxMp!M8;BGlm2>1o>-3tDQW(6!EHvONDdAKi`LSQ1`xl zSgd5x?p{St+ihx2pgd46n6k8BHVbKAFcG^#iKpKC+ zR1=6%8!oukD7?{@zH+Ty;?3Ty%+1YJs7v=Oib*Pm!4IcAc978;opITn%Wy@46>nK= z>cX01Tk0-P7n}tgphpcST$;wZ@7}z*n^L#S)0sp8Y`+vqyTRYRdGp=f)t?l17CAh6 z&~W=Xyo}G=(yr3M2M3v^xZ**Ng&CsM>rqWb_bR6;XRe4V(I3kWN4SDA#A2P1?x{Xl zWWdp-(ejoQ^ikHlva`7yGt=HKS(oDFNc)`#sF~61!JK1mt=_nuZ&FQFHPl!sk}i4C zXivlg&&4Ee6A#3W6k&q3M~)by|qZrs!C3AMPwD>~tyeQ~onYibdTYmyoB1Y&O4 zt!g1d*fUDVwF?r#NnM=7f=as83gYO6ZEm%u98*CPzWBunwA^!w}?U^T9Yh=#0|3|-47IVL`ShP|q^=*Z`T3JKbg^TA zohM;gj;8sO4NVGX#{|r`d()v{n^$P{LRVTq)k|;faP+|^6XV*i`Ty|u;);$aaDB^#sG$A2@Q!u=Q5o5gGxIJ zWF&E>#Q@dMBr5x(`HQ*b%X2J|TTX{_^A}+f{45cOME&y@*{jpjuj4=F3hFl2)ENDB z$n-Dont#v82zzNcp6!C_6jNomhn^Lxst0<8+2W|bdLR&q1n60R^y=#)cqBeZ*@!YE zHJg;SFPWa^)6>(786Dx$pjZ!_JbLXLe44+2YqTxoQS=z|jj3(C)8c3(e3L^91#uu1 zeJsGuicW$``vzFCkig}EqQX?%eBK6H$?;OFxI%D_;9(mUsx-hG5<&QCh+q|Hk{=So`N4kYA=B+>Th{O60C-#aq$-p$3u8N=4Ii~DCDn;3s= zM}MqzSGTL#>|Rq!uu69Lo1^yCU#_j3QzKiFC< zY+jzpEZ*rOAzisGHpqkR9Z6npgBipu(&<~GnyCi!+lyu`^w;yq~EMMGOFS_r(k*ybH zSTxD+p8qubrR&S#hQ8)Ls05;sE7O`!tVqOmiFBQOVS;v`^TO z(9#H24?M*d$OcUPtma{wDnaxfNtdkc2FD{r4H=@$(5lJYhGpsWvJJVOTHeE=ek$g|6yRx)(%UA}){m=CnOvII#Y|MIJsy9okvoD@nx9-%7v%6n-eC^uDU)Vi) zZf(`%t zX-_?;caI)_e(TofkB^NVe-8N{j#dX(J$zreeBZ;XL|)nU@IQ1g7O_$LDoN5OvFZ zqi|`o*#&3D1K~5cg_7yNgNt5LWTwjh$hyXN-g)QDco)@vg?YgLe^nMef466Ow`Tjy zX6+>=6yWlJy`+ULUqsG~$GSMwDE$0+t{}3i{>EMuxBQ7kp_3B`u-L>&%;h%nS^fpi z@JS|)`^+t3E)~_PSu757(h8Kiyu$Mt7>?fByXHq!#{_}eMFTal z+B=|R*pn?wnqzmglqP%gEvr)1OKq!C`QA;XRDZmAAo;x&rNh(f*W9x)HnwtEVC2!< ziroi$CJ(a@wyj>YXmvC(wkR{+X6Kvvl8)^?nTeK`iA?Uk&Vgtz`j3n(b-} z!~GlV`f0L;AK@k3XCr32c_s5B{X0)hOq>!+ua?sb3o%fI@5TK^J?;x&pA&h(UI;-W zzhyt9u>oE_y>8v=|(=Zuuw4Ziwlbdo2ZvQOMb?7vp++uvktAV z-Jy!5ip42T#<1i|sQ3=B5O?CixZ|Y4!4SW_ki*1w!zyd?GqNJdShfH|A;7{413n6n zNe&^41G6TtcL)nW+MSiKo+XfUDJvwUqu1fO#;<^g#lL6i4aG~Z3pw*6MwlGf_)&VF zcj+&Q8wLr__l|t>nHxDqe-u$SzW0f_a2~|s&UZ5+cL-b>718-kPg_k>jV8o^Uy%rX z2nn#0PJlKqchHX`eFEtuQXCq8h&+WP=!=jJAss{dKKQ>3*;b(&Nwn5LdbAF^kSj={ zERM7d>2wXJ&w?!C*dV@mbYpjp<4eOA@RjrDG(&eXgPoNU(qXwtJ|O?I;!_?}A5p)i z4QQ|G`}NPcPPwkQ-|hac@owWm<3$g?mpHv`qXurM#M2lR2dTeI^2Pd?P<-vGNeE6A z_14Bpd<84lVF~b&IxIW)A2l8Y^fh(5i$rk##dktU2gO854HouhR~_bnm(^hj@Pl<& z##fe>I;?=cp$@ySwx8cUJ-2UeZvV|+mftmV@X+qL{Vlz@{Ovy{-&X3j#0@jMjyy1R z@aFH-x8)q2+kd!a*UbKzgHwlRcC_4otmUrhxe2r_hm(f*lzkYz*o7}d`<)NmQ^04) c4$$r=$MC+(`7}OJ|1_S%sPlzW@=o~w0Pj)0SO5S3 diff --git a/fonts/h5p-core-16.svg b/fonts/h5p-core-16.svg deleted file mode 100644 index d0bdb88..0000000 --- a/fonts/h5p-core-16.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - -{ - "fontFamily": "h5p", - "description": "Font generated by IcoMoon.", - "majorVersion": 1, - "minorVersion": 1, - "version": "Version 1.1", - "fontId": "h5p", - "psName": "h5p", - "subFamily": "Regular", - "fullName": "h5p" -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fonts/h5p-core-16.ttf b/fonts/h5p-core-16.ttf deleted file mode 100644 index daf157117573b9cc4baba149f16cb136104f217b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7932 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%0DU=~7luN=L83 zb&g*~B^Ljl<<}W6zeeQDkI*nVu;~x!-QMLtCvF%dJl{R?@u#on(Eb>rZv5XT=E8Xp zi#z|D5xGN@r7;nm-|)26IMwO|8Om2ALhnZc?4%Q@TaY{GQ%D~}I*Akq#~&b1A&L4W zNQaP)A$v!!G0sl4y$~Z9_U;E7MP*TEwwI{NvG$-8q4O8a|JI zIe$tsbSE>|St%hMmYd`Q@;@s+x%o`?(Z1yHtsW?_u&5$ zXS8k9zzvmn8d`A*^^ZxuP}d2?zg;y7fvKY3ny$pZVC6b20X|ZPW#|5*hNGZi5}QDIStMiq z{m;*X2-<_d+eFed*__{hHNxepsXnYxQ3rTsV9L{atpAj|1Y;W_pgugs=`lalTG1fMO^Ua139;6(bUvM;<0VP!!NwUfcEB zhkrm{fAsYay?*`;^XlT&t0;$_*XCY3`vda&Y0%HUK^!{0dO7lBs@;bHFiWni4Tb-s}Z}0D;U7<1$vnT;ypp{->-PJ2BTD<~=l-^GsWhdDa zqO9ElN|i^MQT>%uMrbo9Y>OCUovK+i%g~UOFTJ#qZMo{?kjD$2Rf31wI<463rIlBS zC}yhw2!7}?=t&Z0_w&8ztBO_YB>`fSC`pnu$&xP81Lz~b`k9) zG-a|-Fv09nI+YouyrGi>{UJ}OZx5IYs<-H zYqpixMSS~mn@5)G-*PFRR@F}{u5anfM>gjmCRR45A0uDo=h=%Go2I4p6$=AQ)F4Kt z6iAnfC8SU>#GVk6LTl+Rk>9m#dis5zFBBeL#$E(gs#Bf3cVwjc!Qzv}SBiwuQ{)-` z3HD8rAf1@?>;QGzU+-P9P_EeF0{YjNZBJ!NedS^y911G@6NiF!+u)X-{FZ_C_JJ+= z_BFA?l3PkiQuUQJ_uR7v=|_j+t26oO!M3)+={)jwJ9JnAhaqj6852BXGlaNkocy(9 zu&)sVJ19+-lYX+AY$SJ*9b^upT+Z~+loGNNbdaj`mh@0LTt<^JY1O1jnpKq`1|N-7 zm2i?4$~`nky|j|eq`XuVwP%!&8VnaJ1GIoMK z!(5ka&WGvuxJ=ztkq%0X&#xZl+~zJmyiV{+3QCqTSF>C(WceCv6W96hY4$&h7W?Yr zKK3}f6UvO=qU-JT{`v+^M^(Mnw{z0j#a}U{nx%hHg;mCG`aUJn7<-T`Be#&dFnT$P z6`58CXr&y+#9&zgEz*DWpHA4S2Zdc!tU7H!78w$|9wx=GD;;-s(qVJc=F1_GpSjL32pQxXC(m>0VT;7?9C4 zpDOs#Fn8ERDSb}$D5zfXsNO{2pAC3**!z#duYAPuE4N(tVhyhn2nS&^Q$5%i_+E_6)^->{6lAj$0c-{KpTaM2FK+V^FC!`dt1LB|nXxAptyW;TbnP*A|6x@*v-;dN4jmCvso z!mbc*Wbt*W!OG`Hhb8zHxQ6v~fAujsRQ(YRu*%yQgqwk`$(vPMeHmV+RxLczP+b>% zL#>5M&3lO!4$+}E-~7fWKZz;FoFB##{0e&jvFfdu^8}64pd!5FD%vL;X;|QOUnzWK ztsq%vL_wy;$hLA>=C4Y4UKP*^msUs|0gNscBo}*vao{@JhgCg4 z&lJ8FeOw_i_BiqaM(R9cazAnrNlWxN`3C<2dm6D}h^!!p&z$LT;t)7}c%Lp1;?z!| z)i7*%1!lcB=d`?50&m}1D5KTjl$XRU^cOB!xu96e$5ch%>2Zzg=1YoKnv^`Us;R2% z0q9lUuj;zWniO4EPFDZK@c9fR=60(uXxa;^+jYienyxdCsPZ-l;PC)}7dk5h&Z_!h z%WYVe;kM8hO211v-^A{BVz_Iu$FpQg`(kg?9Jt=;%tin52yFAD1+qh~;8E!HIWG$S!sHrePRnO^+0tG|TO_RL!ru z+%R68Q~Frc?~g=~IJdm%wrv&Nql_IGlQcPVr$_TTDmZloi|dvLvw=t80D2}KPpPy`g(A>44qq1ck(35yOWw9|fC1k!i~Q%xX7 zZMfiCqwqyr`pUI-i7$J#vaqmFp)TFCEGDTO20xtg*g-~XbjD?OF2fZGR=j1osS9h4 zZK=CFU2qm~fF3oRaA_LrzJ2S~Zc5!QPiGPZu>Ddb?FN7M)~&a9SASUCRpjvKLBs9m z@G?GcOS?)3?;B*A;)(}77G{W2uSYc%-K(6ToVg;dM1L$h9N`Me5Q}w2x@Y=ekpV|n zM$31mppUZVHJ#1nn3?u=$+{FTN7`>iK+TM159SETJe!cN4)wmxdSz#HcWULbzv$?s0j-X=0@zVP_`AJ zvq?ji6LkqAghKEL*0kJ+zeN-Z(VAo-OyO-RHnsUrJ%$nYzje_{7=S#0`LGrDxINQv zU6f=*NBAK=7)DqmV5aiLkDFevX|{zzX#fD*f}u7xw3zOp~skSOl{+v7DprDn;cpwhy$tUV*zef zbP`nBH^7R81TGI06{h0t=WU>s951zsD+K2V9=2hjN&~zh5rnUX2v&h6=`pWnDlY6Q znRg}=U0T2u@6y!$uB7$NKthg45^awzezv&v-J_%L-dbFqF>F1%ynpWDsmX_T_Qy)M zb-S9)?scUEt7M1oC=~7(&hEIaJVukHiH?r(ax&ILyGt}gsexUsyXjz}yEFN5S?)?e z(MLQ!+rQV_>Klk;X|K>P>KRz*V!E1t&*qsUu zvbC`BeRe)PJ0R*vmd87?t0ORhJ*l}B7*6cB3H~OxNe1@4w$W0!1HfpB4cAy(abfK1 z=T(cpY3NX(n$qR%qtzp$+qaL>4~=fWEPFtdbmIZHA91!UU)okLy8G_YZI@(NG|BH? z{3QIP>+0WOkAImwO#T>6z{nXUd)|{j6{4SuL>s~Yzv%y+{6`eqDqfbyq(nnyaWxee`@Wa?JhCu~S)Wdy4S zp5ji(22A~&=3$yDLG%tum#iHI$0I}y8KTV4s>$4@Rq6DqO}U<0-ov7PE9tYlY@E>~ zjm7(Ij%aU#`#Vk>^CY4ciY~$&riMTInAVwYZ=Wga3j*PlCAl5E0L{ z+pF4LS=zc`ECb^H=UNOV;z~?5X1y=fo2AwHXHRd~aQfN#J?xVv1w`+*6OSGGU!kKI$d`ulfHbvJT;aft#)QI$Z$ zSY4CItB`~I_DUnKetM|}XoB5jBu;MOZ{ePCfNVzJDvMsY-80gy**>#bdyWYO zxIAFbX<^G3ku#IAE)F#czi@#oh^(r=vX{jze_~na)D!|NHgyVfxt)B5e~vR^qSyWK z6ONa#Y8H#boQNQS*H8;+mWRveNG~pD61>9m85oY<+PCIMRL2B?+C>93vD!DFWY}XZ zE1F}sw3McM^DPsp>gBeHRK9mhDb*iu9!P#?b?M0L#&vgWj*YKb6&QUew|dW^p6Mg( z18r-UEn6E+j4#Vfw%Pe+zM^ACPiCs6Wh#@qyK^Ag%f4b+)$@ig8lopn%k)+s2t`dV z-5u~-i&w1M7#|qpG?6fUEWC=bbSIN4)s$sLell$;^! z_)%WMeKumYx36S=w13y>sj1Uq>D6+2aVZ9>@V&UdsK~kV7*$W|Puf;lYj>z(sbX=8 zlQAs$3M&2wSc*IGVBB$1;b4g0T*zT!dtj9{`AJ!kWGq{Np%7qUg#jM}$Rvl5#erFq zH#&qRAnnddSkDqjx|9`?(y?oBUE`NP#Nyw(^19-c*Myw;5hF|vZvI1hw|C{wi5mt9 z&v%Y~{K@M%Mt=-ZxA;#`+$D(zvAFZU8Id~#D%Jmu6mb1%t7)oXLJas7iO~Cz06Xa< zXp3?O{V3AMkWL}Rq4E33Q%Hio4Cye^ais5p|BH}q6}pi`YYn7_>aYvBf+Wh~NZXOl z)NuMKkVPCD#6KS0*qxL3r{Qzt1@aS`p}UyD&PfUBh}bHXDfg)ls$bCtv={XQ z`k%Q@yRNw3>HfCyPUAk~Sr7g%5&b7^)W8jucp9VPAoY()KJRb|UKKPUAvjgkTZ6^F zVC6b20X|xXW#|5*#-o6~u1Eb!j@!gOU93w7~7~AOUNMP#1ARL zE@r@q!NhIABzOoVJQ^}?3QS?*WGFNJgJ}uSc9>?SQ>Zg7O!`mC!%)ghGwJk!*ZrNl zvMorad&FHW)I$V`xBXS-$eb#gv2)OpP9Zb zII|Hg#{UcS+I}F+`}r>jNerUiv;WYMW49Cc2vVan(1ZHxs1F^QJ~mJK zX;hCLDRp@I(9G$74Wd0ag+6xA&)snZ<3#d~X*%Kj-{`|c62cy5KLh4F&60jZ%2a8- zC5d9#pG~8LTu}U#ORtbojtrvK#pRUp0o|LmwwKU^E_s2!t%l-Mm*9+x=4oP$Z|3O>2e}dM>c=QlAatN zfANbxo*y3{-=*`WO`CQi`!MQye&ixK%Q>s~W6zl$&C$O(y7LXXb|>9(bQj89;Q5q1 z%l?J7R@&o!3Q#4yhlIMlQ1mhl|xTD5KE-T)U5il8YU zG>yRC+OAqGs0P;Gu#&x)zi!{Efn6I0tn)$h1(OQ#f*Cv?T)S(wR@hQcL7yCbKEnk& zM-rr!V4vuI6X>^G8fMjErNDApN3l$`Tt!ZlO-5={WBvVOQ?<)weq!Rjm&)Y_2P8Y! z#ED_mG9r}lq#Ws^A4?jv#38{7BSLsU zkMyO5l!t`r80isdNl%E;#*m;`?L&h0jYwd?bw7Kn@8U^zoQ(?B;tSB(5ua$u)#*NI zh(o%ojNg+$tBv=;Xo~%UrZ4xj`8T-eM8l@PYEgW;FeP$F0&M6+AwqVOd?awIN(f_eM^8b zThd6t~y_p^T@b%etAX&9Plr$tOV zZDjH^GnlPn9)oF(F|vA2*;;v+LKjd#5r#fkDxx|>i(YR{YsAFgHB9JG?*dQu!d7TS25mbkG^?JE?UvnWM^v>a85SyNg$&yg zf8S{vwpNnl0t00N!kCj{BA_Jg8qENys9<_>lDg=rXPd?eiwTIdRg;)FQpyow;k5(n zby3k4RR;RhV+krJtQf=eEeC5LRV?%u7W@Rd3(HW4dtTWBQTcK#h?CWyiv$P+nV1`g zV@tyg>R+}3$ESa_QWs1UKTY-+sZWjd!TndOQ{(-8V^j5q;C*FSZQm{;s$0sP+l5Gr zh_!jKzI{78mmiSJsPEd|Z0ow~wLc1wbL7YT4)z21 zaF?XX3XEF&@IB-favM$`OPzB43%qhnJA4@Z@96MjiSBB*ME)6k?LoK{IlEz4S|SS9 z%9!uN4@!E)hi}oYUYIS$?8d7OxZ8KYaN;Yq1BUQO-vYh4@k2V$_-|^mwD03@m)egn zh63L~>t^7x5irnK<4wUF6C+?(x%_~EF|`hz!f@{PW#x8$lvi4d&-Z`YsqG9 zk47`xVyU`41c>FpbkH(-`f_<=3#+$SUj&oNN4e zW9YyB5Nw(es(?+6vtG7VZw3-SbYqA*%ph)2sInQ9vj$yJxut)3SAYp){Lr|tcxQW| zeIUN+Ynx=iT)AUij#5h{%gZdMrNuF=s0y?st8JW9`bb+enoJ_I*PL*sXK!8E#@OL8 zgy`%Z+Y0+la2kl0x2%lkV#!j{G7;1r)shReRofW5_+}L6x@eLn38&p;3;P%L5?N2a zMP9*aGS4$>^kHK$Rw7&tR+^CzbEw0%hj=;eCa#3nQXFAWN8_d%auD|8Cb$tRueRcc z)GAtsaFZLM4xzGn(1G3ILv@IA#P4R+|7)Hme>?SXDnL8U4@0Tcv|gJZ`$c_jZmv!P z%I<3yCZ~+LY1@l4+Qc(1TMAij2uf*cWk>~3!opR+UY=ryte~_d zVCTC)K(G|aObhr|OibK(p?rIpOK!!3Em6)Wjf7LZCLOtFgjq(QBW^ob9!kTu88TGZ z*g`oABm_b5GU4Tja2>T|?`j-z zS+Lc{f@5KwVuv@l%nX@k&}^?JGu1GLY$t67QNf*ZD!Hm18;<@7r0uw++=y&3>{dIG z3b-5cjIl^Zs5OV^?r=+l+U;0-i)=BiM9|+)gglBnq5itDS9{-xoe;0d&wx<;?# zHM9h$rf(6P#KVszLSv!CW8wG`kS=^4;}g*Z{Ll$9IXF!h`QHF-DB z8m5CC6~jv6JX@d{tUModH3ybnAM*6cu2$q6G}r*hUWPO82>K zhW1aq(`~G=I%2jVl1PGwVjU)Zqj4Y-74cXsE<%xj&FS}J32x)OVh4FV)|fOx0j(nj z9X;yns6sZv$H+`Bq-|eM(Ze^F z`-sGG%NZtLB8M=erFUX|5^~Z7R;>)my^+=e}s@}3Pjl;sLa zznMP6G>!LkQ*DO116?Ey%QEh?@a)3q8S+Q$0QN(XJPgg~dqeY@2cWtNi>fa?8B}!k z%{&nwEGAI#F9~J5Bmn*`T)%GF*6D~r9R2Q&1o2}C46S;7Z# zpoZ(X&jrqs_i17ibK;>i@)|JrCHNrsDCI3J&p|6&nJPqi2P|Y(>|1Eksm>RwuvL>P@YHr z2r|o1{sd)rQ=XhaUFYXfKjZflz@F&DDSL=)#R>b*@PhlFbOl{tLH02JRO}T0F0Yj@ z$oGsL=1%jJwaogu3aKALQtP1|kE7aWYj5$JDMWs>ILGE2sFu*2CV5~H&++DU4#Q9Nfhc-C}1?D2{SxnpIL-C@OLl50`S>ISmHLZZxJ?tzkd-9 zkP6A{ug*7L#K-0iA4$#59G+R2J~DG#YTsR{>u#OfHaB;;K*s#n@FOIJ*YX*>i|aS? zX>tVRZNTj#cj12>xfNX7koC)XVfW0!9bcI{oGKQIzx`4DO21ct9W%2>4^A(9_Er80 M75dY!$^TFKKi?ExG5`Po literal 0 HcmV?d00001 diff --git a/fonts/h5p-core-17.svg b/fonts/h5p-core-17.svg new file mode 100755 index 0000000..1c5ad99 --- /dev/null +++ b/fonts/h5p-core-17.svg @@ -0,0 +1,50 @@ + + + + + + +{ + "fontFamily": "h5p", + "description": "Font generated by IcoMoon.", + "majorVersion": 1, + "minorVersion": 1, + "version": "Version 1.1", + "fontId": "h5p", + "psName": "h5p", + "subFamily": "Regular", + "fullName": "h5p" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/h5p-core-17.ttf b/fonts/h5p-core-17.ttf new file mode 100755 index 0000000000000000000000000000000000000000..d9350bf45cb00ec56c06884c348946d1ea9f036a GIT binary patch literal 6040 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 NLWTYG-{k-2`5*nsP-g%D literal 0 HcmV?d00001 diff --git a/fonts/h5p-core-17.woff b/fonts/h5p-core-17.woff new file mode 100755 index 0000000000000000000000000000000000000000..0e07503a8a1476e1113c0136dafd2410a0e44dbb GIT binary patch literal 6116 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 literal 0 HcmV?d00001 diff --git a/styles/h5p.css b/styles/h5p.css old mode 100644 new mode 100755 index 2e5842e..af1af73 --- a/styles/h5p.css +++ b/styles/h5p.css @@ -3,11 +3,11 @@ /* Custom H5P font to use for icons. */ @font-face { font-family: 'h5p'; - src: url('../fonts/h5p-core-16.eot?80e76o'); - src: url('../fonts/h5p-core-16.eot?80e76o#iefix') format('embedded-opentype'), - url('../fonts/h5p-core-16.ttf?80e76o') format('truetype'), - url('../fonts/h5p-core-16.woff?80e76o') format('woff'), - url('../fonts/h5p-core-16.svg?80e76o#h5p-core-15') format('svg'); + src: url('../fonts/h5p-core-17.eot?8pte1w'); + src: url('../fonts/h5p-core-17.eot?8pte1w#iefix') format('embedded-opentype'), + url('../fonts/h5p-core-17.ttf?8pte1w') format('truetype'), + url('../fonts/h5p-core-17.woff?8pte1w') format('woff'), + url('../fonts/h5p-core-17.svg?8pte1w#h5p') format('svg'); font-weight: normal; font-style: normal; } From c41d001754128a8901a6452ef2601be426b44708 Mon Sep 17 00:00:00 2001 From: Andreas Nergaard Date: Thu, 9 Feb 2017 14:09:25 +0100 Subject: [PATCH 36/36] HFP-750 Adding fixed icon. --- fonts/h5p-core-17.eot | Bin 6188 -> 6072 bytes fonts/h5p-core-17.svg | 2 +- fonts/h5p-core-17.ttf | Bin 6040 -> 5924 bytes fonts/h5p-core-17.woff | Bin 6116 -> 6000 bytes styles/h5p.css | 10 +++++----- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fonts/h5p-core-17.eot b/fonts/h5p-core-17.eot index 1e70252b8fc169596257637bb69d84d7db2e1f2f..89bfcc6349e3036065ebc568728a7ad0eae161a5 100755 GIT binary patch delta 536 zcmZ2uutT49hd2X+%0yOk7VD&tu89s)>OXAa$zfn%+`zyfbR{D-F@-0!SCN51q=kWj z!7KwPz}~`q7s%fOf3=ERGag|S5@C>JkY!M0P-W0$ z&}A?Ls-0}eEX-zV%_ypDI@y3(VsZqt5ue8#4-bz}cXxLXW|%yUd8vd5g9L*zP`MHx zlcU~`kH*9xiHEk6~(oxy9ot=@)#H@A}AEInE32oWR5OQ-VAs-?k zsuv5A+e1h%g@Kfyut-5c1pb7cg5JWQ#a^Q5A*4G+5YofB=YHRJ&-u>bzD%rivCdlH z;R5RpSj$`PLjQxgMRWBwK-Pe8W^y8bAvkf20FfU6+(}8Ox0DZn-X(F%WMO9ZB8(Y{ zvl17lrqAc?bi7644T*aS`Pm{YvKufYMAPMbVdCz`pzLQfNsp1@^wk+Cz7(DHbtp>d z6xkqF7*W)t5hhyEfrNZ(>t?qMbw?Hh%3v^?$z+B-&+|hpDo^Wl)IknGnnP5N#HgL@ zqt5*v#VkZrq!n;21IOAeI+-|mM+~F!ADtDN6Nn=w?(^z%2f1YGoHCJg_ zya=aazxg(bW{3YxZzWO+KV%)jL|PcB3Lxu~do~i{uv`6o*Ry diff --git a/fonts/h5p-core-17.svg b/fonts/h5p-core-17.svg index 1c5ad99..9c12732 100755 --- a/fonts/h5p-core-17.svg +++ b/fonts/h5p-core-17.svg @@ -45,6 +45,6 @@ - + \ No newline at end of file diff --git a/fonts/h5p-core-17.ttf b/fonts/h5p-core-17.ttf index d9350bf45cb00ec56c06884c348946d1ea9f036a..4d70f94fb03a2743caab5f6d4365b0d4c990fc82 100755 GIT binary patch delta 538 zcmbQCzeI0>L;Z(MJUI*uj2jpjgsx(kk2C)(#{{fI+kdvRBXw0Jfgn>ac0LZt>O{^$j&}0+>@_T@Mg}lVv)Sf3ij6nVx zpdDca`Nbs+%s?3iaTbs~12gl+{YMzJMHnO*WEm70R2eiGbQuhRN{vO8&4HL5i8i%n z6je5zJcmhQ@)0H@K94yb9v-3Y?(QJWFqw&Yse}lF1cNeAvl1VZsE8bsv5_5W{rPBlS5cTnKYCpuVR(ie3JDQ zv#2nG1)Aj`yH9eOF@{ZM}vqA%!`VnV&(FL7Tw=Q-iXhppqVwsfiuX z{c=Em@G*fzkbDF*RC%(Bun`k8|Kv(xlg+1vO&MjnLXxcG`E9;3aDxJtf#J#_@uM($ zvW)0%^)(Fp7=AG7FcvZKFil|o!xF^uhSh;}73&kW0QMmE77j6v8jdTRS)2cf&SC@r DE!ubV delta 656 zcmZuuO=}ZD7=CAVXLq7Wq{;3^ZH*;cOgEK8vmavAP(-uAgJ?}+6=H}Q3E0@60do)w zq8|K!!d#>`r9$z8s-dTfMd+bFz?0xjdI@?FKM<+TS_>i`-g)PFo_RkWW;%H|ws5e^ebD*_2E^yo&8$VMubIAdY z7dg%}s_iBiClnTjoO{}e%?>(=QB#p@k=cbJSo!lbP$B+3yUZeh6wb+R?s01 zLy+Q6*({E*SD5~ZjK7f2=ZmiEdch>&VO51qzz3sP5UWZQo9QgJcUnmtmrYd<>$Yk0 zP)!+wQ^^@duHvla#OUa?In%tIB6xO{cMZd>I9AzBU^BO}U_CJ3rhrlLzV84Wfs=3w zPO}%H%f5>z@Jfc2ND&=kPe~sJ=q!=0peeBXpH=uKI$QENc-LL_(^u*oTUVkJ1Rle% zP{dtOz*B{>T^EK7?llsKf`*_`&4-I`qSK6W;4os{1Lo_FK9cu ji2Zm{_#qa=k7Pf2LYAcwxgg)98|hVF&^O(=rB0z=E|G}@ diff --git a/fonts/h5p-core-17.woff b/fonts/h5p-core-17.woff index 0e07503a8a1476e1113c0136dafd2410a0e44dbb..78cdb4c2904038a3fe7e63d96c435195a3edda3d 100755 GIT binary patch delta 549 zcmaE&|3Ocp+~3WOfsp|S#0wa>!L$mH&p7!ZqsT-Zw|b!~3=Be7K5XL2$w*C1VPFus z090cJ!aS+HiWxvb1_n_MAYTQ9*;|ld`zMua!kfXc1)&*CVEVhc~}$}6DI4iNOKyx{o8M{$oQ82_o}3}NTYU|~K87ERI*dh3JWLaq|F8tHykT`q`6+c3fgIFbMBw%8S2HHch z7xko|%td-ptAeN?5pJM;3ue2%r_{j>Wr z89*Rbpbf5Gy(%=~?~!(8JhPzY0My)~@!)*5Qa%pgpAvU08+TlNYE08q*1qO0vj zGmSG%5%2lKl^4jTLI8rW4h)FE7{vKItn&|;{lP0)?m{k?D`vA<8=jYL5BOog&WFNY z?AJm#nMh-EV=9It+$RrVYm{%0gD#xRbf2b{l5gU`3-S%e6zqqCa2SsAuAJqsojN~6 zCUJLyFOeeJ#ea}N4A9bON76Oe{LgeT+~Ati!kaGf-Of_$nzO_f2;771QW1ARgD9k7 zFVz3@tS=nHW-2XGhD2H~Hv0aKZ4Xvb))th|1AZ?WL*eyiK%;iuhHT$7V(8Z?p|3^B zgzm?Xou4+%X+Eir8Y7IFR6$6gCSfNTsS(DA$7AmY2=h<{cX&fhg?j!7RTR2x_kvW( zi#Ka1V|130)=kw!gVx78(IdXC-@qk{4+MDm?8~Fym4DS9NAAKC_>RWWY3#mRN