From 1b079d36f17e299dd8ee5873847dec59ee762083 Mon Sep 17 00:00:00 2001 From: Thomas Horn Sivertsen Date: Fri, 19 Jan 2018 14:42:43 +0100 Subject: [PATCH 01/92] HFP-1806 Load presave script if present in library. --- h5p-default-storage.class.php | 11 +++++++++++ h5p-file-storage.interface.php | 7 +++++++ h5p.classes.php | 14 +++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 38af307..ba41eb9 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -451,6 +451,17 @@ class H5PDefaultStorage implements \H5PFileStorage { return self::dirReady($this->path); } + public function hasPresave($name) { + $filePath = implode(DIRECTORY_SEPARATOR, [ + $this->path, + 'libraries', + $name, + 'presave.js', + ]); + + return file_exists($filePath); + } + /** * Recursive function for copying directories. * diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 394210b..953365c 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -190,4 +190,11 @@ interface H5PFileStorage { * @return bool True if server has the proper write access */ public function hasWriteAccess(); + + /** + * Check if the library has a presave.js in the root folder + * + * @return string|null Path to script or null if missing + */ + public function hasPresave($name); } diff --git a/h5p.classes.php b/h5p.classes.php index d8ed62e..a978516 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2109,14 +2109,18 @@ class H5PCore { // Using content dependencies foreach ($dependencies as $dependency) { + $libraryName = H5PCore::libraryToString($dependency, TRUE); if (isset($dependency['path']) === FALSE) { - $dependency['path'] = 'libraries/' . H5PCore::libraryToString($dependency, TRUE); + $dependency['path'] = 'libraries/' . $libraryName; $dependency['preloadedJs'] = explode(',', $dependency['preloadedJs']); $dependency['preloadedCss'] = explode(',', $dependency['preloadedCss']); } $dependency['version'] = "?ver={$dependency['majorVersion']}.{$dependency['minorVersion']}.{$dependency['patchVersion']}"; $this->getDependencyAssets($dependency, 'preloadedJs', $files['scripts'], $prefix); $this->getDependencyAssets($dependency, 'preloadedCss', $files['styles'], $prefix); + if( $this->fs->hasPresave($libraryName) ){ + $this->addPresaveFile($files, $dependency, $prefix); + } } if ($this->aggregateAssets) { @@ -2130,6 +2134,14 @@ class H5PCore { return $files; } + public function addPresaveFile(&$assets, $library, $prefix = ''){ + $this->getDependencyAssets([ + 'path' => 'libraries' . DIRECTORY_SEPARATOR . self::libraryToString($library, true), + 'version' => array_key_exists('version', $library) ? $library['version'] : "?ver={$library['majorVersion']}.{$library['minorVersion']}.{$library['patchVersion']}", + 'presaveJs' => ['presave.js'] + ], 'presaveJs', $assets['scripts'], $prefix); + } + private static function getDependenciesHash(&$dependencies) { // Build hash of dependencies $toHash = array(); From 04edd738552cc4ee0391824459eb4ced16e54351 Mon Sep 17 00:00:00 2001 From: Thomas Horn Sivertsen Date: Fri, 19 Jan 2018 14:56:07 +0100 Subject: [PATCH 02/92] HFP-1806 Formatting code + adding comments. --- h5p-default-storage.class.php | 21 +++++++++++++-------- h5p.classes.php | 16 ++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index ba41eb9..a06c47c 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -451,15 +451,20 @@ class H5PDefaultStorage implements \H5PFileStorage { return self::dirReady($this->path); } + /** + * Check if the file presave.js exists in the root of the library + * + * @param string $name + * @return bool + */ public function hasPresave($name) { - $filePath = implode(DIRECTORY_SEPARATOR, [ - $this->path, - 'libraries', - $name, - 'presave.js', - ]); - - return file_exists($filePath); + $filePath = implode(DIRECTORY_SEPARATOR, [ + $this->path, + 'libraries', + $name, + 'presave.js', + ]); + return file_exists($filePath); } /** diff --git a/h5p.classes.php b/h5p.classes.php index a978516..80399d9 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2119,7 +2119,7 @@ class H5PCore { $this->getDependencyAssets($dependency, 'preloadedJs', $files['scripts'], $prefix); $this->getDependencyAssets($dependency, 'preloadedCss', $files['styles'], $prefix); if( $this->fs->hasPresave($libraryName) ){ - $this->addPresaveFile($files, $dependency, $prefix); + $this->addPresaveFile($files, $dependency, $prefix); } } @@ -2134,13 +2134,13 @@ class H5PCore { return $files; } - public function addPresaveFile(&$assets, $library, $prefix = ''){ - $this->getDependencyAssets([ - 'path' => 'libraries' . DIRECTORY_SEPARATOR . self::libraryToString($library, true), - 'version' => array_key_exists('version', $library) ? $library['version'] : "?ver={$library['majorVersion']}.{$library['minorVersion']}.{$library['patchVersion']}", - 'presaveJs' => ['presave.js'] - ], 'presaveJs', $assets['scripts'], $prefix); - } + public function addPresaveFile(&$assets, $library, $prefix = ''){ + $this->getDependencyAssets([ + 'path' => 'libraries' . DIRECTORY_SEPARATOR . self::libraryToString($library, true), + 'version' => array_key_exists('version', $library) ? $library['version'] : "?ver={$library['majorVersion']}.{$library['minorVersion']}.{$library['patchVersion']}", + 'presaveJs' => ['presave.js'] + ], 'presaveJs', $assets['scripts'], $prefix); + } private static function getDependenciesHash(&$dependencies) { // Build hash of dependencies From 615bac7c08e4fe23726fc45edbe8615ee9b7139b Mon Sep 17 00:00:00 2001 From: Thomas Horn Sivertsen Date: Thu, 1 Feb 2018 12:18:31 +0100 Subject: [PATCH 03/92] HFP-1806 Add presave for development and public libraries --- h5p-default-storage.class.php | 10 +++------- h5p-file-storage.interface.php | 4 ++-- h5p.classes.php | 16 ++++++++++++++-- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index a06c47c..9766e47 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -457,13 +457,9 @@ class H5PDefaultStorage implements \H5PFileStorage { * @param string $name * @return bool */ - public function hasPresave($name) { - $filePath = implode(DIRECTORY_SEPARATOR, [ - $this->path, - 'libraries', - $name, - 'presave.js', - ]); + public function hasPresave($libraryFolder, $developmentPath = null) { + $path = is_null($developmentPath) ? 'libraries' . DIRECTORY_SEPARATOR . $libraryFolder : $developmentPath; + $filePath = realpath($this->path . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . 'presave.js'); return file_exists($filePath); } diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 953365c..976605e 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -194,7 +194,7 @@ interface H5PFileStorage { /** * Check if the library has a presave.js in the root folder * - * @return string|null Path to script or null if missing + * @return string|null Path if presave.js found */ - public function hasPresave($name); + public function hasPresave($libraryName, $developmentPath = null); } diff --git a/h5p.classes.php b/h5p.classes.php index 80399d9..efac783 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2118,7 +2118,7 @@ class H5PCore { $dependency['version'] = "?ver={$dependency['majorVersion']}.{$dependency['minorVersion']}.{$dependency['patchVersion']}"; $this->getDependencyAssets($dependency, 'preloadedJs', $files['scripts'], $prefix); $this->getDependencyAssets($dependency, 'preloadedCss', $files['styles'], $prefix); - if( $this->fs->hasPresave($libraryName) ){ + if( $this->hasPresave($libraryName) ){ $this->addPresaveFile($files, $dependency, $prefix); } } @@ -2134,9 +2134,21 @@ class H5PCore { return $files; } + public function hasPresave($libraryName) + { + if( isset($this->h5pD) ){ + extract(H5PCore::libraryFromString($libraryName)); + $library = $this->h5pD->getLibrary($machineName, $majorVersion, $minorVersion); + if( !is_null($library)){ + return $this->fs->hasPresave($libraryName, $library['path']); + } + } + return $this->fs->hasPresave($libraryName); + } + public function addPresaveFile(&$assets, $library, $prefix = ''){ $this->getDependencyAssets([ - 'path' => 'libraries' . DIRECTORY_SEPARATOR . self::libraryToString($library, true), + 'path' => array_key_exists('path', $library) ? $library['path'] : 'libraries' . DIRECTORY_SEPARATOR . self::libraryToString($library, true), 'version' => array_key_exists('version', $library) ? $library['version'] : "?ver={$library['majorVersion']}.{$library['minorVersion']}.{$library['patchVersion']}", 'presaveJs' => ['presave.js'] ], 'presaveJs', $assets['scripts'], $prefix); From 011c7df6758fc692e4c52ae91eb27e0a45b03eed Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 16 Mar 2018 20:11:08 +0100 Subject: [PATCH 04/92] HFP-1905 Add metadata to contentData --- js/h5p.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index dd74a79..34a1223 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -99,7 +99,8 @@ H5P.init = function (target) { } var library = { library: contentData.library, - params: JSON.parse(contentData.jsonContent) + params: JSON.parse(contentData.jsonContent), + metadata: contentData.metadata }; H5P.getUserData(contentId, 'state', function (err, previousState) { @@ -795,6 +796,10 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize, extras) { extras.previousState = library.userDatas.state; } + if (library.metadata) { + extras.metadata = library.metadata; + } + // Makes all H5P libraries extend H5P.ContentType: var standalone = extras.standalone || false; // This order makes it possible for an H5P library to override H5P.ContentType functions! From 6ef2f96e8bbb22798f77fabd8f3108769b73d2d7 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 23 Mar 2018 13:11:00 +0100 Subject: [PATCH 05/92] HFP-1905 Add logic for download/upload Add metadata fields to h5p.json --- h5p.classes.php | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index d8ed62e..3f54f0d 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -620,8 +620,17 @@ class H5PValidator { private $h5pOptional = array( 'contentType' => '/^.{1,255}$/', + // deprecated '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|MPL|MPL2)$/', + 'authors' => array( + 'name' => '/^.{1,255}$/', + 'role' => '/^(Illustrator|Designer|Photographer)$/', + ), + 'license' => '/^(CC BY|CC BY-SA|CC BY-ND|CC BY-NC|CC BY-NC-SA|CC BY-NC-ND|GNU GPL|PD|ODC PDDL|CC PDM|U|C|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)$/', + 'licenseVersion' => '/^(1.0|2.0|2.5|3.0|4.0)$/', + 'source' => '/^(http[s]?://.+)$', + 'yearsFrom' => '/^([0-9]{1,4})$/', + 'yearsTo' => '/^([0-9]{1,4})$/', 'dynamicDependencies' => array( 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'majorVersion' => '/^[0-9]{1,5}$/', @@ -629,7 +638,9 @@ class H5PValidator { ), 'w' => '/^[0-9]{1,4}$/', 'h' => '/^[0-9]{1,4}$/', + // deprecated 'metaKeywords' => '/^.{1,}$/', + // deprecated 'metaDescription' => '/^.{1,}$/', ); @@ -1584,14 +1595,30 @@ Class H5PExport { // Make embedType into an array $embedTypes = explode(', ', $content['embedType']); - // Build h5p.json + // Build h5p.json, the en-/de-coding will ensure proper escaping $h5pJson = array ( 'title' => $content['title'], 'language' => (isset($content['language']) && strlen(trim($content['language'])) !== 0) ? $content['language'] : 'und', 'mainLibrary' => $content['library']['name'], 'embedTypes' => $embedTypes, + 'authors' => json_decode(json_encode($content['metadata']['authors'], TRUE)), + 'source' => $content['metadata']['source'], + 'yearFrom' => $content['metadata']['yearFrom'], + 'yearTo' => $content['metadata']['yearTo'], + 'license' => $content['metadata']['license'], + 'licenseVersion' => $content['metadata']['licenseVersion'], + 'licenseExtras' => $content['metadata']['licenseExtras'], + 'changes' => json_decode(json_encode($content['metadata']['changes'], TRUE)), + 'authorComments' => $content['metadata']['authorComments'] ); + // Remove all values that are not set + foreach ($h5pJson as $key => $value) { + if (!isset($value)) { + unset($h5pJson[$key]); + } + } + // Add dependencies to h5p foreach ($content['dependencies'] as $dependency) { $library = $dependency['library']; From c589285351d5471be05484e35d73917eb61579d5 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 26 Mar 2018 11:24:26 +0200 Subject: [PATCH 06/92] HFP-1905 Fix unset values for array fields Some array fields were filled with undefined and lead to warnings. --- h5p.classes.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 3f54f0d..659475c 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1600,18 +1600,15 @@ Class H5PExport { 'title' => $content['title'], 'language' => (isset($content['language']) && strlen(trim($content['language'])) !== 0) ? $content['language'] : 'und', 'mainLibrary' => $content['library']['name'], - 'embedTypes' => $embedTypes, - 'authors' => json_decode(json_encode($content['metadata']['authors'], TRUE)), - 'source' => $content['metadata']['source'], - 'yearFrom' => $content['metadata']['yearFrom'], - 'yearTo' => $content['metadata']['yearTo'], - 'license' => $content['metadata']['license'], - 'licenseVersion' => $content['metadata']['licenseVersion'], - 'licenseExtras' => $content['metadata']['licenseExtras'], - 'changes' => json_decode(json_encode($content['metadata']['changes'], TRUE)), - 'authorComments' => $content['metadata']['authorComments'] + 'embedTypes' => $embedTypes ); + foreach(array('authors', 'source', 'license', 'licenseVersion', 'licenseExtras' ,'yearFrom', 'yearTo', 'changes', 'authorComments') as $field) { + if (isset($content['metadata'][$field])) { + $h5pJson[$field] = json_decode(json_encode($content['metadata'][$field], TRUE)); + } + } + // Remove all values that are not set foreach ($h5pJson as $key => $value) { if (!isset($value)) { From b28624ba8ef1d8db879ed8b781d6b9db7c9cdb89 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 26 Mar 2018 17:44:00 +0200 Subject: [PATCH 07/92] HFP-1905 Allow metadata as keyword for subcontent --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 659475c..23a125c 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -3736,7 +3736,7 @@ class H5PContentValidator { 'type' => 'group', 'fields' => $library['semantics'], ), FALSE); - $validKeys = array('library', 'params', 'subContentId'); + $validKeys = array('library', 'params', 'subContentId', 'metadata'); if (isset($semantics->extraAttributes)) { $validKeys = array_merge($validKeys, $semantics->extraAttributes); } From 95d99d0ad37f6c58997fb36885e70039a88b3f16 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Tue, 3 Apr 2018 15:17:46 +0200 Subject: [PATCH 08/92] HFP-1905 Add update functionality for metadata --- js/h5p-content-upgrade-process.js | 51 ++++++++++++++++++++++++------- js/h5p-content-upgrade-worker.js | 7 +++-- js/h5p-content-upgrade.js | 26 ++++++++++------ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index e54dbb7..0f954ac 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -7,7 +7,7 @@ H5P.ContentUpgradeProcess = (function (Version) { * @class * @namespace H5P */ - function ContentUpgradeProcess(name, oldVersion, newVersion, params, id, loadLibrary, done) { + function ContentUpgradeProcess(name, oldVersion, newVersion, params, extras, id, loadLibrary, done) { var self = this; // Make params possible to work with @@ -24,20 +24,38 @@ H5P.ContentUpgradeProcess = (function (Version) { }); } + // Make extras possible to work with + for (var element in extras) { + if (extras.hasOwnProperty(element)) { + try { + extras[element] = JSON.parse(extras[element]); + if (!(extras[element] instanceof Object)) { + throw true; + } + } + catch (event) { + return done({ + type: 'errorExtrasBroken', + id: id + }); + } + } + } + self.loadLibrary = loadLibrary; - self.upgrade(name, oldVersion, newVersion, params, function (err, result) { + self.upgrade(name, oldVersion, newVersion, params, extras, function (err, result) { if (err) { return done(err); } - done(null, JSON.stringify(params)); + done(null, JSON.stringify(params), JSON.stringify(extras)); }); } /** * */ - ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, done) { + ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, extras, done) { var self = this; // Load library details and upgrade routines @@ -47,7 +65,7 @@ H5P.ContentUpgradeProcess = (function (Version) { } // Run upgrade routines on params - self.processParams(library, oldVersion, newVersion, params, function (err, params) { + self.processParams(library, oldVersion, newVersion, params, extras, function (err, params, extras) { if (err) { return done(err); } @@ -61,7 +79,7 @@ H5P.ContentUpgradeProcess = (function (Version) { next(err); }); }, function (err) { - done(err, params); + done(err, params, extras); }); }); }); @@ -77,7 +95,7 @@ H5P.ContentUpgradeProcess = (function (Version) { * @param {Object} params * @param {Function} next */ - ContentUpgradeProcess.prototype.processParams = function (library, oldVersion, newVersion, params, next) { + ContentUpgradeProcess.prototype.processParams = function (library, oldVersion, newVersion, params, extras, next) { if (H5PUpgrades[library.name] === undefined) { if (library.upgradesScript) { // Upgrades script should be loaded so the upgrades should be here. @@ -110,10 +128,11 @@ H5P.ContentUpgradeProcess = (function (Version) { var unnecessaryWrapper = (upgrade.contentUpgrade !== undefined ? upgrade.contentUpgrade : upgrade); try { - unnecessaryWrapper(params, function (err, upgradedParams) { + unnecessaryWrapper(params, function (err, upgradedParams, upgradedExtras) { params = upgradedParams; + extras = upgradedExtras; nextMinor(err); - }); + }, extras); } catch (err) { if (console && console.log) { @@ -127,7 +146,7 @@ H5P.ContentUpgradeProcess = (function (Version) { }, nextMajor); } }, function (err) { - next(err, params); + next(err, params, extras); }); }; @@ -168,11 +187,21 @@ H5P.ContentUpgradeProcess = (function (Version) { return done(); // Larger or same version that's available } + // Currently, we only use metadata as additional things that might need change + var extras = { + metadata: params.metadata + }; + // A newer version is available, upgrade params - return self.upgrade(availableLib[0], usedVer, availableVer, params.params, function (err, upgraded) { + return self.upgrade(availableLib[0], usedVer, availableVer, params.params, extras, function (err, upgraded, extras) { if (!err) { params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor; params.params = upgraded; + if (extras) { + if (extras.metadata) { + params.metadata = extras.metadata; + } + } } done(err, params); }); diff --git a/js/h5p-content-upgrade-worker.js b/js/h5p-content-upgrade-worker.js index 26ad038..af0be94 100644 --- a/js/h5p-content-upgrade-worker.js +++ b/js/h5p-content-upgrade-worker.js @@ -9,7 +9,7 @@ var libraryLoadedCallback; var messageHandlers = { newJob: function (job) { // Start new job - new H5P.ContentUpgradeProcess(job.name, new H5P.Version(job.oldVersion), new H5P.Version(job.newVersion), job.params, job.id, function loadLibrary(name, version, next) { + new H5P.ContentUpgradeProcess(job.name, new H5P.Version(job.oldVersion), new H5P.Version(job.newVersion), job.params, job.extras, job.id, function loadLibrary(name, version, next) { // TODO: Cache? postMessage({ action: 'loadLibrary', @@ -17,7 +17,7 @@ var messageHandlers = { version: version.toString() }); libraryLoadedCallback = next; - }, function done(err, result) { + }, function done(err, result, extras) { if (err) { // Return error postMessage({ @@ -33,7 +33,8 @@ var messageHandlers = { postMessage({ action: 'done', id: job.id, - params: result + params: result, + extras: extras }); }); }, diff --git a/js/h5p-content-upgrade.js b/js/h5p-content-upgrade.js index 9c1e4ce..564a0a8 100644 --- a/js/h5p-content-upgrade.js +++ b/js/h5p-content-upgrade.js @@ -116,7 +116,7 @@ // Register message handlers var messageHandlers = { done: function (result) { - self.workDone(result.id, result.params, this); + self.workDone(result.id, result.params, result.extras, this); }, error: function (error) { self.printError(error.err); @@ -184,7 +184,7 @@ self.token = inData.token; // Start processing - self.processBatch(inData.params); + self.processBatch(inData.params, {metadata: inData.metadata}); }); }; @@ -202,7 +202,7 @@ * * @param {Object} parameters */ - ContentUpgrade.prototype.processBatch = function (parameters) { + ContentUpgrade.prototype.processBatch = function (parameters, extras) { var self = this; // Track upgraded params @@ -210,6 +210,7 @@ // Track current batch self.parameters = parameters; + self.extras = extras; // Create id mapping self.ids = []; @@ -247,6 +248,11 @@ self.current++; self.working++; + // Rewrap metadata + if (self.extras.metadata) { + self.extras.metadata = self.extras.metadata[id]; + } + if (worker) { worker.postMessage({ action: 'newJob', @@ -254,11 +260,12 @@ name: info.library.name, oldVersion: info.library.version, newVersion: self.version.toString(), - params: self.parameters[id] + params: self.parameters[id], + extras: self.extras }); } else { - new H5P.ContentUpgradeProcess(info.library.name, new Version(info.library.version), self.version, self.parameters[id], id, function loadLibrary(name, version, next) { + new H5P.ContentUpgradeProcess(info.library.name, new Version(info.library.version), self.version, self.parameters[id], self.extras, id, function loadLibrary(name, version, next) { self.loadLibrary(name, version, function (err, library) { if (library.upgradesScript) { self.loadScript(library.upgradesScript, function (err) { @@ -273,13 +280,13 @@ } }); - }, function done(err, result) { + }, function done(err, result, extras) { if (err) { self.printError(err); return ; } - self.workDone(id, result); + self.workDone(id, result, extras); }); } }; @@ -287,7 +294,7 @@ /** * */ - ContentUpgrade.prototype.workDone = function (id, result, worker) { + ContentUpgrade.prototype.workDone = function (id, result, extras, worker) { var self = this; self.working--; @@ -302,7 +309,8 @@ self.nextBatch({ libraryId: self.version.libraryId, token: self.token, - params: JSON.stringify(self.upgraded) + params: JSON.stringify(self.upgraded), + extras: extras }); } }; From 83e3c58ba3fcafcb89a451bc8033836f42690cde Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 4 Apr 2018 15:46:30 +0200 Subject: [PATCH 09/92] HFP-1943 Include metadata information in "rights of use" popup Add copyright information of main content in popup Add copyright information of subcontent in popup HFP-1902 is intended to make the styling better --- js/h5p.js | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 34a1223..d14001e 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -164,7 +164,7 @@ H5P.init = function (target) { if (displayOptions.frame) { // Special handling of copyrights if (displayOptions.copyright) { - var copyrights = H5P.getCopyrights(instance, library.params, contentId); + var copyrights = H5P.getCopyrights(instance, library.params, library.metadata, contentId); if (!copyrights) { displayOptions.copyright = false; } @@ -976,11 +976,13 @@ H5P.Dialog = function (name, title, content, $element) { * H5P instance to get copyright information for. * @param {Object} parameters * Parameters of the content instance. + * @param {Object} metadata + * Metadata of the content instance. * @param {number} contentId * Identifies the H5P content * @returns {string} Copyright information. */ -H5P.getCopyrights = function (instance, parameters, contentId) { +H5P.getCopyrights = function (instance, parameters, metadata, contentId) { var copyrights; if (instance.getCopyrights !== undefined) { @@ -999,6 +1001,11 @@ H5P.getCopyrights = function (instance, parameters, contentId) { H5P.findCopyrights(copyrights, parameters, contentId); } + var metadataCopyrights = H5P.buildMetadataCopyrights(metadata, instance.libraryInfo.machineName); + if (metadataCopyrights !== undefined) { + copyrights.addMediaInFront(metadataCopyrights); + } + if (copyrights !== undefined) { // Convert to string copyrights = copyrights.toString(); @@ -1017,6 +1024,7 @@ H5P.getCopyrights = function (instance, parameters, contentId) { * Used to insert thumbnails for images. */ H5P.findCopyrights = function (info, parameters, contentId) { + var lastContentTypeName = undefined; // Cycle through parameters for (var field in parameters) { if (!parameters.hasOwnProperty(field)) { @@ -1026,6 +1034,8 @@ H5P.findCopyrights = function (info, parameters, contentId) { /** * @deprecated This hack should be removed after 2017-11-01 * The code that was using this was removed by HFP-574 + * This note was seen on 2018-04-04, and consultation with + * higher authorities lead to keeping the code for now ;-) */ if (field === 'overrideSettings') { console.warn("The semantics field 'overrideSettings' is DEPRECATED and should not be used."); @@ -1035,11 +1045,21 @@ H5P.findCopyrights = function (info, parameters, contentId) { var value = parameters[field]; + // TODO: Get the real name of the content type + lastContentTypeName = (value.library) ? value.library.split(' ')[0] : lastContentTypeName; + if (value instanceof Array) { // Cycle through array H5P.findCopyrights(info, value, contentId); } else if (value instanceof Object) { + if (value.metadata) { + var metadataCopyrights = H5P.buildMetadataCopyrights(value.metadata, lastContentTypeName); + if (metadataCopyrights !== undefined) { + info.addMedia(metadataCopyrights); + } + } + // Check if object is a file with copyrights if (value.copyright === undefined || value.copyright.license === undefined || @@ -1061,6 +1081,34 @@ H5P.findCopyrights = function (info, parameters, contentId) { } }; +H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { + if (metadata && metadata.license !== undefined && metadata.license !== 'U') { + var dataset = { + title: metadata.title, + author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function(author) {return author.name + ' (' + author.role + ')';}).join(', ') : undefined, + source: metadata.source, + year: (metadata.yearFrom) ? (metadata.yearFrom + ((metadata.yearTo) ? '-' + metadata.yearTo: '')) : undefined, + license: metadata.license, + version: metadata.licenseVersion, + licenseExtras: metadata.licenseExtras, + changes: (metadata.changes && metadata.changes.length > 0) ? metadata.changes.map(function(change) { + return change.log + (change.author ? ', ' + change.author : '') + (change.date ? ', ' + change.date : ''); + }).join(' / ') : undefined + }; + + if (contentTypeName && contentTypeName.indexOf('H5P.') === 0) { + contentTypeName = contentTypeName.substr(4); + } + + return new H5P.MediaCopyright( + dataset, + {type: 'Content type', licenseExtras: 'License extras', changes: 'Changelog'}, + ['type', 'title', 'license', 'author', 'year', 'source', 'licenseExtras', 'changes'], + {type: contentTypeName} + ); + } +}; + /** * Display a dialog containing the embed code. * @@ -1180,6 +1228,17 @@ H5P.ContentCopyrights = function () { } }; + /** + * Add sub content in front. + * + * @param {H5P.MediaCopyright} newMedia + */ + this.addMediaInFront = function (newMedia) { + if (newMedia !== undefined) { + media.unshift(newMedia); + } + }; + /** * Add sub content. * From ef063ce5cb1395ef4805e5faa3cceae758faba93 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 5 Apr 2018 17:17:56 +0200 Subject: [PATCH 10/92] HFP-1905 Fix image in "rights of use" view --- js/h5p.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index d14001e..2f0ba32 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -164,7 +164,7 @@ H5P.init = function (target) { if (displayOptions.frame) { // Special handling of copyrights if (displayOptions.copyright) { - var copyrights = H5P.getCopyrights(instance, library.params, library.metadata, contentId); + var copyrights = H5P.getCopyrights(instance, library.params, contentId, library.metadata); if (!copyrights) { displayOptions.copyright = false; } @@ -982,12 +982,13 @@ H5P.Dialog = function (name, title, content, $element) { * Identifies the H5P content * @returns {string} Copyright information. */ -H5P.getCopyrights = function (instance, parameters, metadata, contentId) { +H5P.getCopyrights = function (instance, parameters, contentId, metadata) { var copyrights; if (instance.getCopyrights !== undefined) { try { // Use the instance's own copyright generator + console.log('getCopyrights() of', instance.libraryInfo.machineName); copyrights = instance.getCopyrights(); } catch (err) { @@ -1056,11 +1057,17 @@ H5P.findCopyrights = function (info, parameters, contentId) { if (value.metadata) { var metadataCopyrights = H5P.buildMetadataCopyrights(value.metadata, lastContentTypeName); if (metadataCopyrights !== undefined) { + if (value.params && value.params.contentName === 'Image' && value.params.file) { + var path = value.params.file.path; + var width = value.params.file.width; + var height = value.params.file.height; + metadataCopyrights.setThumbnail(new H5P.Thumbnail(H5P.getPath(path, contentId), width, height)); + } info.addMedia(metadataCopyrights); } } - // Check if object is a file with copyrights + // Check if object is a file with copyrights (old core) if (value.copyright === undefined || value.copyright.license === undefined || value.path === undefined || From fbc21f636820240aaeb5cbea46456aaed19c2ab3 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 5 Apr 2018 17:22:31 +0200 Subject: [PATCH 11/92] HFP-1905 Remove console output --- js/h5p.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index 2f0ba32..599e59b 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -988,7 +988,6 @@ H5P.getCopyrights = function (instance, parameters, contentId, metadata) { if (instance.getCopyrights !== undefined) { try { // Use the instance's own copyright generator - console.log('getCopyrights() of', instance.libraryInfo.machineName); copyrights = instance.getCopyrights(); } catch (err) { From 3b38e273eb8c40bdd5dda391585692fb09fcbfc4 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 6 Apr 2018 17:11:33 +0200 Subject: [PATCH 12/92] HFP-1905 Bump minor core version number --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 23a125c..65e99d5 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1797,7 +1797,7 @@ class H5PCore { public static $coreApi = array( 'majorVersion' => 1, - 'minorVersion' => 14 + 'minorVersion' => 15 ); public static $styles = array( 'styles/h5p.css', From 58597460f662234dfbe7d4d60385357e58ebad7b Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 9 Apr 2018 11:43:48 +0200 Subject: [PATCH 13/92] HFP-1905 Fix updating for scripts with old update functions "params" (and "extras", e.g. metadata) flow through each update function and are changed if necessary. Since "extras" was added later and old update functions don't return it, we need to ignore undefined values -- or change every single update function in all content types. --- js/h5p-content-upgrade-process.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index 0f954ac..e440341 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -130,7 +130,15 @@ H5P.ContentUpgradeProcess = (function (Version) { try { unnecessaryWrapper(params, function (err, upgradedParams, upgradedExtras) { params = upgradedParams; - extras = upgradedExtras; + /** + * "params" (and "extras", e.g. metadata) flow through each update function and are changed + * if necessary. Since "extras" was added later and old update functions don't return + * it, we need to ignore undefined values here -- or change every single update function + * in all content types. + */ + if (upgradedExtras) { + extras = upgradedExtras; + } nextMinor(err); }, extras); } From 3deda27f9bcde2b4d85826846ef2d00d75f06c02 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 25 May 2018 18:38:42 +0200 Subject: [PATCH 14/92] HFP-1898 Move mocked semantics to backend --- h5p.classes.php | 233 ++++++++++++++++++++++++++++++++++++++++++++++++ js/h5p.js | 11 ++- 2 files changed, 242 insertions(+), 2 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 65e99d5..bc48e66 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -3557,6 +3557,7 @@ class H5PContentValidator { if (isset($file->copyright)) { $this->validateGroup($file->copyright, $this->getCopyrightSemantics()); + // TODO: We'll need to do something here about getMetadataSemantics() if we change the widgets } } @@ -4106,6 +4107,238 @@ class H5PContentValidator { return $uri; } + public function getMetadataSemantics() { + static $semantics; + + $cc_versions = array( + (object) array( + 'value' => '4.0', + 'label' => $this->h5pF->t('4.0 International') + ), + (object) array( + 'value' => '3.0', + 'label' => $this->h5pF->t('3.0 Unported') + ), + (object) array( + 'value' => '2.5', + 'label' => $this->h5pF->t('2.5 Generic') + ), + (object) array( + 'value' => '2.0', + 'label' => $this->h5pF->t('2.0 Generic') + ), + (object) array( + 'value' => '1.0', + 'label' => $this->h5pF->t('1.0 Generic') + ) + ); + + $semantics = (object) array( + (object) array( + 'name' => 'copyright', + 'type' => 'group', + 'label' => $this->h5pF->t('Copyright information'), + 'fields' => array( + (object) array( + 'name' => 'title', + 'type' => 'text', + 'label' => $this->h5pF->t('Title'), + 'placeholder' => 'La Gioconda' + ), + (object) array( + 'name' => 'license', + 'type' => 'select', + 'label' => $this->h5pF->t('License'), + 'default' => 'U', + 'options' => array( + (object) array( + 'value' => 'U', + 'label' => 'Undisclosed' + ), + (object) array( + 'value' => 'CC BY', + 'label' => 'Attribution', + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-SA', + 'label' => 'Attribution-ShareAlike', + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-ND', + 'label' => 'Attribution-NoDerivs', + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-NC', + 'label' => 'Attribution-NonCommercial', + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-NC-SA', + 'label' => 'Attribution-NonCommercial-ShareAlike', + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-NC-ND', + 'label' => 'Attribution-NonCommercial-NoDerivs', + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC0 1.0', + 'label' => 'Public Domain Dedication' + ), + (object) array( + 'value' => 'CC PDM', + 'label' => 'Public Domain Mark' + ), + (object) array( + 'value' => 'GNU GPL', + 'label' => 'General Public License v3' + ), + (object) array( + 'value' => 'PD', + 'label' => 'Public Domain' + ), + (object) array( + 'value' => 'ODC PDDL', + 'label' => 'Public Domain Dedication and Licence' + ), + (object) array( + 'value' => 'C', + 'label' => 'Copyright' + ) + ) + ), + (object) array( + 'name' => 'licenseVersion', + 'type' => 'select', + 'label' => $this->h5pF->t('License Version'), + 'options' => array(), + 'optional' => TRUE + ), + (object) array( + 'name' => 'yearFrom', + 'type' => 'text', + 'label' => $this->h5pF->t('Years (from-to)'), + 'placeholder' => '1991', + 'optional' => TRUE + ), + (object) array( + 'name' => 'yearTo', + 'type' => 'text', + 'label' => 'hiddenLabel', + 'placeholder' => '1992', + 'optional' => TRUE + ), + (object) array( + 'name' => 'source', + 'type' => 'text', + 'label' => 'Source', + 'placeholder' => 'https://', + 'optional' => TRUE, + 'regexp' => array( + 'pattern' => '^http[s]?://.+', + 'modifiers' => 'i' + ) + ) + ) + ), + (object) array( + 'name' => 'authorWidget', + 'type' => 'group', + 'fields'=> array( + (object) array( + 'label' => "Author's name", + 'name' => "authorName", + 'optional' => TRUE, + 'type' => "text" + ), + (object) array( + 'name' => 'authorRole', + 'type' => 'select', + 'label' => $this->h5pF->t("Author's role"), + 'default' => 'Originator', + 'options' => array( + (object) array( + 'value' => 'Editor', + 'label' => $this->h5pF->t('Editor') + ), + (object) array( + 'value' => 'Licensee', + 'label' => $this->h5pF->t('Licensee') + ), + (object) array( + 'value' => 'Originator', + 'label' => $this->h5pF->t('Originator') + ) + ) + ) + ) + ), + (object) array( + 'name' => 'licenseExtras', + 'type' => 'textarea', + 'label' => $this->h5pF->t('License Extras'), + 'optional' => TRUE, + 'description' => $this->h5pF->t('Any additional information about the license') + ), + (object) array( + 'name' => 'changeLog', + 'type' => 'group', + 'expanded' => FALSE, + 'label' => $this->h5pF->t('Change Log'), + 'fields' => array( + (object) array ( + 'name' => 'changeLogForm', + 'type' => 'group', + 'label' => $this->h5pF->t('Question'), + 'expanded' => TRUE, + 'fields' => array( + (object) array( + 'name' => 'date', + 'type' => 'text', + 'label' => $this->h5pF->t('Date'), + 'optional' => TRUE + ), + (object) array( + 'name' => 'author', + 'type' => 'text', + 'label' => $this->h5pF->t('Changed by'), + 'optional' => TRUE + ), + (object) array( + 'name' => 'log', + 'type' => 'textarea', + 'label' => $this->h5pF->t('Description of change'), + 'placeholder' => $this->h5pF->t('Add a description of your change'), + 'optional' => TRUE + ) + ) + ) + ) + ), + (object) array( + 'name' => 'authorComments', + 'label' => $this->h5pF->t('Additional Information'), + 'type' => 'group', + 'expanded' => FALSE, + 'fields' => array( + (object) array ( + 'name' => 'authorComments', + 'type' => 'textarea', + 'label' => $this->h5pF->t('Author comments'), + 'description' => $this->h5pF->t('Comments for the editor of the content (This text will not be published as a part of copyright info'), + 'optional' => TRUE + ) + ) + ) + ); + + return $semantics; + } + public function getCopyrightSemantics() { static $semantics; diff --git a/js/h5p.js b/js/h5p.js index 599e59b..390efe4 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1364,7 +1364,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { link = copyrightLicense.link.replace(':version', copyrightLicense.linkVersions ? copyrightLicense.linkVersions[version] : version); } else if (versionInfo && copyrightLicense.hasOwnProperty('link')) { - link = versionInfo.link + link = versionInfo.link; } if (link) { value = '' + value + ''; @@ -2150,6 +2150,10 @@ H5P.createTitle = function (rawTitle, maxLength) { link: 'http://creativecommons.org/licenses/by-nc-nd/:version/legalcode', versions: ccVersions }, + 'CC0 1.0': { + label: H5P.t('licenseCC010'), + link: 'https://creativecommons.org/publicdomain/zero/1.0/' + }, 'GNU GPL': { label: H5P.t('licenseGPL'), link: 'http://www.gnu.org/licenses/gpl-:version-standalone.html', @@ -2179,7 +2183,10 @@ H5P.createTitle = function (rawTitle, maxLength) { } }, 'ODC PDDL': 'Public Domain Dedication and Licence', - 'CC PDM': H5P.t('licensePDM'), + 'CC PDM': { + label: H5P.t('licensePDM'), + link: 'https://creativecommons.org/publicdomain/mark/1.0/' + }, 'C': H5P.t('licenseC'), }; From 00deb02aa7fddd38eaf9a12193b90073d1bf1f3c Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 28 May 2018 18:01:28 +0200 Subject: [PATCH 15/92] HFP-1897 Set author as default role, change placeholder text for change --- h5p.classes.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index bc48e66..9580125 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4259,8 +4259,12 @@ class H5PContentValidator { 'name' => 'authorRole', 'type' => 'select', 'label' => $this->h5pF->t("Author's role"), - 'default' => 'Originator', + 'default' => 'Author', 'options' => array( + (object) array( + 'value' => 'Editor', + 'label' => $this->h5pF->t('Author') + ), (object) array( 'value' => 'Editor', 'label' => $this->h5pF->t('Editor') @@ -4312,7 +4316,7 @@ class H5PContentValidator { 'name' => 'log', 'type' => 'textarea', 'label' => $this->h5pF->t('Description of change'), - 'placeholder' => $this->h5pF->t('Add a description of your change'), + 'placeholder' => $this->h5pF->t('Photo cropped, text changed, etc.'), 'optional' => TRUE ) ) From 47be831f4990a07325b0563c6b29b9ad3d5fa85f Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Tue, 29 May 2018 17:00:56 +0200 Subject: [PATCH 16/92] HFP-1897 Fix author as default role --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 9580125..9ef5ef5 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4262,7 +4262,7 @@ class H5PContentValidator { 'default' => 'Author', 'options' => array( (object) array( - 'value' => 'Editor', + 'value' => 'Author', 'label' => $this->h5pF->t('Author') ), (object) array( From a30a93e62ec5cb45e6e7b09bc750fe37cae33d09 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Tue, 29 May 2018 17:23:06 +0200 Subject: [PATCH 17/92] HFP-1897 Set correct field types and boundaries --- h5p.classes.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 9ef5ef5..44ea65d 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4220,16 +4220,20 @@ class H5PContentValidator { ), (object) array( 'name' => 'yearFrom', - 'type' => 'text', + 'type' => 'number', 'label' => $this->h5pF->t('Years (from-to)'), 'placeholder' => '1991', + 'min' => '-9999', + 'max' => '9999', 'optional' => TRUE ), (object) array( 'name' => 'yearTo', - 'type' => 'text', + 'type' => 'number', 'label' => 'hiddenLabel', 'placeholder' => '1992', + 'min' => '-9999', + 'max' => '9999', 'optional' => TRUE ), (object) array( From 3ce0adf418a57d68ca00e5c40ea6ddebe888b217 Mon Sep 17 00:00:00 2001 From: thomasmars Date: Fri, 1 Jun 2018 11:09:45 +0200 Subject: [PATCH 18/92] HFP-1942 Add crossorigin api call. Update minor version of core since interface changes --- h5p.classes.php | 2 +- js/h5p.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 310de97..2efebbd 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1773,7 +1773,7 @@ class H5PCore { public static $coreApi = array( 'majorVersion' => 1, - 'minorVersion' => 15 + 'minorVersion' => 16 ); public static $styles = array( 'styles/h5p.css', diff --git a/js/h5p.js b/js/h5p.js index 1b899d3..ef56834 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1938,6 +1938,16 @@ H5P.createTitle = function (rawTitle, maxLength) { } }; + /** + * Get crossorigin option that is set for site. Usefull for setting crossorigin policy for elements. + * + * @returns {string|null} Returns the string that should be set as crossorigin policy for elements or null if + * no policy is set. + */ + H5P.getCrossOrigin = function () { + return H5PIntegration.crossorigin ? H5PIntegration.crossorigin : null; + }; + /** * Async error handling. * From 8f45ea4d7920b1c85189fc3305db44247b749c7e Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 1 Jun 2018 15:45:09 +0200 Subject: [PATCH 19/92] HFP-1905 Fix finding copyright edge case bug --- js/h5p.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 390efe4..ea21c78 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1024,7 +1024,7 @@ H5P.getCopyrights = function (instance, parameters, contentId, metadata) { * Used to insert thumbnails for images. */ H5P.findCopyrights = function (info, parameters, contentId) { - var lastContentTypeName = undefined; + var lastContentTypeName; // Cycle through parameters for (var field in parameters) { if (!parameters.hasOwnProperty(field)) { @@ -1045,8 +1045,12 @@ H5P.findCopyrights = function (info, parameters, contentId) { var value = parameters[field]; - // TODO: Get the real name of the content type - lastContentTypeName = (value.library) ? value.library.split(' ')[0] : lastContentTypeName; + if (value.library && typeof value.library === 'string') { + lastContentTypeName = value.library.split(' ')[0]; + } + else if (value.library && typeof value.library === 'object') { + lastContentTypeName = (value.library.library && typeof value.library.library === 'string') ? value.library.library.split(' ')[0] : lastContentTypeName; + } if (value instanceof Array) { // Cycle through array From 0fbc6ef5a8380ba1430dea92f931837ac1820c05 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 1 Jun 2018 17:44:21 +0200 Subject: [PATCH 20/92] HFP-1905 Fix potential undefined values --- js/h5p.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index ea21c78..6d63259 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1045,10 +1045,10 @@ H5P.findCopyrights = function (info, parameters, contentId) { var value = parameters[field]; - if (value.library && typeof value.library === 'string') { + if (value && value.library && typeof value.library === 'string') { lastContentTypeName = value.library.split(' ')[0]; } - else if (value.library && typeof value.library === 'object') { + else if (value && value.library && typeof value.library === 'object') { lastContentTypeName = (value.library.library && typeof value.library.library === 'string') ? value.library.library.split(' ')[0] : lastContentTypeName; } From ea722126ffc4020642d8062c6d66eb40f563440e Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 4 Jun 2018 15:44:04 +0200 Subject: [PATCH 21/92] HFP-1905 Fix property naming and validation defaults Fix property naming for more harmony (and peace in the world) Fix validation on uploading content by adding Author as a role --- h5p.classes.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 44ea65d..67a6a7b 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -624,7 +624,7 @@ class H5PValidator { 'author' => '/^.{1,255}$/', 'authors' => array( 'name' => '/^.{1,255}$/', - 'role' => '/^(Illustrator|Designer|Photographer)$/', + 'role' => '/^(Author|Illustrator|Designer|Photographer)$/', ), 'license' => '/^(CC BY|CC BY-SA|CC BY-ND|CC BY-NC|CC BY-NC-SA|CC BY-NC-ND|GNU GPL|PD|ODC PDDL|CC PDM|U|C|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)$/', 'licenseVersion' => '/^(1.0|2.0|2.5|3.0|4.0)$/', @@ -4255,12 +4255,12 @@ class H5PContentValidator { 'fields'=> array( (object) array( 'label' => "Author's name", - 'name' => "authorName", + 'name' => "name", 'optional' => TRUE, 'type' => "text" ), (object) array( - 'name' => 'authorRole', + 'name' => 'role', 'type' => 'select', 'label' => $this->h5pF->t("Author's role"), 'default' => 'Author', From be77130fef33dc3ff6b9548cf18f5bc5b7497fb4 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Tue, 5 Jun 2018 08:54:12 +0200 Subject: [PATCH 22/92] HFP-1905 Require words for author roles --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 67a6a7b..47258ef 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -624,7 +624,7 @@ class H5PValidator { 'author' => '/^.{1,255}$/', 'authors' => array( 'name' => '/^.{1,255}$/', - 'role' => '/^(Author|Illustrator|Designer|Photographer)$/', + 'role' => '/^\w+$/', ), 'license' => '/^(CC BY|CC BY-SA|CC BY-ND|CC BY-NC|CC BY-NC-SA|CC BY-NC-ND|GNU GPL|PD|ODC PDDL|CC PDM|U|C|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)$/', 'licenseVersion' => '/^(1.0|2.0|2.5|3.0|4.0)$/', From a59640672dc5ff0030bc688eaacfa8a544ed9f72 Mon Sep 17 00:00:00 2001 From: Thomas Horn Sivertsen Date: Thu, 7 Jun 2018 12:43:12 +0200 Subject: [PATCH 23/92] HFP-1806 - fixed PhpDoc - only load presave.js when editing a H5P - moved loading logic from core to editor - removed unused code --- h5p-default-storage.class.php | 3 ++- h5p-file-storage.interface.php | 4 +++- h5p.classes.php | 23 ----------------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 9766e47..d6d13ea 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -454,7 +454,8 @@ class H5PDefaultStorage implements \H5PFileStorage { /** * Check if the file presave.js exists in the root of the library * - * @param string $name + * @param string $libraryFolder + * @param string $developmentPath * @return bool */ public function hasPresave($libraryFolder, $developmentPath = null) { diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 976605e..4dbdbc6 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -194,7 +194,9 @@ interface H5PFileStorage { /** * Check if the library has a presave.js in the root folder * - * @return string|null Path if presave.js found + * @param string $libraryName + * @param string $developmentPath + * @return bool */ public function hasPresave($libraryName, $developmentPath = null); } diff --git a/h5p.classes.php b/h5p.classes.php index efac783..650895c 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2118,9 +2118,6 @@ class H5PCore { $dependency['version'] = "?ver={$dependency['majorVersion']}.{$dependency['minorVersion']}.{$dependency['patchVersion']}"; $this->getDependencyAssets($dependency, 'preloadedJs', $files['scripts'], $prefix); $this->getDependencyAssets($dependency, 'preloadedCss', $files['styles'], $prefix); - if( $this->hasPresave($libraryName) ){ - $this->addPresaveFile($files, $dependency, $prefix); - } } if ($this->aggregateAssets) { @@ -2134,26 +2131,6 @@ class H5PCore { return $files; } - public function hasPresave($libraryName) - { - if( isset($this->h5pD) ){ - extract(H5PCore::libraryFromString($libraryName)); - $library = $this->h5pD->getLibrary($machineName, $majorVersion, $minorVersion); - if( !is_null($library)){ - return $this->fs->hasPresave($libraryName, $library['path']); - } - } - return $this->fs->hasPresave($libraryName); - } - - public function addPresaveFile(&$assets, $library, $prefix = ''){ - $this->getDependencyAssets([ - 'path' => array_key_exists('path', $library) ? $library['path'] : 'libraries' . DIRECTORY_SEPARATOR . self::libraryToString($library, true), - 'version' => array_key_exists('version', $library) ? $library['version'] : "?ver={$library['majorVersion']}.{$library['minorVersion']}.{$library['patchVersion']}", - 'presaveJs' => ['presave.js'] - ], 'presaveJs', $assets['scripts'], $prefix); - } - private static function getDependenciesHash(&$dependencies) { // Build hash of dependencies $toHash = array(); From e179ec2934cd6cec539c277d12008fbf9b5a47e8 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 13 Jun 2018 16:41:49 +0200 Subject: [PATCH 24/92] HFP-2052 Make changes requested in UX review - Add link for source - Change URLs for licenses --- js/h5p.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 631836d..dc50cd4 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1096,7 +1096,7 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { var dataset = { title: metadata.title, author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function(author) {return author.name + ' (' + author.role + ')';}).join(', ') : undefined, - source: metadata.source, + source: '' + metadata.source + '', year: (metadata.yearFrom) ? (metadata.yearFrom + ((metadata.yearTo) ? '-' + metadata.yearTo: '')) : undefined, license: metadata.license, version: metadata.licenseVersion, @@ -2136,32 +2136,32 @@ H5P.createTitle = function (rawTitle, maxLength) { 'U': H5P.t('licenseU'), 'CC BY': { label: H5P.t('licenseCCBY'), - link: 'http://creativecommons.org/licenses/by/:version/legalcode', + link: 'http://creativecommons.org/licenses/by/:version', versions: ccVersions }, 'CC BY-SA': { label: H5P.t('licenseCCBYSA'), - link: 'http://creativecommons.org/licenses/by-sa/:version/legalcode', + link: 'http://creativecommons.org/licenses/by-sa/:version', versions: ccVersions }, 'CC BY-ND': { label: H5P.t('licenseCCBYND'), - link: 'http://creativecommons.org/licenses/by-nd/:version/legalcode', + link: 'http://creativecommons.org/licenses/by-nd/:version', versions: ccVersions }, 'CC BY-NC': { label: H5P.t('licenseCCBYNC'), - link: 'http://creativecommons.org/licenses/by-nc/:version/legalcode', + link: 'http://creativecommons.org/licenses/by-nc/:version', versions: ccVersions }, 'CC BY-NC-SA': { label: H5P.t('licenseCCBYNCSA'), - link: 'http://creativecommons.org/licenses/by-nc-sa/:version/legalcode', + link: 'http://creativecommons.org/licenses/by-nc-sa/:version', versions: ccVersions }, 'CC BY-NC-ND': { label: H5P.t('licenseCCBYNCND'), - link: 'http://creativecommons.org/licenses/by-nc-nd/:version/legalcode', + link: 'http://creativecommons.org/licenses/by-nc-nd/:version', versions: ccVersions }, 'CC0 1.0': { From 04707a9f8ace4db2491127e6ebb20f0bf26e33dd Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 13 Jun 2018 18:36:23 +0200 Subject: [PATCH 25/92] HFP-1871 Add function to load MathDisplay if params contain math --- h5p.classes.php | 60 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 2efebbd..b160abd 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1770,7 +1770,7 @@ abstract class H5PHubEndpoints { * Functions and storage shared by the other H5P classes */ class H5PCore { - + public static $coreApi = array( 'majorVersion' => 1, 'minorVersion' => 16 @@ -1938,6 +1938,14 @@ class H5PCore { } $validator->validateLibrary($params, (object) array('options' => array($params->library))); + // Add MathDisplay. + // TODO: Get library name for MathDisplay dynamically + // TODO: Get regexp from MathDisplay config + // TODO: Find out how to pass settings to MathDisplay + if ($this->containsMath($params->params)) { + $validator->addMathDisplay('H5P.MathDisplay 0.1'); + } + $params = json_encode($params->params); // Update content dependencies. @@ -1970,6 +1978,39 @@ class H5PCore { return $params; } + /** + * Determine if params contain math. + * + * @param {object} params - Parameters. + * @param {string} [mathPattern] - Regular expression to identify math. + * @param {boolean} [found] - Used for recursion. + * @return {boolean} True, if params contain math. + */ + private function containsMath ($params, $mathPattern = '/\$\$.+\$\$|\\\[.+\\\]|\\\(.+\\\)/', $found = false) { + foreach($params as $property => $value) { + if (gettype($value) === 'string') { + if (preg_match($mathPattern, $value) === 1) { + $found = true; + break; + } + } + if ($found === false) { + if (gettype($value) === 'array') { + for ($i = 0; $i < sizeof($value); $i++) { + $found = $this->containsMath($value[$i], $mathPattern, $found); + if ($found === true) { + break; + } + } + } + if (gettype($value) === 'object') { + $found = $this->containsMath($value, $mathPattern, $found); + } + } + } + return $found; + } + /** * Generate content slug * @@ -3204,6 +3245,23 @@ class H5PContentValidator { $this->dependencies = array(); } + /** + * Add MathDisplay. + */ + public function addMathDisplay($libraryName) { + $libSpec = H5PCore::libraryFromString($libraryName); + $library = $this->h5pC->loadLibrary($libSpec['machineName'], $libSpec['majorVersion'], $libSpec['minorVersion']); + $library['semantics'] = $this->h5pC->loadLibrarySemantics($libSpec['machineName'], $libSpec['majorVersion'], $libSpec['minorVersion']); + + $depKey = 'preloaded-' . $library['machineName']; + $this->dependencies[$depKey] = array( + 'library' => $library, + 'type' => 'preloaded' + ); + $this->nextWeight = $this->h5pC->findLibraryDependencies($this->dependencies, $library, $this->nextWeight); + $this->dependencies[$depKey]['weight'] = $this->nextWeight++; + } + /** * Get the flat dependency tree. * From 930f85e0fb3a0ee2bcc2496303dbca1c8012cb40 Mon Sep 17 00:00:00 2001 From: Thomas Horn Sivertsen Date: Mon, 18 Jun 2018 11:24:04 +0200 Subject: [PATCH 26/92] HFP-1806 Removed unused changes, fixed indentation --- h5p-default-storage.class.php | 14 +++++++------- h5p.classes.php | 3 +-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index d6d13ea..26e3e01 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -451,13 +451,13 @@ class H5PDefaultStorage implements \H5PFileStorage { return self::dirReady($this->path); } - /** - * Check if the file presave.js exists in the root of the library - * - * @param string $libraryFolder - * @param string $developmentPath - * @return bool - */ + /** + * Check if the file presave.js exists in the root of the library + * + * @param string $libraryFolder + * @param string $developmentPath + * @return bool + */ public function hasPresave($libraryFolder, $developmentPath = null) { $path = is_null($developmentPath) ? 'libraries' . DIRECTORY_SEPARATOR . $libraryFolder : $developmentPath; $filePath = realpath($this->path . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . 'presave.js'); diff --git a/h5p.classes.php b/h5p.classes.php index 650895c..d8ed62e 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2109,9 +2109,8 @@ class H5PCore { // Using content dependencies foreach ($dependencies as $dependency) { - $libraryName = H5PCore::libraryToString($dependency, TRUE); if (isset($dependency['path']) === FALSE) { - $dependency['path'] = 'libraries/' . $libraryName; + $dependency['path'] = 'libraries/' . H5PCore::libraryToString($dependency, TRUE); $dependency['preloadedJs'] = explode(',', $dependency['preloadedJs']); $dependency['preloadedCss'] = explode(',', $dependency['preloadedCss']); } From 17162f32effb253648231b60ecac550e54016c21 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 20 Jun 2018 16:25:27 +0200 Subject: [PATCH 27/92] HFP-2052 Fix undefined source --- js/h5p.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index dc50cd4..5579751 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1096,7 +1096,7 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { var dataset = { title: metadata.title, author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function(author) {return author.name + ' (' + author.role + ')';}).join(', ') : undefined, - source: '' + metadata.source + '', + source: (metadata.source) ? '' + metadata.source + '' : undefined, year: (metadata.yearFrom) ? (metadata.yearFrom + ((metadata.yearTo) ? '-' + metadata.yearTo: '')) : undefined, license: metadata.license, version: metadata.licenseVersion, From 2a53b7bb7b11f1d89239c38e1a85661c183cd210 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 22 Jun 2018 13:45:59 +0200 Subject: [PATCH 28/92] HFP-2059 Accept objects as library options, too Compound content types should be able to deactivate the metadata button for their subcontent types. This should be achievable by amending the options property of a library field. The options array can not either contains strings with library names or objects with the property "name" (mandatory) and further properties, one of them being "hasmetadata" --- h5p.classes.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 09f15ed..2d1c5e9 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -3692,12 +3692,24 @@ class H5PContentValidator { $value = NULL; return; } - if (!in_array($value->library, $semantics->options)) { + + // Check for array of objects or array of strings + if (is_object($semantics->options[0])) { + $getLibraryNames = function ($item) { + return $item->name; + }; + $libraryNames = array_map($getLibraryNames, $semantics->options); + } + else { + $libraryNames = $semantics->options; + } + + if (!in_array($value->library, $libraryNames)) { $message = NULL; // Create an understandable error message: $machineNameArray = explode(' ', $value->library); $machineName = $machineNameArray[0]; - foreach ($semantics->options as $semanticsLibrary) { + foreach ($libraryNames as $semanticsLibrary) { $semanticsMachineNameArray = explode(' ', $semanticsLibrary); $semanticsMachineName = $semanticsMachineNameArray[0]; if ($machineName === $semanticsMachineName) { @@ -3741,6 +3753,7 @@ class H5PContentValidator { if (isset($semantics->extraAttributes)) { $validKeys = array_merge($validKeys, $semantics->extraAttributes); } + $this->filterParams($value, $validKeys); if (isset($value->subContentId) && ! preg_match('/^\{?[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\}?$/', $value->subContentId)) { unset($value->subContentId); From 8190fe1d42e0b8bbf91ff6378ebfc20d2a57d3d2 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 25 Jun 2018 11:31:09 +0200 Subject: [PATCH 29/92] HFP-1896 Fix metadata form error handling --- h5p.classes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 2d1c5e9..95a9264 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4234,7 +4234,7 @@ class H5PContentValidator { (object) array( 'name' => 'yearFrom', 'type' => 'number', - 'label' => $this->h5pF->t('Years (from-to)'), + 'label' => $this->h5pF->t('Years (from)'), 'placeholder' => '1991', 'min' => '-9999', 'max' => '9999', @@ -4243,7 +4243,7 @@ class H5PContentValidator { (object) array( 'name' => 'yearTo', 'type' => 'number', - 'label' => 'hiddenLabel', + 'label' => $this->h5pF->t('Years (to)'), 'placeholder' => '1992', 'min' => '-9999', 'max' => '9999', From d113809e80aa36f44ec56399fe4ed179e81c49bc Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 25 Jun 2018 14:14:13 +0200 Subject: [PATCH 30/92] HFP-1905 Fix source and link in copyright view --- js/h5p.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 5579751..dd4716c 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1095,8 +1095,10 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { if (metadata && metadata.license !== undefined && metadata.license !== 'U') { var dataset = { title: metadata.title, - author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function(author) {return author.name + ' (' + author.role + ')';}).join(', ') : undefined, - source: (metadata.source) ? '' + metadata.source + '' : undefined, + author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function(author) { + return (author.role) ? author.name + ' (' + author.role + ')' : author.name; + }).join(', ') : undefined, + source: metadata.source, year: (metadata.yearFrom) ? (metadata.yearFrom + ((metadata.yearTo) ? '-' + metadata.yearTo: '')) : undefined, license: metadata.license, version: metadata.licenseVersion, @@ -1415,6 +1417,9 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { if (fieldName === 'license') { humanValue = humanizeLicense(copyright.license, copyright.version); } + if (fieldName === 'source') { + humanValue = (humanValue) ? '' + humanValue + '' : undefined; + } list.add(new H5P.Field(getLabel(fieldName), humanValue)); } } From 4d286b0bdbf527b65a9be0d73453b3c34b05a4b9 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 25 Jun 2018 14:14:51 +0200 Subject: [PATCH 31/92] Fix HFP-1905 Add CC for creative commons license names --- h5p.classes.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 95a9264..303e2b5 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4166,61 +4166,61 @@ class H5PContentValidator { 'options' => array( (object) array( 'value' => 'U', - 'label' => 'Undisclosed' + 'label' => $this->h5pF->t('Undisclosed') ), (object) array( 'value' => 'CC BY', - 'label' => 'Attribution', + 'label' => $this->h5pF->t('CC Attribution'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-SA', - 'label' => 'Attribution-ShareAlike', + 'label' => $this->h5pF->t('CC Attribution-ShareAlike'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-ND', - 'label' => 'Attribution-NoDerivs', + 'label' => $this->h5pF->t('CC Attribution-NoDerivs'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC', - 'label' => 'Attribution-NonCommercial', + 'label' => $this->h5pF->t('CC Attribution-NonCommercial'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC-SA', - 'label' => 'Attribution-NonCommercial-ShareAlike', + 'label' => $this->h5pF->t('CC Attribution-NonCommercial-ShareAlike'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC-ND', - 'label' => 'Attribution-NonCommercial-NoDerivs', + 'label' => $this->h5pF->t('CC Attribution-NonCommercial-NoDerivs'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC0 1.0', - 'label' => 'Public Domain Dedication' + 'label' => $this->h5pF->t('CC Public Domain Dedication') ), (object) array( 'value' => 'CC PDM', - 'label' => 'Public Domain Mark' + 'label' => $this->h5pF->t('Public Domain Mark') ), (object) array( 'value' => 'GNU GPL', - 'label' => 'General Public License v3' + 'label' => $this->h5pF->t('General Public License v3') ), (object) array( 'value' => 'PD', - 'label' => 'Public Domain' + 'label' => $this->h5pF->t('Public Domain') ), (object) array( 'value' => 'ODC PDDL', - 'label' => 'Public Domain Dedication and Licence' + 'label' => $this->h5pF->t('Public Domain Dedication and Licence') ), (object) array( 'value' => 'C', - 'label' => 'Copyright' + 'label' => $this->h5pF->t('Copyright') ) ) ), @@ -4252,7 +4252,7 @@ class H5PContentValidator { (object) array( 'name' => 'source', 'type' => 'text', - 'label' => 'Source', + 'label' => $this->h5pF->t('Source'), 'placeholder' => 'https://', 'optional' => TRUE, 'regexp' => array( @@ -4267,7 +4267,7 @@ class H5PContentValidator { 'type' => 'group', 'fields'=> array( (object) array( - 'label' => "Author's name", + 'label' => $this->h5pF->t("Author's name"), 'name' => "name", 'optional' => TRUE, 'type' => "text" From b1446e8d60dd5ae4e7cf9a12aadc7029de29c216 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 25 Jun 2018 15:59:17 +0200 Subject: [PATCH 32/92] HFP-2027 Add copy paste helpers to core --- js/h5p.js | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/js/h5p.js b/js/h5p.js index ef56834..00dc019 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -2041,9 +2041,129 @@ H5P.createTitle = function (rawTitle, maxLength) { contentUserDataAjax(contentId, dataId, subContentId, undefined, null); }; + /** + * Prepares the content parameters for storing in the clipboard. + * + * @class + * @param {Object} parameters The parameters for the content to store + * @param {string} [genericProperty] If only part of the parameters are generic, which part + * @param {string} [specificKey] If the parameters are specific, what content type does it fit + * @returns {Object} Ready for the clipboard + */ + H5P.ClipboardItem = function (parameters, genericProperty, specificKey) { + this.specific = parameters; + + if (genericProperty && specificKey) { + this.from = specificKey; + if (parameters[genericProperty]) { + this.generic = genericProperty; + } + } + + if (window.H5PEditor && H5PEditor.contentId) { + this.contentId = H5PEditor.contentId; + } + }; + + /** + * Store item in the H5P Clipboard. + * + * @param {H5P.ClipboardItem|*} clipboardItem + */ + H5P.clipboardify = function (clipboardItem) { + if (!(clipboardItem instanceof H5P.ClipboardItem)) { + clipboardItem = new H5P.ClipboardItem(clipboardItem); + } + + localStorage.setItem('h5pClipboard', JSON.stringify(clipboardItem)); + + // Trigger an event so all 'Paste' buttons may be enabled. + H5P.externalDispatcher.trigger('datainclipboard', {reset: false}); + }; + + /** + * Get item from the H5P Clipboard. + * + * @return {Object} + */ + H5P.getClipboard = function () { + var clipboardData = localStorage.getItem('h5pClipboard'); + if (!clipboardData) { + return; + } + + // Try to parse clipboard dat + try { + clipboardData = JSON.parse(clipboardData); + } + catch (err) { + console.error('Unable to parse JSON from clipboard.', err); + return; + } + + // Update file URLs + H5P.updateFileUrls(clipboardData.specific, function (path) { + var isTmpFile = (path.substr(-4, 4) === '#tmp'); + if (!isTmpFile && clipboardData.contentId) { + // Comes from existing content + + if (H5PEditor.contentId) { + // .. to existing content + return '../' + clipboardData.contentId + '/' + path; + } + else { + // .. to new content + return (H5PEditor.contentRelUrl ? H5PEditor.contentRelUrl : '../content/') + clipboardData.contentId + '/' + path; + } + } + return path; // Will automatically be looked for in tmp folder + }); + + if (clipboardData.from === undefined) { + clipboardData.generic = clipboardData.specific; + } + else if (clipboardData.generic) { + clipboardData.generic = clipboardData.specific[clipboardData.generic]; + } + + if (clipboardData.generic) { + // Avoid multiple content with same ID + delete clipboardData.generic.subContentId; + } + + return clipboardData; + }; + + /** + * Update file URLs. Useful when copying content. + * + * @param {object} params Reference + * @param {function} handler Modifies the path to work when pasted + */ + H5P.updateFileUrls = function (params, handler) { + for (var prop in params) { + if (params.hasOwnProperty(prop) && params[prop] instanceof Object) { + var obj = params[prop]; + if (obj.path !== undefined && obj.mime !== undefined) { + obj.path = handler(obj.path); + } + else { + H5P.updateFileUrls(obj, handler); + } + } + } + }; + // Init H5P when page is fully loadded $(document).ready(function () { + window.addEventListener('storage', function (event) { + // Pick up clipboard changes from other tabs + if (event.key === 'h5pClipboard') { + H5P.externalDispatcher.trigger('datainclipboard', {reset: event.newValue === null}); + } + }); + var ccVersions = { 'default': '4.0', '4.0': H5P.t('licenseCC40'), From d963a23a159d1d18a6d0c5992951744b33a79273 Mon Sep 17 00:00:00 2001 From: thomasmars Date: Tue, 26 Jun 2018 10:51:48 +0200 Subject: [PATCH 33/92] HFP-2074 Add optgroup to Creative Commons licenses in metadata dialog. --- h5p.classes.php | 78 ++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 303e2b5..b413920 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4169,42 +4169,48 @@ class H5PContentValidator { 'label' => $this->h5pF->t('Undisclosed') ), (object) array( - 'value' => 'CC BY', - 'label' => $this->h5pF->t('CC Attribution'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-SA', - 'label' => $this->h5pF->t('CC Attribution-ShareAlike'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-ND', - 'label' => $this->h5pF->t('CC Attribution-NoDerivs'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-NC', - 'label' => $this->h5pF->t('CC Attribution-NonCommercial'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-NC-SA', - 'label' => $this->h5pF->t('CC Attribution-NonCommercial-ShareAlike'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-NC-ND', - 'label' => $this->h5pF->t('CC Attribution-NonCommercial-NoDerivs'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC0 1.0', - 'label' => $this->h5pF->t('CC Public Domain Dedication') - ), - (object) array( - 'value' => 'CC PDM', - 'label' => $this->h5pF->t('Public Domain Mark') + 'type' => 'optgroup', + 'label' => $this->h5pF->t('Creative Commons'), + 'options' => [ + (object) array( + 'value' => 'CC BY', + 'label' => $this->h5pF->t('CC Attribution'), + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-SA', + 'label' => $this->h5pF->t('CC Attribution-ShareAlike'), + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-ND', + 'label' => $this->h5pF->t('CC Attribution-NoDerivs'), + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-NC', + 'label' => $this->h5pF->t('CC Attribution-NonCommercial'), + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-NC-SA', + 'label' => $this->h5pF->t('CC Attribution-NonCommercial-ShareAlike'), + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC BY-NC-ND', + 'label' => $this->h5pF->t('CC Attribution-NonCommercial-NoDerivs'), + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC0 1.0', + 'label' => $this->h5pF->t('CC Public Domain Dedication') + ), + (object) array( + 'value' => 'CC PDM', + 'label' => $this->h5pF->t('Public Domain Mark') + ), + ] ), (object) array( 'value' => 'GNU GPL', From d24fd0e66792e8a510c8616ed035bfec76b00e9a Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 26 Jun 2018 11:52:57 +0200 Subject: [PATCH 34/92] HFP-2072 Improve copy-paste API behavior --- js/h5p.js | 90 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 16 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 00dc019..b09759b 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -2051,17 +2051,49 @@ H5P.createTitle = function (rawTitle, maxLength) { * @returns {Object} Ready for the clipboard */ H5P.ClipboardItem = function (parameters, genericProperty, specificKey) { - this.specific = parameters; + var self = this; - if (genericProperty && specificKey) { - this.from = specificKey; - if (parameters[genericProperty]) { - this.generic = genericProperty; + /** + * Set relative dimensions when params contains a file with a width and a height. + * Very useful to be compatible with wysiwyg editors. + * + * @private + */ + var setDimensionsFromFile = function () { + if (!self.generic) { + return; } + var params = self.specific[self.generic]; + if (!params.params.file || !params.params.file.width || !params.params.file.height) { + return; + } + + self.width = 20; // % + self.height = (params.params.file.height / params.params.file.width) * self.width; + } + + if (!genericProperty) { + genericProperty = 'action'; + parameters = { + action: parameters + }; + } + + self.specific = parameters; + + if (genericProperty && parameters[genericProperty]) { + self.generic = genericProperty; + } + if (specificKey) { + self.from = specificKey; } if (window.H5PEditor && H5PEditor.contentId) { - this.contentId = H5PEditor.contentId; + self.contentId = H5PEditor.contentId; + } + + if (!self.specific.width && !self.specific.height) { + setDimensionsFromFile(); } }; @@ -2077,16 +2109,40 @@ H5P.createTitle = function (rawTitle, maxLength) { localStorage.setItem('h5pClipboard', JSON.stringify(clipboardItem)); + // Clear cache + parsedClipboard = null; + // Trigger an event so all 'Paste' buttons may be enabled. H5P.externalDispatcher.trigger('datainclipboard', {reset: false}); }; /** - * Get item from the H5P Clipboard. + * This is a cache for pasted data to prevent parsing multiple times. + * @type {Object} + */ + var parsedClipboard = null; + + /** + * Retrieve parsed clipboard data. * * @return {Object} */ H5P.getClipboard = function () { + if (!parsedClipboard) { + parsedClipboard = parseClipboard(); + } + + return parsedClipboard; + } + + /** + * Get item from the H5P Clipboard. + * + * @private + * @param {boolean} [skipUpdateFileUrls] + * @return {Object} + */ + var parseClipboard = function () { var clipboardData = localStorage.getItem('h5pClipboard'); if (!clipboardData) { return; @@ -2102,7 +2158,7 @@ H5P.createTitle = function (rawTitle, maxLength) { } // Update file URLs - H5P.updateFileUrls(clipboardData.specific, function (path) { + updateFileUrls(clipboardData.specific, function (path) { var isTmpFile = (path.substr(-4, 4) === '#tmp'); if (!isTmpFile && clipboardData.contentId) { // Comes from existing content @@ -2119,14 +2175,11 @@ H5P.createTitle = function (rawTitle, maxLength) { return path; // Will automatically be looked for in tmp folder }); - if (clipboardData.from === undefined) { - clipboardData.generic = clipboardData.specific; - } - else if (clipboardData.generic) { - clipboardData.generic = clipboardData.specific[clipboardData.generic]; - } if (clipboardData.generic) { + // Use reference instead of key + clipboardData.generic = clipboardData.specific[clipboardData.generic]; + // Avoid multiple content with same ID delete clipboardData.generic.subContentId; } @@ -2137,10 +2190,11 @@ H5P.createTitle = function (rawTitle, maxLength) { /** * Update file URLs. Useful when copying content. * + * @private * @param {object} params Reference * @param {function} handler Modifies the path to work when pasted */ - H5P.updateFileUrls = function (params, handler) { + var updateFileUrls = function (params, handler) { for (var prop in params) { if (params.hasOwnProperty(prop) && params[prop] instanceof Object) { var obj = params[prop]; @@ -2148,7 +2202,7 @@ H5P.createTitle = function (rawTitle, maxLength) { obj.path = handler(obj.path); } else { - H5P.updateFileUrls(obj, handler); + updateFileUrls(obj, handler); } } } @@ -2160,6 +2214,10 @@ H5P.createTitle = function (rawTitle, maxLength) { window.addEventListener('storage', function (event) { // Pick up clipboard changes from other tabs if (event.key === 'h5pClipboard') { + // Clear cache + parsedClipboard = null; + + // Trigger an event so all 'Paste' buttons may be enabled. H5P.externalDispatcher.trigger('datainclipboard', {reset: event.newValue === null}); } }); From 9432d80b232568891b7524c502ddb07b27d0bb19 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 27 Jun 2018 15:59:34 +0200 Subject: [PATCH 35/92] HFP-2075 Don't require protocol for metadata source field --- h5p.classes.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 303e2b5..9c46c87 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4254,11 +4254,7 @@ class H5PContentValidator { 'type' => 'text', 'label' => $this->h5pF->t('Source'), 'placeholder' => 'https://', - 'optional' => TRUE, - 'regexp' => array( - 'pattern' => '^http[s]?://.+', - 'modifiers' => 'i' - ) + 'optional' => TRUE ) ) ), From 972c7a13f66012092e3e4db89b76ee7e85166991 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 28 Jun 2018 11:44:01 +0200 Subject: [PATCH 36/92] HFP-2074 Remove CC prefix in license names Now that the optgroup feature is in place, the prefix is obsolete --- h5p.classes.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 184082e..9d21c50 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4174,37 +4174,37 @@ class H5PContentValidator { 'options' => [ (object) array( 'value' => 'CC BY', - 'label' => $this->h5pF->t('CC Attribution'), + 'label' => $this->h5pF->t('Attribution'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-SA', - 'label' => $this->h5pF->t('CC Attribution-ShareAlike'), + 'label' => $this->h5pF->t('Attribution-ShareAlike'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-ND', - 'label' => $this->h5pF->t('CC Attribution-NoDerivs'), + 'label' => $this->h5pF->t('Attribution-NoDerivs'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC', - 'label' => $this->h5pF->t('CC Attribution-NonCommercial'), + 'label' => $this->h5pF->t('Attribution-NonCommercial'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC-SA', - 'label' => $this->h5pF->t('CC Attribution-NonCommercial-ShareAlike'), + 'label' => $this->h5pF->t('Attribution-NonCommercial-ShareAlike'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC-ND', - 'label' => $this->h5pF->t('CC Attribution-NonCommercial-NoDerivs'), + 'label' => $this->h5pF->t('Attribution-NonCommercial-NoDerivs'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC0 1.0', - 'label' => $this->h5pF->t('CC Public Domain Dedication') + 'label' => $this->h5pF->t('Public Domain Dedication') ), (object) array( 'value' => 'CC PDM', From 14bcb913b77e38456df1f5f8430cc33fc02fb0b4 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 28 Jun 2018 18:21:30 +0200 Subject: [PATCH 37/92] HFP-1905 Include metadata in findCopyrights --- js/h5p.js | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 37259db..a92d24c 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -976,10 +976,10 @@ H5P.Dialog = function (name, title, content, $element) { * H5P instance to get copyright information for. * @param {Object} parameters * Parameters of the content instance. - * @param {Object} metadata - * Metadata of the content instance. * @param {number} contentId * Identifies the H5P content + * @param {Object} metadata + * Metadata of the content instance. * @returns {string} Copyright information. */ H5P.getCopyrights = function (instance, parameters, contentId, metadata) { @@ -1022,8 +1022,18 @@ H5P.getCopyrights = function (instance, parameters, contentId, metadata) { * To search for file objects in. * @param {number} contentId * Used to insert thumbnails for images. + * @param {Object} extras - Extras. + * @param {object} extras.metadata - Metadata + * @param {object} extras.machineName - Library name of some kind. + * Metadata of the content instance. */ -H5P.findCopyrights = function (info, parameters, contentId) { +H5P.findCopyrights = function (info, parameters, contentId, extras) { + // If extras are + if (extras) { + extras.params = parameters; + buildFromMetadata(extras, extras.machineName, contentId); + } + var lastContentTypeName; // Cycle through parameters for (var field in parameters) { @@ -1057,18 +1067,7 @@ H5P.findCopyrights = function (info, parameters, contentId) { H5P.findCopyrights(info, value, contentId); } else if (value instanceof Object) { - if (value.metadata) { - var metadataCopyrights = H5P.buildMetadataCopyrights(value.metadata, lastContentTypeName); - if (metadataCopyrights !== undefined) { - if (value.params && value.params.contentName === 'Image' && value.params.file) { - var path = value.params.file.path; - var width = value.params.file.width; - var height = value.params.file.height; - metadataCopyrights.setThumbnail(new H5P.Thumbnail(H5P.getPath(path, contentId), width, height)); - } - info.addMedia(metadataCopyrights); - } - } + buildFromMetadata(value, lastContentTypeName, contentId); // Check if object is a file with copyrights (old core) if (value.copyright === undefined || @@ -1089,6 +1088,21 @@ H5P.findCopyrights = function (info, parameters, contentId) { } } } + + function buildFromMetadata (data, name, contentId) { + if (data.metadata) { + const metadataCopyrights = H5P.buildMetadataCopyrights(data.metadata, name); + if (metadataCopyrights !== undefined) { + if (data.params && data.params.contentName === 'Image' && data.params.file) { + const path = data.params.file.path; + const width = data.params.file.width; + const height = data.params.file.height; + metadataCopyrights.setThumbnail(new H5P.Thumbnail(H5P.getPath(path, contentId), width, height)); + } + info.addMedia(metadataCopyrights); + } + } + } }; H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { @@ -1108,8 +1122,11 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { }).join(' / ') : undefined }; - if (contentTypeName && contentTypeName.indexOf('H5P.') === 0) { - contentTypeName = contentTypeName.substr(4); + if (contentTypeName) { + contentTypeName = contentTypeName + .split(' ')[0] + .replace(/^H5P\./, '') + .replace(/([a-z])([A-Z])/, '$1' + ' ' + '$2'); } return new H5P.MediaCopyright( From e241ec6963204bf019934ea39fab96637b4cf0be Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 28 Jun 2018 18:36:58 +0200 Subject: [PATCH 38/92] HFP-1905 Fix for LibraryNames with many elements --- js/h5p.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index a92d24c..1c73e70 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1126,7 +1126,7 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { contentTypeName = contentTypeName .split(' ')[0] .replace(/^H5P\./, '') - .replace(/([a-z])([A-Z])/, '$1' + ' ' + '$2'); + .replace(/([a-z])([A-Z])/g, '$1' + ' ' + '$2'); } return new H5P.MediaCopyright( From 10aaa38844720d02e2dfdc4de3bd4add0a187b5e Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 3 Jul 2018 10:34:26 +0200 Subject: [PATCH 39/92] HFP-2027 Fix copy-paste icons --- fonts/h5p-core-18.eot | Bin 6532 -> 0 bytes fonts/h5p-core-18.svg | 52 --------------------------------------- fonts/h5p-core-18.ttf | Bin 6384 -> 0 bytes fonts/h5p-core-18.woff | Bin 6460 -> 0 bytes fonts/h5p-core-19.eot | Bin 0 -> 7408 bytes fonts/h5p-core-19.svg | 54 +++++++++++++++++++++++++++++++++++++++++ fonts/h5p-core-19.ttf | Bin 0 -> 7228 bytes fonts/h5p-core-19.woff | Bin 0 -> 7304 bytes styles/h5p.css | 10 ++++---- 9 files changed, 59 insertions(+), 57 deletions(-) delete mode 100755 fonts/h5p-core-18.eot delete mode 100755 fonts/h5p-core-18.svg delete mode 100755 fonts/h5p-core-18.ttf delete mode 100755 fonts/h5p-core-18.woff create mode 100644 fonts/h5p-core-19.eot create mode 100644 fonts/h5p-core-19.svg create mode 100644 fonts/h5p-core-19.ttf create mode 100644 fonts/h5p-core-19.woff diff --git a/fonts/h5p-core-18.eot b/fonts/h5p-core-18.eot deleted file mode 100755 index eba9d6cc9f06607fcbb05b2ecb599df2cc5c3071..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6532 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 diff --git a/fonts/h5p-core-18.svg b/fonts/h5p-core-18.svg deleted file mode 100755 index 13da36e..0000000 --- a/fonts/h5p-core-18.svg +++ /dev/null @@ -1,52 +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" -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fonts/h5p-core-18.ttf b/fonts/h5p-core-18.ttf deleted file mode 100755 index 37f845e07c942ee71609208e7bb775764e297355..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6384 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 PBP&$QA9rW*|IGdyo!5`V diff --git a/fonts/h5p-core-19.eot b/fonts/h5p-core-19.eot new file mode 100644 index 0000000000000000000000000000000000000000..2348e291db555f2af7998c5b81e4f08dcc9383cb GIT binary patch literal 7408 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 literal 0 HcmV?d00001 diff --git a/fonts/h5p-core-19.svg b/fonts/h5p-core-19.svg new file mode 100644 index 0000000..a808aa6 --- /dev/null +++ b/fonts/h5p-core-19.svg @@ -0,0 +1,54 @@ + + + + + + +{ + "fontFamily": "h5p-core-18", + "description": "Font generated by IcoMoon.", + "majorVersion": 1, + "minorVersion": 1, + "version": "Version 1.1", + "fontId": "h5p-core-18", + "psName": "h5p-core-18", + "subFamily": "Regular", + "fullName": "h5p-core-18" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/h5p-core-19.ttf b/fonts/h5p-core-19.ttf new file mode 100644 index 0000000000000000000000000000000000000000..729aea457e0ccf764dd3b5e51c77dc0e23046273 GIT binary patch literal 7228 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 literal 0 HcmV?d00001 diff --git a/fonts/h5p-core-19.woff b/fonts/h5p-core-19.woff new file mode 100644 index 0000000000000000000000000000000000000000..be9ead2878e3737baf88c149e9a7a5bcf2916456 GIT binary patch literal 7304 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 F{|m*p3|#;K literal 0 HcmV?d00001 diff --git a/styles/h5p.css b/styles/h5p.css index f4324a7..e3ece5c 100644 --- 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-18.eot?cb8kvi'); - src: url('../fonts/h5p-core-18.eot?cb8kvi#iefix') format('embedded-opentype'), - url('../fonts/h5p-core-18.ttf?cb8kvi') format('truetype'), - url('../fonts/h5p-core-18.woff?cb8kvi') format('woff'), - url('../fonts/h5p-core-18.svg?cb8kvi#h5p') format('svg'); + src: url('../fonts/h5p-core-19.eot?cb8kvi'); + src: url('../fonts/h5p-core-19.eot?cb8kvi#iefix') format('embedded-opentype'), + url('../fonts/h5p-core-19.ttf?cb8kvi') format('truetype'), + url('../fonts/h5p-core-19.woff?cb8kvi') format('woff'), + url('../fonts/h5p-core-19.svg?cb8kvi#h5p') format('svg'); font-weight: normal; font-style: normal; } From b1a01b728de2d2d535be7240c8aa39996d45e851 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 11 Jul 2018 09:33:59 +0200 Subject: [PATCH 40/92] Fix update script bug that was introduced with adding the hasmetadata field --- js/h5p-content-upgrade-process.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index e440341..66e71ca 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -182,7 +182,7 @@ H5P.ContentUpgradeProcess = (function (Version) { // Look for available upgrades var usedLib = params.library.split(' ', 2); for (var i = 0; i < field.options.length; i++) { - var availableLib = field.options[i].split(' ', 2); + var availableLib = (typeof field.options === 'string') ? field.options[i].split(' ', 2) : field.options[i].name.split(' ', 2); if (availableLib[0] === usedLib[0]) { if (availableLib[1] === usedLib[1]) { return done(); // Same version From a295d7d434f34f22d3ea945099712e754dffa18b Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 12 Jul 2018 10:07:01 +0200 Subject: [PATCH 41/92] HFP-1925 Fix typo --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 9d21c50..22ea069 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4352,7 +4352,7 @@ class H5PContentValidator { 'name' => 'authorComments', 'type' => 'textarea', 'label' => $this->h5pF->t('Author comments'), - 'description' => $this->h5pF->t('Comments for the editor of the content (This text will not be published as a part of copyright info'), + 'description' => $this->h5pF->t('Comments for the editor of the content (This text will not be published as a part of copyright info)'), 'optional' => TRUE ) ) From 9ee5fb99078b3beb1cc98cf291c3569df9ec5caf Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 18 Jul 2018 10:45:14 +0200 Subject: [PATCH 42/92] HFP-2110 Refactor to have a plain setClipboard function Custom editors handled storing data to the clipboard on their own, so the datainclipboard trigger was missing for some functionality. H5P.clipboardify() however doesn't cope with additional information that the custom editors can deal with. Custom editors that prepare their own clipboard data can now use H5P.setClipboard() directly and set their data while other elements will be informed about the clipboard change. Also relevant for HFP-2111 and HFP-2112. --- js/h5p.js | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 1c73e70..a5bc3c5 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -2166,7 +2166,7 @@ H5P.createTitle = function (rawTitle, maxLength) { self.width = 20; // % self.height = (params.params.file.height / params.params.file.width) * self.width; - } + }; if (!genericProperty) { genericProperty = 'action'; @@ -2202,14 +2202,7 @@ H5P.createTitle = function (rawTitle, maxLength) { if (!(clipboardItem instanceof H5P.ClipboardItem)) { clipboardItem = new H5P.ClipboardItem(clipboardItem); } - - localStorage.setItem('h5pClipboard', JSON.stringify(clipboardItem)); - - // Clear cache - parsedClipboard = null; - - // Trigger an event so all 'Paste' buttons may be enabled. - H5P.externalDispatcher.trigger('datainclipboard', {reset: false}); + H5P.setClipboard(clipboardItem); }; /** @@ -2229,7 +2222,22 @@ H5P.createTitle = function (rawTitle, maxLength) { } return parsedClipboard; - } + }; + + /** + * Set item in the H5P Clipboard. + * + * @param {H5P.ClipboardItem|object} clipboardItem - Data to be set. + */ + H5P.setClipboard = function (clipboardItem) { + localStorage.setItem('h5pClipboard', JSON.stringify(clipboardItem)); + + // Clear cache + parsedClipboard = null; + + // Trigger an event so all 'Paste' buttons may be enabled. + H5P.externalDispatcher.trigger('datainclipboard', {reset: false}); + }; /** * Get item from the H5P Clipboard. From e22766157b0d1668f1ba2877918b9f79a7e1b020 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 23 Jul 2018 14:57:14 +0200 Subject: [PATCH 43/92] HFP-2133 Add Creative Commons acronyms --- h5p.classes.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 22ea069..a7fe2e0 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4174,41 +4174,41 @@ class H5PContentValidator { 'options' => [ (object) array( 'value' => 'CC BY', - 'label' => $this->h5pF->t('Attribution'), + 'label' => $this->h5pF->t('Attribution (CC BY)'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-SA', - 'label' => $this->h5pF->t('Attribution-ShareAlike'), + 'label' => $this->h5pF->t('Attribution-ShareAlike (CC BY-SA)'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-ND', - 'label' => $this->h5pF->t('Attribution-NoDerivs'), + 'label' => $this->h5pF->t('Attribution-NoDerivs (CC BY-ND)'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC', - 'label' => $this->h5pF->t('Attribution-NonCommercial'), + 'label' => $this->h5pF->t('Attribution-NonCommercial (CC BY-NC)'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC-SA', - 'label' => $this->h5pF->t('Attribution-NonCommercial-ShareAlike'), + 'label' => $this->h5pF->t('Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC BY-NC-ND', - 'label' => $this->h5pF->t('Attribution-NonCommercial-NoDerivs'), + 'label' => $this->h5pF->t('Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)'), 'versions' => $cc_versions ), (object) array( 'value' => 'CC0 1.0', - 'label' => $this->h5pF->t('Public Domain Dedication') + 'label' => $this->h5pF->t('Public Domain Dedication (CC 0)') ), (object) array( 'value' => 'CC PDM', - 'label' => $this->h5pF->t('Public Domain Mark') + 'label' => $this->h5pF->t('Public Domain Mark (PDM)') ), ] ), From 023613c1315f8835a3ba6d517e17e2a623f23d11 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 23 Jul 2018 15:26:09 +0200 Subject: [PATCH 44/92] HFP-2131 Fix additional space --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index a7fe2e0..9bca4ec 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4204,7 +4204,7 @@ class H5PContentValidator { ), (object) array( 'value' => 'CC0 1.0', - 'label' => $this->h5pF->t('Public Domain Dedication (CC 0)') + 'label' => $this->h5pF->t('Public Domain Dedication (CC0)') ), (object) array( 'value' => 'CC PDM', From d3a63dd756a1c3fbdb03e1907e1b73d04b863eff Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 23 Jul 2018 15:26:09 +0200 Subject: [PATCH 45/92] HFP-2133 Fix additional space --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index a7fe2e0..9bca4ec 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4204,7 +4204,7 @@ class H5PContentValidator { ), (object) array( 'value' => 'CC0 1.0', - 'label' => $this->h5pF->t('Public Domain Dedication (CC 0)') + 'label' => $this->h5pF->t('Public Domain Dedication (CC0)') ), (object) array( 'value' => 'CC PDM', From a55379adcfc88a3b0738b4ba537716b1ede90b4f Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Tue, 24 Jul 2018 10:36:43 +0200 Subject: [PATCH 46/92] Fix Array use --- js/h5p-content-upgrade-process.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index 66e71ca..81e55de 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -182,7 +182,7 @@ H5P.ContentUpgradeProcess = (function (Version) { // Look for available upgrades var usedLib = params.library.split(' ', 2); for (var i = 0; i < field.options.length; i++) { - var availableLib = (typeof field.options === 'string') ? field.options[i].split(' ', 2) : field.options[i].name.split(' ', 2); + var availableLib = (typeof field.options[i] === 'string') ? field.options[i].split(' ', 2) : field.options[i].name.split(' ', 2); if (availableLib[0] === usedLib[0]) { if (availableLib[1] === usedLib[1]) { return done(); // Same version From 62b6345c497b9740834257b243382a64a20811cc Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 25 Jul 2018 15:08:29 +0200 Subject: [PATCH 47/92] HFP-1871 Use param from MathDisplay library.json for configuration --- h5p.classes.php | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index b160abd..a61cb61 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1938,12 +1938,37 @@ class H5PCore { } $validator->validateLibrary($params, (object) array('options' => array($params->library))); - // Add MathDisplay. - // TODO: Get library name for MathDisplay dynamically - // TODO: Get regexp from MathDisplay config + // Add latest MathDisplay version if content contains math // TODO: Find out how to pass settings to MathDisplay - if ($this->containsMath($params->params)) { - $validator->addMathDisplay('H5P.MathDisplay 0.1'); + $libs = $this->h5pF->loadLibraries(); + + // Get latest version of MathDisplay library + $mathLibs = $libs['H5P.MathDisplay']; + foreach($mathLibs as $libVersion) { + if ($libVersion->major_version === $majorMax && $libVersion->minor_version > $minorMax) { + $mathLib = $libVersion; + } + else if ($libVersion->major_version > $majorMax) { + $mathLib = $libVersion; + } + } + + if (isset($mathLib)) { + // Retrieve regular expression from library.json + // Careful: \ will have to be escaped itself inside json + $libParams = $this->loadLibrary($mathLib->name, $mathLib->major_version, $mathLib->minor_version); + // Is there a general function to pass a "path" to that finds a parameter? + $libParamsTypes = $libParams['addTo']['content']['types']; + foreach($libParamsTypes as $type => $property) { + $regex = $property['text']['regex']; + if (isset($regex)) { + break; + } + } + + if ($this->containsMath($params->params, $regex)) { + $validator->addMathDisplay($mathLib->name . ' ' . $mathLib->major_version . '.' . $mathLib->minor_version); + } } $params = json_encode($params->params); @@ -1986,7 +2011,10 @@ class H5PCore { * @param {boolean} [found] - Used for recursion. * @return {boolean} True, if params contain math. */ - private function containsMath ($params, $mathPattern = '/\$\$.+\$\$|\\\[.+\\\]|\\\(.+\\\)/', $found = false) { + private function containsMath ($params, $mathPattern, $found = false) { + if (!isset($mathPattern) == NULL) { + $mathPattern = '/\$\$.+\$\$|\\\[.+\\\]|\\\(.+\\\)/'; + } foreach($params as $property => $value) { if (gettype($value) === 'string') { if (preg_match($mathPattern, $value) === 1) { From 942fd922bc40166e58ab5a4a85e93ac5e0665576 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 26 Jul 2018 10:37:16 +0200 Subject: [PATCH 48/92] HFP-1871 Add general function to get values from library json config --- h5p.classes.php | 53 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index a61cb61..f56a284 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1939,7 +1939,6 @@ class H5PCore { $validator->validateLibrary($params, (object) array('options' => array($params->library))); // Add latest MathDisplay version if content contains math - // TODO: Find out how to pass settings to MathDisplay $libs = $this->h5pF->loadLibraries(); // Get latest version of MathDisplay library @@ -1957,14 +1956,7 @@ class H5PCore { // Retrieve regular expression from library.json // Careful: \ will have to be escaped itself inside json $libParams = $this->loadLibrary($mathLib->name, $mathLib->major_version, $mathLib->minor_version); - // Is there a general function to pass a "path" to that finds a parameter? - $libParamsTypes = $libParams['addTo']['content']['types']; - foreach($libParamsTypes as $type => $property) { - $regex = $property['text']['regex']; - if (isset($regex)) { - break; - } - } + $regex = $this->retrieveValue($libParams, 'addTo.content.types.text.regex'); if ($this->containsMath($params->params, $regex)) { $validator->addMathDisplay($mathLib->name . ' ' . $mathLib->major_version . '.' . $mathLib->minor_version); @@ -2003,6 +1995,49 @@ class H5PCore { return $params; } + /** + * Retrieve a value from a nested mixed array structure. + * + * @param Array $params Array to be looked in. + * @param String $path Supposed path to the value. + * @param String [$delimiter='.'] Property delimiter within the path. + * @return Object|NULL The object found or NULL. + */ + private function retrieveValue ($params, $path, $delimiter='.') { + $path = explode($delimiter, $path); + + // Property not found + if (!isset($params[$path[0]])) { + return NULL; + } + + $first = $params[$path[0]]; + + // End of path, done + if (sizeof($path) === 1) { + return $first; + } + + // We cannot go deeper + if (!is_array($first)) { + return NULL; + } + + // Regular Array + if (isset($first[0])) { + foreach($first as $number => $object) { + $found = $this->retrieveValue($object, implode($delimiter, array_slice($path, 1))); + if (isset($found)) { + return $found; + } + } + return NULL; + } + + // Associative Array + return $this->retrieveValue($first, implode('.', array_slice($path, 1))); + } + /** * Determine if params contain math. * From 9314c55994783c58bfb023d23954102bae0cfed9 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Thu, 26 Jul 2018 19:34:46 +0200 Subject: [PATCH 49/92] HFP-1871 Clean code --- h5p.classes.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index f56a284..78d6d06 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1938,10 +1938,8 @@ class H5PCore { } $validator->validateLibrary($params, (object) array('options' => array($params->library))); - // Add latest MathDisplay version if content contains math - $libs = $this->h5pF->loadLibraries(); - // Get latest version of MathDisplay library + $libs = $this->h5pF->loadLibraries(); $mathLibs = $libs['H5P.MathDisplay']; foreach($mathLibs as $libVersion) { if ($libVersion->major_version === $majorMax && $libVersion->minor_version > $minorMax) { @@ -1952,6 +1950,7 @@ class H5PCore { } } + // Add latest MathDisplay version if content contains math if (isset($mathLib)) { // Retrieve regular expression from library.json // Careful: \ will have to be escaped itself inside json From 5be4ba1222e1b9e600fc2d22462a9db021b8b283 Mon Sep 17 00:00:00 2001 From: thomasmars Date: Mon, 30 Jul 2018 14:28:02 +0200 Subject: [PATCH 50/92] JI-781 Support regex in core for when CORS attributes is set --- js/h5p.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index a5bc3c5..f10df8f 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -2040,8 +2040,11 @@ H5P.createTitle = function (rawTitle, maxLength) { * @returns {string|null} Returns the string that should be set as crossorigin policy for elements or null if * no policy is set. */ - H5P.getCrossOrigin = function () { - return H5PIntegration.crossorigin ? H5PIntegration.crossorigin : null; + H5P.getCrossOrigin = function (url) { + var crossorigin = H5PIntegration.crossorigin; + var urlRegex = H5PIntegration.crossoriginRegex; + + return crossorigin && urlRegex && url.match(urlRegex) ? crossorigin : null; }; /** From d068b82ff56f4bad1cc870b7696e0cad833204ea Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Mon, 30 Jul 2018 14:52:36 +0200 Subject: [PATCH 51/92] HFP-1871 Fix sanitization of containsMath --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 78d6d06..3478f23 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2046,7 +2046,7 @@ class H5PCore { * @return {boolean} True, if params contain math. */ private function containsMath ($params, $mathPattern, $found = false) { - if (!isset($mathPattern) == NULL) { + if (!isset($mathPattern)) { $mathPattern = '/\$\$.+\$\$|\\\[.+\\\]|\\\(.+\\\)/'; } foreach($params as $property => $value) { From 64e5ab442437fcee36a733523224737b4e09f8bd Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 1 Aug 2018 10:55:13 +0200 Subject: [PATCH 52/92] HFP-1871 Fix missing variables --- h5p.classes.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 3478f23..2aeee76 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1941,12 +1941,18 @@ class H5PCore { // Get latest version of MathDisplay library $libs = $this->h5pF->loadLibraries(); $mathLibs = $libs['H5P.MathDisplay']; + + $majorMax = '0'; + $minorMax = '0'; foreach($mathLibs as $libVersion) { - if ($libVersion->major_version === $majorMax && $libVersion->minor_version > $minorMax) { + if ($libVersion->major_version === $majorMax && $libVersion->minor_version >= $minorMax) { $mathLib = $libVersion; + $minorMax = $libVersion->minor_version; } else if ($libVersion->major_version > $majorMax) { $mathLib = $libVersion; + $majorMax = $libVersion->major_version; + $minorMax = $libVersion->minor_version; } } From 6d50bae108c6bba42a7d7d9a43bf316c64e3f349 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Wed, 1 Aug 2018 17:30:30 +0200 Subject: [PATCH 53/92] HFP-1905 Complete validation for h5p.json --- h5p.classes.php | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 9bca4ec..fccb4de 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -620,22 +620,29 @@ class H5PValidator { private $h5pOptional = array( 'contentType' => '/^.{1,255}$/', + 'dynamicDependencies' => array( + 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', + 'majorVersion' => '/^[0-9]{1,5}$/', + 'minorVersion' => '/^[0-9]{1,5}$/', + ), // deprecated 'author' => '/^.{1,255}$/', 'authors' => array( 'name' => '/^.{1,255}$/', 'role' => '/^\w+$/', ), - 'license' => '/^(CC BY|CC BY-SA|CC BY-ND|CC BY-NC|CC BY-NC-SA|CC BY-NC-ND|GNU GPL|PD|ODC PDDL|CC PDM|U|C|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)$/', - 'licenseVersion' => '/^(1.0|2.0|2.5|3.0|4.0)$/', - 'source' => '/^(http[s]?://.+)$', + 'source' => '/^(http[s]?:\/\/.+)$/', + 'license' => '/^(CC BY|CC BY-SA|CC BY-ND|CC BY-NC|CC BY-NC-SA|CC BY-NC-ND|CC0 1\.0|GNU GPL|PD|ODC PDDL|CC PDM|U|C|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)$/', + 'licenseVersion' => '/^(1\.0|2\.0|2\.5|3\.0|4\.0)$/', + 'licenseExtras' => '/^.{1,5000}$/', 'yearsFrom' => '/^([0-9]{1,4})$/', 'yearsTo' => '/^([0-9]{1,4})$/', - 'dynamicDependencies' => array( - 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', - 'majorVersion' => '/^[0-9]{1,5}$/', - 'minorVersion' => '/^[0-9]{1,5}$/', + 'changes' => array( + 'date' => '/^[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{1,2}:[0-9]{2}:[0-9]{2}$/', + 'author' => '/^.{1,255}$/', + 'log' => '/^.{1,5000}$/' ), + 'authorComments' => '/^.{1,5000}$/', 'w' => '/^[0-9]{1,4}$/', 'h' => '/^[0-9]{1,4}$/', // deprecated @@ -1605,7 +1612,9 @@ Class H5PExport { foreach(array('authors', 'source', 'license', 'licenseVersion', 'licenseExtras' ,'yearFrom', 'yearTo', 'changes', 'authorComments') as $field) { if (isset($content['metadata'][$field])) { - $h5pJson[$field] = json_decode(json_encode($content['metadata'][$field], TRUE)); + if (($field !== 'authors' && $field !== 'changes') || (count($content['metadata'][$field]) > 0)) { + $h5pJson[$field] = json_decode(json_encode($content['metadata'][$field], TRUE)); + } } } From 3a8847424c84df370a1b63395bfaa9cb8beb9eb2 Mon Sep 17 00:00:00 2001 From: Bastian Heist Date: Sat, 4 Aug 2018 13:46:52 +0200 Subject: [PATCH 54/92] BUGFIX: Fix capitalization of the "Content-Type" HTTP header --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index fccb4de..d9b4e4b 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2881,7 +2881,7 @@ class H5PCore { */ private static function printJson($data, $status_code = NULL) { header('Cache-Control: no-cache'); - header('Content-type: application/json; charset=utf-8'); + header('Content-Type: application/json; charset=utf-8'); print json_encode($data); } From 6c4f904079ccbefbd6c3b4ac805b60dc264a2b39 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Thu, 23 Aug 2018 10:24:20 +0200 Subject: [PATCH 55/92] Generalize addons --- h5p.classes.php | 63 +++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 2aeee76..04249fc 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -101,6 +101,13 @@ interface H5PFrameworkInterface { */ public function getUploadedH5pPath(); + /** + * Load addon libraries + * + * @return array + */ + public function loadAddons(); + /** * Get a list of the current installed libraries * @@ -1609,7 +1616,7 @@ Class H5PExport { $library['minorVersion'] ); - if ($isDevLibrary !== NULL) { + if ($isDevLibrary !== NULL && isset($library['path'])) { $exportFolder = "/" . $library['path']; } } @@ -1938,33 +1945,20 @@ class H5PCore { } $validator->validateLibrary($params, (object) array('options' => array($params->library))); - // Get latest version of MathDisplay library - $libs = $this->h5pF->loadLibraries(); - $mathLibs = $libs['H5P.MathDisplay']; + // Handle addons: + $addons = $this->h5pF->loadAddons(); + foreach ($addons as $addon) { + $add_to = json_decode($addon['addTo']); - $majorMax = '0'; - $minorMax = '0'; - foreach($mathLibs as $libVersion) { - if ($libVersion->major_version === $majorMax && $libVersion->minor_version >= $minorMax) { - $mathLib = $libVersion; - $minorMax = $libVersion->minor_version; - } - else if ($libVersion->major_version > $majorMax) { - $mathLib = $libVersion; - $majorMax = $libVersion->major_version; - $minorMax = $libVersion->minor_version; - } - } + if (isset($add_to->content->types)) { + foreach($add_to->content->types as $type) { + if ($this->textAddonMatches($params->params, $type->text->regex)) { + $validator->addon($addon); - // Add latest MathDisplay version if content contains math - if (isset($mathLib)) { - // Retrieve regular expression from library.json - // Careful: \ will have to be escaped itself inside json - $libParams = $this->loadLibrary($mathLib->name, $mathLib->major_version, $mathLib->minor_version); - $regex = $this->retrieveValue($libParams, 'addTo.content.types.text.regex'); - - if ($this->containsMath($params->params, $regex)) { - $validator->addMathDisplay($mathLib->name . ' ' . $mathLib->major_version . '.' . $mathLib->minor_version); + // Addon can only be added once + break; + } + } } } @@ -2051,7 +2045,7 @@ class H5PCore { * @param {boolean} [found] - Used for recursion. * @return {boolean} True, if params contain math. */ - private function containsMath ($params, $mathPattern, $found = false) { + private function textAddonMatches($params, $mathPattern, $found = false) { if (!isset($mathPattern)) { $mathPattern = '/\$\$.+\$\$|\\\[.+\\\]|\\\(.+\\\)/'; } @@ -2065,14 +2059,14 @@ class H5PCore { if ($found === false) { if (gettype($value) === 'array') { for ($i = 0; $i < sizeof($value); $i++) { - $found = $this->containsMath($value[$i], $mathPattern, $found); + $found = $this->textAddonMatches($value[$i], $mathPattern, $found); if ($found === true) { break; } } } if (gettype($value) === 'object') { - $found = $this->containsMath($value, $mathPattern, $found); + $found = $this->textAddonMatches($value, $mathPattern, $found); } } } @@ -3314,12 +3308,13 @@ class H5PContentValidator { } /** - * Add MathDisplay. + * Add Addon library. */ - public function addMathDisplay($libraryName) { - $libSpec = H5PCore::libraryFromString($libraryName); - $library = $this->h5pC->loadLibrary($libSpec['machineName'], $libSpec['majorVersion'], $libSpec['minorVersion']); - $library['semantics'] = $this->h5pC->loadLibrarySemantics($libSpec['machineName'], $libSpec['majorVersion'], $libSpec['minorVersion']); + public function addon($library) { + // We need to run loadLibrarySemantics even if we have loadeed semantics from db, in case of + // 1) development foler + // 2) Invocation of semantics alter hook + $library['semantics'] = $this->h5pC->loadLibrarySemantics($library['machineName'], $library['majorVersion'], $library['minorVersion']); $depKey = 'preloaded-' . $library['machineName']; $this->dependencies[$depKey] = array( From 0c7df179a7a348c8387859311a8c0d8164ad9002 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Fri, 24 Aug 2018 12:55:42 +0200 Subject: [PATCH 56/92] Fix content upgrade bug --- h5p.classes.php | 2 +- js/h5p-content-upgrade-process.js | 2 +- js/h5p-content-upgrade.js | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index d9b4e4b..df958b0 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1806,7 +1806,7 @@ class H5PCore { public static $coreApi = array( 'majorVersion' => 1, - 'minorVersion' => 16 + 'minorVersion' => 19 ); public static $styles = array( 'styles/h5p.css', diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index 81e55de..5277eb6 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -26,7 +26,7 @@ H5P.ContentUpgradeProcess = (function (Version) { // Make extras possible to work with for (var element in extras) { - if (extras.hasOwnProperty(element)) { + if (extras[element] !== undefined) { try { extras[element] = JSON.parse(extras[element]); if (!(extras[element] instanceof Object)) { diff --git a/js/h5p-content-upgrade.js b/js/h5p-content-upgrade.js index 564a0a8..de17fc1 100644 --- a/js/h5p-content-upgrade.js +++ b/js/h5p-content-upgrade.js @@ -248,10 +248,9 @@ self.current++; self.working++; - // Rewrap metadata - if (self.extras.metadata) { - self.extras.metadata = self.extras.metadata[id]; - } + var extras = { + metadata: (self.extras.metadata && self.extras.metadata[id]) ? self.extras.metadata[id] : undefined + }; if (worker) { worker.postMessage({ @@ -261,11 +260,11 @@ oldVersion: info.library.version, newVersion: self.version.toString(), params: self.parameters[id], - extras: self.extras + extras: extras }); } else { - new H5P.ContentUpgradeProcess(info.library.name, new Version(info.library.version), self.version, self.parameters[id], self.extras, id, function loadLibrary(name, version, next) { + new H5P.ContentUpgradeProcess(info.library.name, new Version(info.library.version), self.version, self.parameters[id], extras, id, function loadLibrary(name, version, next) { self.loadLibrary(name, version, function (err, library) { if (library.upgradesScript) { self.loadScript(library.upgradesScript, function (err) { From ee0e97e17b04830424757cc2dc2b63c660df71ae Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 27 Aug 2018 13:52:33 +0200 Subject: [PATCH 57/92] HFP-1871 Skip loading semantics for addons --- h5p.classes.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 04249fc..22bf9ea 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -3311,11 +3311,6 @@ class H5PContentValidator { * Add Addon library. */ public function addon($library) { - // We need to run loadLibrarySemantics even if we have loadeed semantics from db, in case of - // 1) development foler - // 2) Invocation of semantics alter hook - $library['semantics'] = $this->h5pC->loadLibrarySemantics($library['machineName'], $library['majorVersion'], $library['minorVersion']); - $depKey = 'preloaded-' . $library['machineName']; $this->dependencies[$depKey] = array( 'library' => $library, From 6959f65022ca2b217d6ae0a614ce799a9f5e1101 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 27 Aug 2018 14:53:17 +0200 Subject: [PATCH 58/92] HFP-1871 Add library config as a generic feature --- h5p.classes.php | 8 ++++++++ js/h5p.js | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/h5p.classes.php b/h5p.classes.php index 511bf5b..134ffdb 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -108,6 +108,14 @@ interface H5PFrameworkInterface { */ public function loadAddons(); + /** + * Load config for libraries + * + * @param array $libraries + * @return array + */ + public function getLibraryConfig($libraries = NULL); + /** * Get a list of the current installed libraries * diff --git a/js/h5p.js b/js/h5p.js index f10df8f..38f6022 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -2242,6 +2242,17 @@ H5P.createTitle = function (rawTitle, maxLength) { H5P.externalDispatcher.trigger('datainclipboard', {reset: false}); }; + /** + * Get config for a library + * + * @param string machineName + * @return Object + */ + H5P.getLibraryConfig = function (machineName) { + var hasConfig = H5PIntegration.libraryConfig && H5PIntegration.libraryConfig[machineName]; + return hasConfig ? H5PIntegration.libraryConfig[machineName] : {}; + }; + /** * Get item from the H5P Clipboard. * From 8c374be79d3c13f75f561b567832697625f43abd Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 28 Aug 2018 08:08:56 +0200 Subject: [PATCH 59/92] HFP-1871 Improve addon feature --- h5p.classes.php | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 134ffdb..6f48f53 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1993,10 +1993,12 @@ class H5PCore { if (isset($add_to->content->types)) { foreach($add_to->content->types as $type) { - if ($this->textAddonMatches($params->params, $type->text->regex)) { + + if (isset($type->text->regex) && + $this->textAddonMatches($params->params, $type->text->regex)) { $validator->addon($addon); - // Addon can only be added once + // An addon shall only be added once break; } } @@ -2079,39 +2081,29 @@ class H5PCore { } /** - * Determine if params contain math. + * Determine if params contain any match. * * @param {object} params - Parameters. - * @param {string} [mathPattern] - Regular expression to identify math. + * @param {string} [pattern] - Regular expression to identify pattern. * @param {boolean} [found] - Used for recursion. - * @return {boolean} True, if params contain math. + * @return {boolean} True, if params matches pattern. */ - private function textAddonMatches($params, $mathPattern, $found = false) { - if (!isset($mathPattern)) { - $mathPattern = '/\$\$.+\$\$|\\\[.+\\\]|\\\(.+\\\)/'; - } - foreach($params as $property => $value) { - if (gettype($value) === 'string') { - if (preg_match($mathPattern, $value) === 1) { - $found = true; - break; - } + private function textAddonMatches($params, $pattern, $found = false) { + $type = gettype($params); + if ($type === 'string') { + if (preg_match($pattern, $params) === 1) { + return true; } - if ($found === false) { - if (gettype($value) === 'array') { - for ($i = 0; $i < sizeof($value); $i++) { - $found = $this->textAddonMatches($value[$i], $mathPattern, $found); - if ($found === true) { - break; - } - } - } - if (gettype($value) === 'object') { - $found = $this->textAddonMatches($value, $mathPattern, $found); + } + elseif ($type === 'array' || $type === 'object') { + foreach ($params as $value) { + $found = $this->textAddonMatches($value, $pattern, $found); + if ($found === true) { + return true; } } } - return $found; + return false; } /** From 768eb2a64bd268b74f7c7da2de50347e858efd9b Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 29 Aug 2018 14:30:31 +0200 Subject: [PATCH 60/92] Remove unkown todo comment --- h5p.classes.php | 1 - 1 file changed, 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 6f48f53..35be986 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -3682,7 +3682,6 @@ class H5PContentValidator { if (isset($file->copyright)) { $this->validateGroup($file->copyright, $this->getCopyrightSemantics()); - // TODO: We'll need to do something here about getMetadataSemantics() if we change the widgets } } From e094da76fa545a152f969707b2647e1b0050166d Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 30 Aug 2018 12:33:24 +0200 Subject: [PATCH 61/92] HFP-1900 Remove metadata/extras from upgrade for main content --- js/h5p-content-upgrade-process.js | 71 +++++++++++-------------------- js/h5p-content-upgrade-worker.js | 7 ++- js/h5p-content-upgrade.js | 25 ++++------- 3 files changed, 36 insertions(+), 67 deletions(-) diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index 5277eb6..3562d6d 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -7,7 +7,7 @@ H5P.ContentUpgradeProcess = (function (Version) { * @class * @namespace H5P */ - function ContentUpgradeProcess(name, oldVersion, newVersion, params, extras, id, loadLibrary, done) { + function ContentUpgradeProcess(name, oldVersion, newVersion, params, id, loadLibrary, done) { var self = this; // Make params possible to work with @@ -24,38 +24,28 @@ H5P.ContentUpgradeProcess = (function (Version) { }); } - // Make extras possible to work with - for (var element in extras) { - if (extras[element] !== undefined) { - try { - extras[element] = JSON.parse(extras[element]); - if (!(extras[element] instanceof Object)) { - throw true; - } - } - catch (event) { - return done({ - type: 'errorExtrasBroken', - id: id - }); - } - } - } - self.loadLibrary = loadLibrary; - self.upgrade(name, oldVersion, newVersion, params, extras, function (err, result) { + self.upgrade(name, oldVersion, newVersion, params, function (err, upgradedParams) { if (err) { return done(err); } - done(null, JSON.stringify(params), JSON.stringify(extras)); + done(null, JSON.stringify(upgradedParams)); }); } /** + * Run content upgrade. * + * @public + * @param {string} name + * @param {Version} oldVersion + * @param {Version} newVersion + * @param {Object} params Only for subcontent + * @param {Function} done Only for subcontent + * @param {Object} [metadata] Only for subcontent */ - ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, extras, done) { + ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, done, metadata) { var self = this; // Load library details and upgrade routines @@ -65,7 +55,7 @@ H5P.ContentUpgradeProcess = (function (Version) { } // Run upgrade routines on params - self.processParams(library, oldVersion, newVersion, params, extras, function (err, params, extras) { + self.processParams(library, oldVersion, newVersion, params, metadata, function (err, params, metadata) { if (err) { return done(err); } @@ -79,7 +69,7 @@ H5P.ContentUpgradeProcess = (function (Version) { next(err); }); }, function (err) { - done(err, params, extras); + done(err, params, metadata); }); }); }); @@ -95,7 +85,7 @@ H5P.ContentUpgradeProcess = (function (Version) { * @param {Object} params * @param {Function} next */ - ContentUpgradeProcess.prototype.processParams = function (library, oldVersion, newVersion, params, extras, next) { + ContentUpgradeProcess.prototype.processParams = function (library, oldVersion, newVersion, params, metadata, next) { if (H5PUpgrades[library.name] === undefined) { if (library.upgradesScript) { // Upgrades script should be loaded so the upgrades should be here. @@ -130,17 +120,11 @@ H5P.ContentUpgradeProcess = (function (Version) { try { unnecessaryWrapper(params, function (err, upgradedParams, upgradedExtras) { params = upgradedParams; - /** - * "params" (and "extras", e.g. metadata) flow through each update function and are changed - * if necessary. Since "extras" was added later and old update functions don't return - * it, we need to ignore undefined values here -- or change every single update function - * in all content types. - */ - if (upgradedExtras) { - extras = upgradedExtras; + if (upgradedExtras && upgradedExtras.metadata) { // Optional + metadata = upgradedExtras.metadata; } nextMinor(err); - }, extras); + }, {metadata: metadata}); } catch (err) { if (console && console.log) { @@ -154,7 +138,7 @@ H5P.ContentUpgradeProcess = (function (Version) { }, nextMajor); } }, function (err) { - next(err, params, extras); + next(err, params, metadata); }); }; @@ -195,24 +179,17 @@ H5P.ContentUpgradeProcess = (function (Version) { return done(); // Larger or same version that's available } - // Currently, we only use metadata as additional things that might need change - var extras = { - metadata: params.metadata - }; - // A newer version is available, upgrade params - return self.upgrade(availableLib[0], usedVer, availableVer, params.params, extras, function (err, upgraded, extras) { + return self.upgrade(availableLib[0], usedVer, availableVer, params.params, function (err, upgradedParams, upgradedMetadata) { if (!err) { params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor; - params.params = upgraded; - if (extras) { - if (extras.metadata) { - params.metadata = extras.metadata; - } + params.params = upgradedParams; + if (upgradedMetadata) { + params.metadata = upgradedMetadata; } } done(err, params); - }); + }, params.metadata); } } done(); diff --git a/js/h5p-content-upgrade-worker.js b/js/h5p-content-upgrade-worker.js index af0be94..26ad038 100644 --- a/js/h5p-content-upgrade-worker.js +++ b/js/h5p-content-upgrade-worker.js @@ -9,7 +9,7 @@ var libraryLoadedCallback; var messageHandlers = { newJob: function (job) { // Start new job - new H5P.ContentUpgradeProcess(job.name, new H5P.Version(job.oldVersion), new H5P.Version(job.newVersion), job.params, job.extras, job.id, function loadLibrary(name, version, next) { + new H5P.ContentUpgradeProcess(job.name, new H5P.Version(job.oldVersion), new H5P.Version(job.newVersion), job.params, job.id, function loadLibrary(name, version, next) { // TODO: Cache? postMessage({ action: 'loadLibrary', @@ -17,7 +17,7 @@ var messageHandlers = { version: version.toString() }); libraryLoadedCallback = next; - }, function done(err, result, extras) { + }, function done(err, result) { if (err) { // Return error postMessage({ @@ -33,8 +33,7 @@ var messageHandlers = { postMessage({ action: 'done', id: job.id, - params: result, - extras: extras + params: result }); }); }, diff --git a/js/h5p-content-upgrade.js b/js/h5p-content-upgrade.js index de17fc1..9c1e4ce 100644 --- a/js/h5p-content-upgrade.js +++ b/js/h5p-content-upgrade.js @@ -116,7 +116,7 @@ // Register message handlers var messageHandlers = { done: function (result) { - self.workDone(result.id, result.params, result.extras, this); + self.workDone(result.id, result.params, this); }, error: function (error) { self.printError(error.err); @@ -184,7 +184,7 @@ self.token = inData.token; // Start processing - self.processBatch(inData.params, {metadata: inData.metadata}); + self.processBatch(inData.params); }); }; @@ -202,7 +202,7 @@ * * @param {Object} parameters */ - ContentUpgrade.prototype.processBatch = function (parameters, extras) { + ContentUpgrade.prototype.processBatch = function (parameters) { var self = this; // Track upgraded params @@ -210,7 +210,6 @@ // Track current batch self.parameters = parameters; - self.extras = extras; // Create id mapping self.ids = []; @@ -248,10 +247,6 @@ self.current++; self.working++; - var extras = { - metadata: (self.extras.metadata && self.extras.metadata[id]) ? self.extras.metadata[id] : undefined - }; - if (worker) { worker.postMessage({ action: 'newJob', @@ -259,12 +254,11 @@ name: info.library.name, oldVersion: info.library.version, newVersion: self.version.toString(), - params: self.parameters[id], - extras: extras + params: self.parameters[id] }); } else { - new H5P.ContentUpgradeProcess(info.library.name, new Version(info.library.version), self.version, self.parameters[id], extras, id, function loadLibrary(name, version, next) { + new H5P.ContentUpgradeProcess(info.library.name, new Version(info.library.version), self.version, self.parameters[id], id, function loadLibrary(name, version, next) { self.loadLibrary(name, version, function (err, library) { if (library.upgradesScript) { self.loadScript(library.upgradesScript, function (err) { @@ -279,13 +273,13 @@ } }); - }, function done(err, result, extras) { + }, function done(err, result) { if (err) { self.printError(err); return ; } - self.workDone(id, result, extras); + self.workDone(id, result); }); } }; @@ -293,7 +287,7 @@ /** * */ - ContentUpgrade.prototype.workDone = function (id, result, extras, worker) { + ContentUpgrade.prototype.workDone = function (id, result, worker) { var self = this; self.working--; @@ -308,8 +302,7 @@ self.nextBatch({ libraryId: self.version.libraryId, token: self.token, - params: JSON.stringify(self.upgraded), - extras: extras + params: JSON.stringify(self.upgraded) }); } }; From e74fb6009ac980f4bb8f768865784cba33e22e7a Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Thu, 30 Aug 2018 12:35:46 +0200 Subject: [PATCH 62/92] Use title from metadata object when creating xAPI stmt --- js/h5p-x-api-event.js | 6 +++--- js/h5p.js | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/js/h5p-x-api-event.js b/js/h5p-x-api-event.js index c1d6c66..4cec937 100644 --- a/js/h5p-x-api-event.js +++ b/js/h5p-x-api-event.js @@ -133,9 +133,10 @@ H5P.XAPIEvent.prototype.setObject = function (instance) { } } else { - if (H5PIntegration && H5PIntegration.contents && H5PIntegration.contents['cid-' + instance.contentId].title) { + var content = H5P.getContentForInstance(instance.contentId); + if (content && content.metadata && content.metadata.title) { this.data.statement.object.definition.name = { - "en-US": H5P.createTitle(H5PIntegration.contents['cid-' + instance.contentId].title) + "en-US": H5P.createTitle(content.metadata.title) }; } } @@ -150,7 +151,6 @@ H5P.XAPIEvent.prototype.setObject = function (instance) { */ H5P.XAPIEvent.prototype.setContext = function (instance) { if (instance.parent && (instance.parent.contentId || instance.parent.subContentId)) { - var parentId = instance.parent.subContentId === undefined ? instance.parent.contentId : instance.parent.subContentId; this.data.statement.context = { "contextActivities": { "parent": [ diff --git a/js/h5p.js b/js/h5p.js index 38f6022..4330ade 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -2140,6 +2140,20 @@ H5P.createTitle = function (rawTitle, maxLength) { contentUserDataAjax(contentId, dataId, subContentId, undefined, null); }; + /** + * Function for getting content for a certain ID + * + * @param {number} contentId + * @return {Object} + */ + H5P.getContentForInstance = function (contentId) { + var key = 'cid-' + contentId; + var exists = H5PIntegration && H5PIntegration.contents && + H5PIntegration.contents[key]; + + return exists ? H5PIntegration.contents[key] : undefined; + }; + /** * Prepares the content parameters for storing in the clipboard. * From 775b45ab6c5733e7d52c5cf352b60bebe4913297 Mon Sep 17 00:00:00 2001 From: Oliver Tacke Date: Fri, 31 Aug 2018 09:32:23 +0200 Subject: [PATCH 63/92] HFP-2094 Add metadata property to validation of library.json --- h5p.classes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/h5p.classes.php b/h5p.classes.php index 6f48f53..024235c 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -680,6 +680,7 @@ class H5PValidator { '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|MPL|MPL2)$/', 'description' => '/^.{1,}$/', + 'metadata' => '/^(0|1)$/', 'dynamicDependencies' => array( 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'majorVersion' => '/^[0-9]{1,5}$/', From 97eab5c3ced65375ccfc48752a0fd9e762080f6e Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 31 Aug 2018 17:28:43 +0200 Subject: [PATCH 64/92] JI-848 Add changing main content metadata in upgrade (except for title!) --- js/h5p-content-upgrade-process.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index 3562d6d..d50a09c 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -25,12 +25,12 @@ H5P.ContentUpgradeProcess = (function (Version) { } self.loadLibrary = loadLibrary; - self.upgrade(name, oldVersion, newVersion, params, function (err, upgradedParams) { + self.upgrade(name, oldVersion, newVersion, params.params, params.metadata, function (err, upgradedParams, upgradedMetadata) { if (err) { return done(err); } - done(null, JSON.stringify(upgradedParams)); + done(null, JSON.stringify({params: upgradedParams, metadata: upgradedMetadata})); }); } @@ -41,11 +41,11 @@ H5P.ContentUpgradeProcess = (function (Version) { * @param {string} name * @param {Version} oldVersion * @param {Version} newVersion - * @param {Object} params Only for subcontent - * @param {Function} done Only for subcontent - * @param {Object} [metadata] Only for subcontent + * @param {Object} params + * @param {Object} metadata + * @param {Function} done */ - ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, done, metadata) { + ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, metadata, done) { var self = this; // Load library details and upgrade routines @@ -180,7 +180,7 @@ H5P.ContentUpgradeProcess = (function (Version) { } // A newer version is available, upgrade params - return self.upgrade(availableLib[0], usedVer, availableVer, params.params, function (err, upgradedParams, upgradedMetadata) { + return self.upgrade(availableLib[0], usedVer, availableVer, params.params, params.metadata, function (err, upgradedParams, upgradedMetadata) { if (!err) { params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor; params.params = upgradedParams; @@ -189,7 +189,7 @@ H5P.ContentUpgradeProcess = (function (Version) { } } done(err, params); - }, params.metadata); + }); } } done(); From e257e5ecff7e1a6478662b17ddce7198e8850109 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 10 Sep 2018 11:30:57 +0200 Subject: [PATCH 65/92] HFP-2185 Treat major & minor version as int (not string) --- js/h5p.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 4330ade..42f9519 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1657,8 +1657,8 @@ H5P.libraryFromString = function (library) { if (res !== null) { return { 'machineName': res[1], - 'majorVersion': res[2], - 'minorVersion': res[3] + 'majorVersion': parseInt(res[2]), + 'minorVersion': parseInt(res[3]) }; } else { From 425aac5d33a1fd4d1c14a2211e57948991b7bce4 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 11 Sep 2018 11:50:51 +0200 Subject: [PATCH 66/92] HFP-2183 Set metadata when saving library --- h5p-development.class.php | 3 +++ h5p.classes.php | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/h5p-development.class.php b/h5p-development.class.php index 6c891f9..4188d88 100644 --- a/h5p-development.class.php +++ b/h5p-development.class.php @@ -86,6 +86,9 @@ class H5PDevelopment { // Save/update library. $library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']); + if (!isset($library['metadata'])) { + $library['metadata'] = 0; + } $this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE); $library['path'] = 'development/' . $contents[$i]; diff --git a/h5p.classes.php b/h5p.classes.php index e4a5608..a93d8a4 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -210,6 +210,7 @@ interface H5PFrameworkInterface { * - minorVersion: The library's minorVersion * - patchVersion: The library's patchVersion * - runnable: 1 if the library is a content type, 0 otherwise + * - metadata: 1 if the library should support setting metadata (copyright etc) * - fullscreen(optional): 1 if the library supports fullscreen, 0 otherwise * - embedTypes(optional): list of supported embed types * - preloadedJs(optional): list of associative arrays containing: @@ -1440,6 +1441,9 @@ class H5PStorage { $library['saveDependencies'] = TRUE; // Save library meta data + if (!isset($library['metadata'])) { + $library['metadata'] = 0; + } $this->h5pF->saveLibraryData($library, $new); // Save library folder From 8067277e63804b8241acf46774b006627271bdca Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 17 Sep 2018 08:41:31 +0200 Subject: [PATCH 67/92] JI-857 Add support for optgroup type in semantics --- h5p.classes.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index a93d8a4..a3c5dca 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -3555,8 +3555,17 @@ class H5PContentValidator { // We have a strict set of options to choose from. $strict = TRUE; $options = array(); + foreach ($semantics->options as $option) { - $options[$option->value] = TRUE; + // Support optgroup - just flatten options into one + if (isset($option->type) && $option->type === 'optgroup') { + foreach ($option->options as $suboption) { + $options[$suboption->value] = TRUE; + } + } + elseif (isset($option->value)) { + $options[$option->value] = TRUE; + } } } From 74f9a8403414bbfa14024a24d7373f2e4adc280c Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 17 Sep 2018 08:45:18 +0200 Subject: [PATCH 68/92] JI-857 Make metadata semantics describe the actual data structure --- h5p.classes.php | 319 +++++++++++++++++++++++------------------------- 1 file changed, 153 insertions(+), 166 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index a3c5dca..4c22804 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4284,155 +4284,152 @@ class H5PContentValidator { ) ); - $semantics = (object) array( + $semantics = array( (object) array( - 'name' => 'copyright', - 'type' => 'group', - 'label' => $this->h5pF->t('Copyright information'), - 'fields' => array( + 'name' => 'title', + 'type' => 'text', + 'label' => $this->h5pF->t('Title'), + 'placeholder' => 'La Gioconda' + ), + (object) array( + 'name' => 'license', + 'type' => 'select', + 'label' => $this->h5pF->t('License'), + 'default' => 'U', + 'options' => array( (object) array( - 'name' => 'title', - 'type' => 'text', - 'label' => $this->h5pF->t('Title'), - 'placeholder' => 'La Gioconda' + 'value' => 'U', + 'label' => $this->h5pF->t('Undisclosed') ), (object) array( - 'name' => 'license', - 'type' => 'select', - 'label' => $this->h5pF->t('License'), - 'default' => 'U', - 'options' => array( + 'type' => 'optgroup', + 'label' => $this->h5pF->t('Creative Commons'), + 'options' => [ (object) array( - 'value' => 'U', - 'label' => $this->h5pF->t('Undisclosed') + 'value' => 'CC BY', + 'label' => $this->h5pF->t('Attribution (CC BY)'), + 'versions' => $cc_versions ), (object) array( - 'type' => 'optgroup', - 'label' => $this->h5pF->t('Creative Commons'), - 'options' => [ - (object) array( - 'value' => 'CC BY', - 'label' => $this->h5pF->t('Attribution (CC BY)'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-SA', - 'label' => $this->h5pF->t('Attribution-ShareAlike (CC BY-SA)'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-ND', - 'label' => $this->h5pF->t('Attribution-NoDerivs (CC BY-ND)'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-NC', - 'label' => $this->h5pF->t('Attribution-NonCommercial (CC BY-NC)'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-NC-SA', - 'label' => $this->h5pF->t('Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC BY-NC-ND', - 'label' => $this->h5pF->t('Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)'), - 'versions' => $cc_versions - ), - (object) array( - 'value' => 'CC0 1.0', - 'label' => $this->h5pF->t('Public Domain Dedication (CC0)') - ), - (object) array( - 'value' => 'CC PDM', - 'label' => $this->h5pF->t('Public Domain Mark (PDM)') - ), - ] + 'value' => 'CC BY-SA', + 'label' => $this->h5pF->t('Attribution-ShareAlike (CC BY-SA)'), + 'versions' => $cc_versions ), (object) array( - 'value' => 'GNU GPL', - 'label' => $this->h5pF->t('General Public License v3') + 'value' => 'CC BY-ND', + 'label' => $this->h5pF->t('Attribution-NoDerivs (CC BY-ND)'), + 'versions' => $cc_versions ), (object) array( - 'value' => 'PD', - 'label' => $this->h5pF->t('Public Domain') + 'value' => 'CC BY-NC', + 'label' => $this->h5pF->t('Attribution-NonCommercial (CC BY-NC)'), + 'versions' => $cc_versions ), (object) array( - 'value' => 'ODC PDDL', - 'label' => $this->h5pF->t('Public Domain Dedication and Licence') + 'value' => 'CC BY-NC-SA', + 'label' => $this->h5pF->t('Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)'), + 'versions' => $cc_versions ), (object) array( - 'value' => 'C', - 'label' => $this->h5pF->t('Copyright') - ) - ) + 'value' => 'CC BY-NC-ND', + 'label' => $this->h5pF->t('Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)'), + 'versions' => $cc_versions + ), + (object) array( + 'value' => 'CC0 1.0', + 'label' => $this->h5pF->t('Public Domain Dedication (CC0)') + ), + (object) array( + 'value' => 'CC PDM', + 'label' => $this->h5pF->t('Public Domain Mark (PDM)') + ), + ] ), (object) array( - 'name' => 'licenseVersion', - 'type' => 'select', - 'label' => $this->h5pF->t('License Version'), - 'options' => array(), - 'optional' => TRUE + 'value' => 'GNU GPL', + 'label' => $this->h5pF->t('General Public License v3') ), (object) array( - 'name' => 'yearFrom', - 'type' => 'number', - 'label' => $this->h5pF->t('Years (from)'), - 'placeholder' => '1991', - 'min' => '-9999', - 'max' => '9999', - 'optional' => TRUE + 'value' => 'PD', + 'label' => $this->h5pF->t('Public Domain') ), (object) array( - 'name' => 'yearTo', - 'type' => 'number', - 'label' => $this->h5pF->t('Years (to)'), - 'placeholder' => '1992', - 'min' => '-9999', - 'max' => '9999', - 'optional' => TRUE + 'value' => 'ODC PDDL', + 'label' => $this->h5pF->t('Public Domain Dedication and Licence') ), (object) array( - 'name' => 'source', - 'type' => 'text', - 'label' => $this->h5pF->t('Source'), - 'placeholder' => 'https://', - 'optional' => TRUE + 'value' => 'C', + 'label' => $this->h5pF->t('Copyright') ) ) ), (object) array( - 'name' => 'authorWidget', - 'type' => 'group', - 'fields'=> array( - (object) array( - 'label' => $this->h5pF->t("Author's name"), - 'name' => "name", - 'optional' => TRUE, - 'type' => "text" - ), - (object) array( - 'name' => 'role', - 'type' => 'select', - 'label' => $this->h5pF->t("Author's role"), - 'default' => 'Author', - 'options' => array( - (object) array( - 'value' => 'Author', - 'label' => $this->h5pF->t('Author') - ), - (object) array( - 'value' => 'Editor', - 'label' => $this->h5pF->t('Editor') - ), - (object) array( - 'value' => 'Licensee', - 'label' => $this->h5pF->t('Licensee') - ), - (object) array( - 'value' => 'Originator', - 'label' => $this->h5pF->t('Originator') + 'name' => 'licenseVersion', + 'type' => 'select', + 'label' => $this->h5pF->t('License Version'), + 'options' => $cc_versions, + 'optional' => TRUE + ), + (object) array( + 'name' => 'yearFrom', + 'type' => 'number', + 'label' => $this->h5pF->t('Years (from)'), + 'placeholder' => '1991', + 'min' => '-9999', + 'max' => '9999', + 'optional' => TRUE + ), + (object) array( + 'name' => 'yearTo', + 'type' => 'number', + 'label' => $this->h5pF->t('Years (to)'), + 'placeholder' => '1992', + 'min' => '-9999', + 'max' => '9999', + 'optional' => TRUE + ), + (object) array( + 'name' => 'source', + 'type' => 'text', + 'label' => $this->h5pF->t('Source'), + 'placeholder' => 'https://', + 'optional' => TRUE + ), + (object) array( + 'name' => 'authors', + 'type' => 'list', + 'field' => (object) array ( + 'name' => 'author', + 'type' => 'group', + 'fields'=> array( + (object) array( + 'label' => $this->h5pF->t("Author's name"), + 'name' => 'name', + 'optional' => TRUE, + 'type' => 'text' + ), + (object) array( + 'name' => 'role', + 'type' => 'select', + 'label' => $this->h5pF->t("Author's role"), + 'default' => 'Author', + 'options' => array( + (object) array( + 'value' => 'Author', + 'label' => $this->h5pF->t('Author') + ), + (object) array( + 'value' => 'Editor', + 'label' => $this->h5pF->t('Editor') + ), + (object) array( + 'value' => 'Licensee', + 'label' => $this->h5pF->t('Licensee') + ), + (object) array( + 'value' => 'Originator', + 'label' => $this->h5pF->t('Originator') + ) ) ) ) @@ -4440,60 +4437,50 @@ class H5PContentValidator { ), (object) array( 'name' => 'licenseExtras', - 'type' => 'textarea', + 'type' => 'text', + 'widget' => 'textarea', 'label' => $this->h5pF->t('License Extras'), 'optional' => TRUE, 'description' => $this->h5pF->t('Any additional information about the license') ), (object) array( - 'name' => 'changeLog', - 'type' => 'group', - 'expanded' => FALSE, - 'label' => $this->h5pF->t('Change Log'), - 'fields' => array( - (object) array ( - 'name' => 'changeLogForm', - 'type' => 'group', - 'label' => $this->h5pF->t('Question'), - 'expanded' => TRUE, - 'fields' => array( - (object) array( - 'name' => 'date', - 'type' => 'text', - 'label' => $this->h5pF->t('Date'), - 'optional' => TRUE - ), - (object) array( - 'name' => 'author', - 'type' => 'text', - 'label' => $this->h5pF->t('Changed by'), - 'optional' => TRUE - ), - (object) array( - 'name' => 'log', - 'type' => 'textarea', - 'label' => $this->h5pF->t('Description of change'), - 'placeholder' => $this->h5pF->t('Photo cropped, text changed, etc.'), - 'optional' => TRUE - ) + 'name' => 'changes', + 'type' => 'list', + 'field' => (object) array( + 'name' => 'change', + 'type' => 'group', + 'label' => $this->h5pF->t('Change Log'), + 'fields' => array( + (object) array( + 'name' => 'date', + 'type' => 'text', + 'label' => $this->h5pF->t('Date'), + 'optional' => TRUE + ), + (object) array( + 'name' => 'author', + 'type' => 'text', + 'label' => $this->h5pF->t('Changed by'), + 'optional' => TRUE + ), + (object) array( + 'name' => 'log', + 'type' => 'text', + 'widget' => 'textarea', + 'label' => $this->h5pF->t('Description of change'), + 'placeholder' => $this->h5pF->t('Photo cropped, text changed, etc.'), + 'optional' => TRUE ) ) ) ), - (object) array( + (object) array ( 'name' => 'authorComments', - 'label' => $this->h5pF->t('Additional Information'), - 'type' => 'group', - 'expanded' => FALSE, - 'fields' => array( - (object) array ( - 'name' => 'authorComments', - 'type' => 'textarea', - 'label' => $this->h5pF->t('Author comments'), - 'description' => $this->h5pF->t('Comments for the editor of the content (This text will not be published as a part of copyright info)'), - 'optional' => TRUE - ) - ) + 'type' => 'text', + 'widget' => 'textarea', + 'label' => $this->h5pF->t('Author comments'), + 'description' => $this->h5pF->t('Comments for the editor of the content (This text will not be published as a part of copyright info)'), + 'optional' => TRUE ) ); From 4fe8eca0f2fdd6848edbadbb0b535fb7c73c70c3 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 17 Sep 2018 08:58:38 +0200 Subject: [PATCH 69/92] JI-857 Validate metadata --- h5p.classes.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/h5p.classes.php b/h5p.classes.php index 4c22804..659f224 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1939,6 +1939,10 @@ class H5PCore { $content = $this->h5pF->loadContent($id); if ($content !== NULL) { + // Validate main content's metadata + $validator = new H5PContentValidator($this->h5pF, $this); + $validator->validateMetadata($content['metadata']); + $content['library'] = array( 'id' => $content['libraryId'], 'name' => $content['libraryName'], @@ -3367,6 +3371,21 @@ class H5PContentValidator { return $this->dependencies; } + /** + * Validate metadata + * + * @param array $metadata + */ + public function validateMetadata(&$metadata) { + $semantics = $this->getMetadataSemantics(); + + $group = (object)$metadata; + $this->validateGroup($group, (object) array( + 'type' => 'group', + 'fields' => $semantics, + ), FALSE); + } + /** * Validate given text value against text semantics. * @param $text @@ -3883,10 +3902,17 @@ class H5PContentValidator { $library = $this->libraries[$value->library]; } + // Validate parameters $this->validateGroup($value->params, (object) array( 'type' => 'group', 'fields' => $library['semantics'], ), FALSE); + + // Validate subcontent's metadata + if (isset($value->metadata)) { + $this->validateMetadata($value->metadata); + } + $validKeys = array('library', 'params', 'subContentId', 'metadata'); if (isset($semantics->extraAttributes)) { $validKeys = array_merge($validKeys, $semantics->extraAttributes); From eb766b0081cd75bf5303f6f6bb75481b4778608c Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 17 Sep 2018 11:47:05 +0200 Subject: [PATCH 70/92] HFP-2184 Add utility class for handling metadata --- h5p-metadata.class.php | 84 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 h5p-metadata.class.php diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php new file mode 100644 index 0000000..b60158f --- /dev/null +++ b/h5p-metadata.class.php @@ -0,0 +1,84 @@ + array( + 'type' => 'json' + ), + 'changes' => array( + 'type' => 'json' + ), + 'source' => array( + 'type' => 'text', + 'maxLength' => 255 + ), + 'license' => array( + 'type' => 'text', + 'maxLength' => 32 + ), + 'licenseVersion' => array( + 'type' => 'text', + 'maxLength' => 10 + ), + 'licenseExtras' => array( + 'type' => 'text', + 'maxLength' => 5000 + ), + 'authorComments' => array( + 'type' => 'text', + 'maxLength' => 5000 + ), + 'yearFrom' => array( + 'type' => 'int' + ), + 'yearTo' => array( + 'type' => 'int' + ) + ); + + /** + * Make the metadata into an associative array keyed by the property names + * @param mixed $metadata Array or object containing metadata + * @return array + */ + public static function toDBArray($metadata, &$types = array()) { + $fields = array(); + + if (!is_array($metadata)) { + $metadata = (array) $metadata; + } + + foreach (self::FIELDS as $key => $config) { + if (isset($metadata[$key])) { + $value = $metadata[$key]; + $db_field_name = strtolower(preg_replace('/(? $config['maxLength']) { + $value = mb_substr($value, 0, $config['maxLength']); + } + $types[] = '%s'; + break; + + case 'int': + $value = ($value !== null) ? intval($value): null; + $types[] = '%i'; + break; + + case 'json': + $value = json_encode($value); + $types[] = '%s'; + break; + } + + $fields[$db_field_name] = $value; + } + } + + return $fields; + } +} From 3d1c1cbe38a0febedd3076c901693a31c461f634 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 17 Sep 2018 12:53:09 +0200 Subject: [PATCH 71/92] Autoload metadata class --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fac6c53..9b1cc88 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,8 @@ "h5p-development.class.php", "h5p-file-storage.interface.php", "h5p-default-storage.class.php", - "h5p-event-base.class.php" + "h5p-event-base.class.php", + "h5p-metadata.class.php" ] } } From 2bf38c5b005b0752789e503bab11db17f5a2f41b Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Mon, 17 Sep 2018 12:54:21 +0200 Subject: [PATCH 72/92] Support title in metadata --- h5p-metadata.class.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index b60158f..eb0f529 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -5,6 +5,10 @@ abstract class H5PMetadata { const FIELDS = array( + 'title' => array( + 'type' => 'text', + 'maxLength' => 255 + ), 'authors' => array( 'type' => 'json' ), @@ -42,9 +46,11 @@ abstract class H5PMetadata { /** * Make the metadata into an associative array keyed by the property names * @param mixed $metadata Array or object containing metadata + * @param bool $include_title + * @param array $types * @return array */ - public static function toDBArray($metadata, &$types = array()) { + public static function toDBArray($metadata, $include_title = true, &$types = array()) { $fields = array(); if (!is_array($metadata)) { @@ -52,6 +58,11 @@ abstract class H5PMetadata { } foreach (self::FIELDS as $key => $config) { + + if ($key === 'title' && !$include_title) { + continue; + } + if (isset($metadata[$key])) { $value = $metadata[$key]; $db_field_name = strtolower(preg_replace('/(? Date: Mon, 17 Sep 2018 13:44:42 +0200 Subject: [PATCH 73/92] Add generic function for generating metadata JSON --- h5p-metadata.class.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index eb0f529..efff0cf 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -43,6 +43,28 @@ abstract class H5PMetadata { ) ); + /** + * JSON encode metadata + * + * @param object $content + * @return string + */ + public static function toJSON($content) { + // Note: deliberatly creating JSON string "manually" to improve performance + return + '{"title":' . (isset($content->title) ? json_encode($content->title) : 'null') . + ',"authors":' . (isset($content->authors) ? $content->authors : 'null') . + ',"source":' . (isset($content->source) ? '"' . $content->source . '"' : 'null') . + ',"license":' . (isset($content->license) ? '"' . $content->license . '"' : 'null') . + ',"licenseVersion":' . (isset($content->license_version) ? '"' . $content->license_version . '"' : 'null') . + ',"licenseExtras":' . (isset($content->license_extras) ? json_encode($content->license_extras) : 'null') . + ',"yearFrom":' . (isset($content->year_from) ? $content->year_from : 'null') . + ',"yearTo":' . (isset($content->year_to) ? $content->year_to : 'null') . + ',"changes":' . (isset($content->changes) ? $content->changes : 'null') . + ',"authorComments":' . (isset($content->author_comments) ? json_encode($content->author_comments) : 'null') . '}'; + } + + /** * Make the metadata into an associative array keyed by the property names * @param mixed $metadata Array or object containing metadata From d391d486c954425d681c2e6ad19752138712b29f Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 18 Sep 2018 14:26:36 +0200 Subject: [PATCH 74/92] HFP-2187 Only allow new license IDs --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 659f224..205a070 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -648,7 +648,7 @@ class H5PValidator { 'role' => '/^\w+$/', ), 'source' => '/^(http[s]?:\/\/.+)$/', - 'license' => '/^(CC BY|CC BY-SA|CC BY-ND|CC BY-NC|CC BY-NC-SA|CC BY-NC-ND|CC0 1\.0|GNU GPL|PD|ODC PDDL|CC PDM|U|C|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)$/', + 'license' => '/^(CC BY|CC BY-SA|CC BY-ND|CC BY-NC|CC BY-NC-SA|CC BY-NC-ND|CC0 1\.0|GNU GPL|PD|ODC PDDL|CC PDM|U|C)$/', 'licenseVersion' => '/^(1\.0|2\.0|2\.5|3\.0|4\.0)$/', 'licenseExtras' => '/^.{1,5000}$/', 'yearsFrom' => '/^([0-9]{1,4})$/', From 3566ac4141b1dac9deca91328bfce939cf9d5a0c Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 18 Sep 2018 15:02:12 +0200 Subject: [PATCH 75/92] HFP-2183 Default library.metadata property shall be 1 --- h5p-development.class.php | 2 +- h5p.classes.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/h5p-development.class.php b/h5p-development.class.php index 4188d88..1211517 100644 --- a/h5p-development.class.php +++ b/h5p-development.class.php @@ -87,7 +87,7 @@ class H5PDevelopment { // Save/update library. $library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']); if (!isset($library['metadata'])) { - $library['metadata'] = 0; + $library['metadata'] = 1; } $this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE); diff --git a/h5p.classes.php b/h5p.classes.php index 205a070..668d6fa 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1442,7 +1442,7 @@ class H5PStorage { // Save library meta data if (!isset($library['metadata'])) { - $library['metadata'] = 0; + $library['metadata'] = 1; } $this->h5pF->saveLibraryData($library, $new); From e2c8d6459a3461aa9bcc2d268d5e0d52d2e66d05 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Thu, 20 Sep 2018 11:14:50 +0200 Subject: [PATCH 76/92] Numbers use format '%d' - only relevant for WordPress --- h5p-metadata.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index efff0cf..29312fc 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -99,7 +99,7 @@ abstract class H5PMetadata { case 'int': $value = ($value !== null) ? intval($value): null; - $types[] = '%i'; + $types[] = '%d'; break; case 'json': From 841d24cab86950ab525daa53f1f8c6e7910cc187 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 21 Sep 2018 10:05:00 +0200 Subject: [PATCH 77/92] Capitalize metadata titles --- js/h5p.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index 42f9519..c97fe1e 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1131,7 +1131,7 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { return new H5P.MediaCopyright( dataset, - {type: 'Content type', licenseExtras: 'License extras', changes: 'Changelog'}, + {type: 'Content Type', licenseExtras: 'License Extras', changes: 'Changelog'}, ['type', 'title', 'license', 'author', 'year', 'source', 'licenseExtras', 'changes'], {type: contentTypeName} ); From d6e9c4ec095a133443994946f54e2ba03408be12 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 21 Sep 2018 13:08:11 +0200 Subject: [PATCH 78/92] Fix const not allowed in abstract class for PHP 5.3 --- h5p-metadata.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index 29312fc..9fb5686 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -4,7 +4,7 @@ */ abstract class H5PMetadata { - const FIELDS = array( + private static $fields = array( 'title' => array( 'type' => 'text', 'maxLength' => 255 @@ -79,7 +79,7 @@ abstract class H5PMetadata { $metadata = (array) $metadata; } - foreach (self::FIELDS as $key => $config) { + foreach (self::$fields as $key => $config) { if ($key === 'title' && !$include_title) { continue; From 73f41e2dbd4346a15ea7e6ab34d2a201d14a264d Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Fri, 21 Sep 2018 15:09:31 +0200 Subject: [PATCH 79/92] library.json: metadata -> metadataSettings --- h5p-development.class.php | 11 +++++++---- h5p-metadata.class.php | 20 ++++++++++++++++++++ h5p.classes.php | 18 ++++++++++++------ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/h5p-development.class.php b/h5p-development.class.php index 1211517..5823a51 100644 --- a/h5p-development.class.php +++ b/h5p-development.class.php @@ -84,11 +84,14 @@ class H5PDevelopment { // TODO: Validate props? Not really needed, is it? this is a dev site. - // Save/update library. $library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']); - if (!isset($library['metadata'])) { - $library['metadata'] = 1; - } + + // Convert metadataSettings values to boolean & json_encode it before saving + $library['metadataSettings'] = isset($library['metadataSettings']) ? + H5PMetadata::boolifyAndEncodeSettings($library['metadataSettings']) : + NULL; + + // Save/update library. $this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE); $library['path'] = 'development/' . $contents[$i]; diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index 9fb5686..4397d7e 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -114,4 +114,24 @@ abstract class H5PMetadata { return $fields; } + + /** + * The metadataSettings field in libraryJson uses 1 for true and 0 for false. + * Here we are converting these to booleans, and also doing JSON encoding. + * This is invoked before the library data is beeing inserted/updated to DB. + * + * @param array $metadataSettings + * @return string + */ + public static function boolifyAndEncodeSettings($metadataSettings) { + // Convert metadataSettings values to boolean + if (isset($metadataSettings['disable'])) { + $metadataSettings['disable'] = $metadataSettings['disable'] === 1; + } + if (isset($metadataSettings['disable'])) { + $metadataSettings['disableExtraTitleField'] = $metadataSettings['disableExtraTitleField'] === 1; + } + + return json_encode($metadataSettings); + } } diff --git a/h5p.classes.php b/h5p.classes.php index 668d6fa..c8e64ed 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -210,7 +210,9 @@ interface H5PFrameworkInterface { * - minorVersion: The library's minorVersion * - patchVersion: The library's patchVersion * - runnable: 1 if the library is a content type, 0 otherwise - * - metadata: 1 if the library should support setting metadata (copyright etc) + * - metadataSettings: Associative array containing: + * - disable: 1 if the library should not support setting metadata (copyright etc) + * - disableExtraTitleField: 1 if the library don't need the extra title field * - fullscreen(optional): 1 if the library supports fullscreen, 0 otherwise * - embedTypes(optional): list of supported embed types * - preloadedJs(optional): list of associative arrays containing: @@ -681,7 +683,10 @@ class H5PValidator { '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|MPL|MPL2)$/', 'description' => '/^.{1,}$/', - 'metadata' => '/^(0|1)$/', + 'metadataSettings' => array( + 'disable' => '/^(0|1)$/', + 'disableExtraTitleField' => '/^(0|1)$/' + ), 'dynamicDependencies' => array( 'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'majorVersion' => '/^[0-9]{1,5}$/', @@ -1440,10 +1445,11 @@ class H5PStorage { // Indicate that the dependencies of this library should be saved. $library['saveDependencies'] = TRUE; - // Save library meta data - if (!isset($library['metadata'])) { - $library['metadata'] = 1; - } + // Convert metadataSettings values to boolean & json_encode it before saving + $library['metadataSettings'] = isset($library['metadataSettings']) ? + H5PMetadata::boolifyAndEncodeSettings($library['metadataSettings']) : + NULL; + $this->h5pF->saveLibraryData($library, $new); // Save library folder From 64c8090d13cd72bdeab9f1d8af224e9c01ceed21 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 24 Sep 2018 10:18:13 +0200 Subject: [PATCH 80/92] Add storing of content type title in metadata --- h5p.classes.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 668d6fa..0fff359 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4507,7 +4507,12 @@ class H5PContentValidator { 'label' => $this->h5pF->t('Author comments'), 'description' => $this->h5pF->t('Comments for the editor of the content (This text will not be published as a part of copyright info)'), 'optional' => TRUE - ) + ), + (object) array( + 'name' => 'contentType', + 'type' => 'text', + 'widget' => 'none' + ), ); return $semantics; From 7762f903c8d00081d002947cc4d117adeb818c2f Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 24 Sep 2018 11:03:10 +0200 Subject: [PATCH 81/92] Add storing of content type title in metadata --- h5p.classes.php | 5 ++++- js/h5p.js | 17 +++-------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 6c525b3..79111ba 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -3307,7 +3307,10 @@ class H5PCore { 'licensePD' => $this->h5pF->t('Public Domain'), 'licenseCC010' => $this->h5pF->t('CC0 1.0 Universal (CC0 1.0) Public Domain Dedication'), 'licensePDM' => $this->h5pF->t('Public Domain Mark'), - 'licenseC' => $this->h5pF->t('Copyright') + 'licenseC' => $this->h5pF->t('Copyright'), + 'contentType' => $this->h5pF->t('Content Type'), + 'licenseExtras' => $this->h5pF->t('License Extras'), + 'changes' => $this->h5pF->t('Changelog'), ); } } diff --git a/js/h5p.js b/js/h5p.js index c97fe1e..db262d8 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1108,6 +1108,7 @@ H5P.findCopyrights = function (info, parameters, contentId, extras) { H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { if (metadata && metadata.license !== undefined && metadata.license !== 'U') { var dataset = { + contentType: metadata.contentType, title: metadata.title, author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function(author) { return (author.role) ? author.name + ' (' + author.role + ')' : author.name; @@ -1122,19 +1123,7 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { }).join(' / ') : undefined }; - if (contentTypeName) { - contentTypeName = contentTypeName - .split(' ')[0] - .replace(/^H5P\./, '') - .replace(/([a-z])([A-Z])/g, '$1' + ' ' + '$2'); - } - - return new H5P.MediaCopyright( - dataset, - {type: 'Content Type', licenseExtras: 'License Extras', changes: 'Changelog'}, - ['type', 'title', 'license', 'author', 'year', 'source', 'licenseExtras', 'changes'], - {type: contentTypeName} - ); + return new H5P.MediaCopyright(dataset); } }; @@ -1424,7 +1413,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { if (order === undefined) { // Set default order - order = ['title', 'author', 'year', 'source', 'license']; + order = ['contentType', 'title', 'license', 'author', 'year', 'source', 'licenseExtras', 'changes']; } for (var i = 0; i < order.length; i++) { From e75745f0dbaa69e124ef322a497149801b99e766 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Wed, 26 Sep 2018 10:30:50 +0200 Subject: [PATCH 82/92] metadataSettings returned as JSON, not string --- h5p-development.class.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/h5p-development.class.php b/h5p-development.class.php index 5823a51..a60262a 100644 --- a/h5p-development.class.php +++ b/h5p-development.class.php @@ -94,6 +94,9 @@ class H5PDevelopment { // Save/update library. $this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE); + // Need to decode it again, since it is served from here. + $library['metadataSettings'] = json_decode($library['metadataSettings']); + $library['path'] = 'development/' . $contents[$i]; $this->libraries[H5PDevelopment::libraryToString($library['machineName'], $library['majorVersion'], $library['minorVersion'])] = $library; } From 9081ca31288d1ee46055a88c4d5c0f35dfe184f1 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Thu, 4 Oct 2018 16:02:05 +0200 Subject: [PATCH 83/92] JI-857 Validate metadata properly --- h5p.classes.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 79111ba..5784264 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1947,7 +1947,7 @@ class H5PCore { if ($content !== NULL) { // Validate main content's metadata $validator = new H5PContentValidator($this->h5pF, $this); - $validator->validateMetadata($content['metadata']); + $content['metadata'] = $validator->validateMetadata($content['metadata']); $content['library'] = array( 'id' => $content['libraryId'], @@ -3384,8 +3384,9 @@ class H5PContentValidator { * Validate metadata * * @param array $metadata + * @return array Validated & filtered */ - public function validateMetadata(&$metadata) { + public function validateMetadata($metadata) { $semantics = $this->getMetadataSemantics(); $group = (object)$metadata; @@ -3393,6 +3394,8 @@ class H5PContentValidator { 'type' => 'group', 'fields' => $semantics, ), FALSE); + + return (array)$group; } /** @@ -3919,7 +3922,7 @@ class H5PContentValidator { // Validate subcontent's metadata if (isset($value->metadata)) { - $this->validateMetadata($value->metadata); + $value->metadata = $this->validateMetadata($value->metadata); } $validKeys = array('library', 'params', 'subContentId', 'metadata'); From 06985cca7cb2bbec9c1c6724ae4110d67d5acecd Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 5 Oct 2018 15:14:47 +0200 Subject: [PATCH 84/92] Fix h5p.json title field htmlencoded --- h5p.classes.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 5784264..6a6fac8 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1598,6 +1598,16 @@ Class H5PExport { $this->h5pC = $H5PCore; } + /** + * Reverts the replace pattern used by the text editor + * + * @param string $value + * @return string + */ + private static function revertH5PEditorTextEscape($value) { + return str_replace('<', '<', str_replace('>', '>', str_replace(''', "'", str_replace('"', '"', $value)))); + } + /** * Return path to h5p package. * @@ -1630,7 +1640,7 @@ Class H5PExport { // Build h5p.json, the en-/de-coding will ensure proper escaping $h5pJson = array ( - 'title' => $content['title'], + 'title' => self::revertH5PEditorTextEscape($content['title']), 'language' => (isset($content['language']) && strlen(trim($content['language'])) !== 0) ? $content['language'] : 'und', 'mainLibrary' => $content['library']['name'], 'embedTypes' => $embedTypes From 8dafa5db91a251534bc07fe5403063e8b6a55990 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 16 Oct 2018 09:53:11 +0200 Subject: [PATCH 85/92] Rename 'Change Log' to 'Changelog' --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 6a6fac8..3ab11db 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -4497,7 +4497,7 @@ class H5PContentValidator { 'field' => (object) array( 'name' => 'change', 'type' => 'group', - 'label' => $this->h5pF->t('Change Log'), + 'label' => $this->h5pF->t('Changelog'), 'fields' => array( (object) array( 'name' => 'date', From c4e52f4f29f3824dd69fbddeeb524ff1dade32aa Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Wed, 17 Oct 2018 13:53:38 +0200 Subject: [PATCH 86/92] Fixed bug for saving metadata settings --- h5p-metadata.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index 4397d7e..a7c42ac 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -128,7 +128,7 @@ abstract class H5PMetadata { if (isset($metadataSettings['disable'])) { $metadataSettings['disable'] = $metadataSettings['disable'] === 1; } - if (isset($metadataSettings['disable'])) { + if (isset($metadataSettings['disableExtraTitleField'])) { $metadataSettings['disableExtraTitleField'] = $metadataSettings['disableExtraTitleField'] === 1; } From 85d2e2eb75b48c75736bc6a67e5688971ff4a63f Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 23 Oct 2018 10:56:44 +0200 Subject: [PATCH 87/92] Don't filter params if library or params is not set --- h5p.classes.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/h5p.classes.php b/h5p.classes.php index 3ab11db..4e53fa3 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2000,6 +2000,10 @@ class H5PCore { return $content['filtered']; } + if (!(isset($content['library']) && isset($content['params']))) { + return NULL; + } + // Validate and filter against main library semantics. $validator = new H5PContentValidator($this->h5pF, $this); $params = (object) array( From e316eff18da913e979df15334a683048450aef6f Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 23 Oct 2018 11:10:06 +0200 Subject: [PATCH 88/92] Don't display empty metadata fields in copyright popup --- js/h5p.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index db262d8..1f9d052 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1418,7 +1418,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { for (var i = 0; i < order.length; i++) { var fieldName = order[i]; - if (copyright[fieldName] !== undefined) { + if (copyright[fieldName] !== undefined && copyright[fieldName] !== '') { var humanValue = copyright[fieldName]; if (fieldName === 'license') { humanValue = humanizeLicense(copyright.license, copyright.version); From 82b50fc2f16a4f85b50939f2d20074dc3c99c62d Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 23 Oct 2018 11:25:46 +0200 Subject: [PATCH 89/92] Fix code style --- js/h5p-confirmation-dialog.js | 2 +- js/h5p-content-type.js | 2 +- js/h5p-content-upgrade-process.js | 8 ++++---- js/h5p-content-upgrade-worker.js | 1 + js/h5p-content-upgrade.js | 2 +- js/h5p-data-view.js | 2 +- js/h5p-event-dispatcher.js | 8 ++++---- js/h5p-library-details.js | 29 +++++++++++++++-------------- js/h5p-library-list.js | 4 ++-- js/h5p-resizer.js | 4 ++-- js/h5p-utils.js | 5 +++-- js/h5p-x-api-event.js | 8 ++++---- js/h5p-x-api.js | 2 +- js/h5p.js | 31 ++++++++++++++++--------------- 14 files changed, 56 insertions(+), 52 deletions(-) diff --git a/js/h5p-confirmation-dialog.js b/js/h5p-confirmation-dialog.js index a6dd998..fa623c6 100644 --- a/js/h5p-confirmation-dialog.js +++ b/js/h5p-confirmation-dialog.js @@ -351,7 +351,7 @@ H5P.ConfirmationDialog = (function (EventDispatcher) { * * @param {number|null} minHeight */ - this.setViewPortMinimumHeight = function(minHeight) { + this.setViewPortMinimumHeight = function (minHeight) { var container = document.querySelector('.h5p-container') || document.body; container.style.minHeight = (typeof minHeight === 'number') ? (minHeight + 'px') : minHeight; }; diff --git a/js/h5p-content-type.js b/js/h5p-content-type.js index 8be8fcd..47c4d21 100644 --- a/js/h5p-content-type.js +++ b/js/h5p-content-type.js @@ -11,7 +11,7 @@ * @class * @augments H5P.EventDispatcher */ -H5P.ContentType = function (isRootLibrary, library) { +H5P.ContentType = function (isRootLibrary) { function ContentType() {} diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index d50a09c..e683902 100644 --- a/js/h5p-content-upgrade-process.js +++ b/js/h5p-content-upgrade-process.js @@ -127,10 +127,10 @@ H5P.ContentUpgradeProcess = (function (Version) { }, {metadata: metadata}); } catch (err) { - if (console && console.log) { - console.log("Error", err.stack); - console.log("Error", err.name); - console.log("Error", err.message); + if (console && console.error) { + console.error("Error", err.stack); + console.error("Error", err.name); + console.error("Error", err.message); } next(err); } diff --git a/js/h5p-content-upgrade-worker.js b/js/h5p-content-upgrade-worker.js index 26ad038..3507a35 100644 --- a/js/h5p-content-upgrade-worker.js +++ b/js/h5p-content-upgrade-worker.js @@ -1,3 +1,4 @@ +/* global importScripts */ var H5P = H5P || {}; importScripts('h5p-version.js', 'h5p-content-upgrade-process.js'); diff --git a/js/h5p-content-upgrade.js b/js/h5p-content-upgrade.js index 9c1e4ce..bb5244a 100644 --- a/js/h5p-content-upgrade.js +++ b/js/h5p-content-upgrade.js @@ -1,4 +1,4 @@ -/*jshint -W083 */ +/* global H5PAdminIntegration H5PUtils */ (function ($, Version) { var info, $container, librariesCache = {}, scriptsCache = {}; diff --git a/js/h5p-data-view.js b/js/h5p-data-view.js index 89080e0..2f708f8 100644 --- a/js/h5p-data-view.js +++ b/js/h5p-data-view.js @@ -1,3 +1,4 @@ +/* global H5PUtils */ var H5PDataView = (function ($) { /** @@ -198,7 +199,6 @@ var H5PDataView = (function ($) { * @param number col ID of column */ H5PDataView.prototype.createFacets = function (input, col) { - var self = this; var facets = ''; if (input instanceof Array) { diff --git a/js/h5p-event-dispatcher.js b/js/h5p-event-dispatcher.js index a6707b4..2027cf7 100644 --- a/js/h5p-event-dispatcher.js +++ b/js/h5p-event-dispatcher.js @@ -10,7 +10,7 @@ var H5P = window.H5P = window.H5P || {}; * @param {boolean} [extras.bubbles] * @param {boolean} [extras.external] */ -H5P.Event = function(type, data, extras) { +H5P.Event = function (type, data, extras) { this.type = type; this.data = data; var bubbles = false; @@ -34,7 +34,7 @@ H5P.Event = function(type, data, extras) { /** * Prevent this event from bubbling up to parent */ - this.preventBubbling = function() { + this.preventBubbling = function () { bubbles = false; }; @@ -44,7 +44,7 @@ H5P.Event = function(type, data, extras) { * @returns {boolean} * true if bubbling false otherwise */ - this.getBubbles = function() { + this.getBubbles = function () { return bubbles; }; @@ -54,7 +54,7 @@ H5P.Event = function(type, data, extras) { * @returns {boolean} * true if external and not already scheduled, otherwise false */ - this.scheduleForExternal = function() { + this.scheduleForExternal = function () { if (external && !scheduledForExternal) { scheduledForExternal = true; return true; diff --git a/js/h5p-library-details.js b/js/h5p-library-details.js index 77d366b..b5ee012 100644 --- a/js/h5p-library-details.js +++ b/js/h5p-library-details.js @@ -1,4 +1,5 @@ -var H5PLibraryDetails= H5PLibraryDetails || {}; +/* global H5PAdminIntegration H5PUtils */ +var H5PLibraryDetails = H5PLibraryDetails || {}; (function ($) { @@ -68,7 +69,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; */ H5PLibraryDetails.createContentTable = function () { // Remove it if it exists: - if(H5PLibraryDetails.$contentTable) { + if (H5PLibraryDetails.$contentTable) { H5PLibraryDetails.$contentTable.remove(); } @@ -77,10 +78,10 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; var i = (H5PLibraryDetails.currentPage*H5PLibraryDetails.PAGER_SIZE); var lastIndex = (i+H5PLibraryDetails.PAGER_SIZE); - if(lastIndex > H5PLibraryDetails.currentContent.length) { + if (lastIndex > H5PLibraryDetails.currentContent.length) { lastIndex = H5PLibraryDetails.currentContent.length; } - for(; i' + content.title + ''])); } @@ -97,7 +98,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; H5PLibraryDetails.$next = $(''); H5PLibraryDetails.$previous.on('click', function () { - if(H5PLibraryDetails.$previous.hasClass('disabled')) { + if (H5PLibraryDetails.$previous.hasClass('disabled')) { return; } @@ -107,7 +108,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; }); H5PLibraryDetails.$next.on('click', function () { - if(H5PLibraryDetails.$next.hasClass('disabled')) { + if (H5PLibraryDetails.$next.hasClass('disabled')) { return; } @@ -127,7 +128,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; H5PLibraryDetails.$pagerInfo.hide(); // User has updated the pageNumber - var pageNumerUpdated = function() { + var pageNumerUpdated = function () { var newPageNum = $gotoInput.val()-1; var intRegex = /^\d+$/; @@ -135,7 +136,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'}); // Check if input value is valid, and that it has actually changed - if(!(intRegex.test(newPageNum) && newPageNum >= 0 && newPageNum < H5PLibraryDetails.getNumPages() && newPageNum != H5PLibraryDetails.currentPage)) { + if (!(intRegex.test(newPageNum) && newPageNum >= 0 && newPageNum < H5PLibraryDetails.getNumPages() && newPageNum != H5PLibraryDetails.currentPage)) { return; } @@ -185,7 +186,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; H5PLibraryDetails.updatePager = function () { H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'}); - if(H5PLibraryDetails.getNumPages() > 0) { + if (H5PLibraryDetails.getNumPages() > 0) { var message = H5PUtils.translateReplace(H5PLibraryDetails.library.translations.pageXOfY, { '$x': (H5PLibraryDetails.currentPage+1), '$y': H5PLibraryDetails.getNumPages() @@ -211,7 +212,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; var searchString = $('.h5p-content-search > input').val(); // If search string same as previous, just do nothing - if(H5PLibraryDetails.currentFilter === searchString) { + if (H5PLibraryDetails.currentFilter === searchString) { return; } @@ -219,7 +220,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; // If empty search, use the complete list H5PLibraryDetails.currentContent = H5PLibraryDetails.library.content; } - else if(H5PLibraryDetails.filterCache[searchString]) { + else if (H5PLibraryDetails.filterCache[searchString]) { // If search is cached, no need to filter H5PLibraryDetails.currentContent = H5PLibraryDetails.filterCache[searchString]; } @@ -227,10 +228,10 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; var listToFilter = H5PLibraryDetails.library.content; // Check if we can filter the already filtered results (for performance) - if(searchString.length > 1 && H5PLibraryDetails.currentFilter === searchString.substr(0, H5PLibraryDetails.currentFilter.length)) { + if (searchString.length > 1 && H5PLibraryDetails.currentFilter === searchString.substr(0, H5PLibraryDetails.currentFilter.length)) { listToFilter = H5PLibraryDetails.currentContent; } - H5PLibraryDetails.currentContent = $.grep(listToFilter, function(content) { + H5PLibraryDetails.currentContent = $.grep(listToFilter, function (content) { return content.title && content.title.match(new RegExp(searchString, 'i')); }); } @@ -256,7 +257,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; $('input', H5PLibraryDetails.$search).on('change keypress paste input', function () { // Here we start the filtering // We wait at least 500 ms after last input to perform search - if(inputTimer) { + if (inputTimer) { clearTimeout(inputTimer); } diff --git a/js/h5p-library-list.js b/js/h5p-library-list.js index 1ee1bc8..344b736 100644 --- a/js/h5p-library-list.js +++ b/js/h5p-library-list.js @@ -1,4 +1,4 @@ -/*jshint multistr: true */ +/* global H5PAdminIntegration H5PUtils */ var H5PLibraryList = H5PLibraryList || {}; (function ($) { @@ -25,7 +25,7 @@ var H5PLibraryList = H5PLibraryList || {}; */ H5PLibraryList.createLibraryList = function (libraries) { var t = H5PAdminIntegration.l10n; - if(libraries.listData === undefined || libraries.listData.length === 0) { + if (libraries.listData === undefined || libraries.listData.length === 0) { return $('
' + t.NA + '
'); } diff --git a/js/h5p-resizer.js b/js/h5p-resizer.js index 772cafd..4ed65e0 100644 --- a/js/h5p-resizer.js +++ b/js/h5p-resizer.js @@ -21,7 +21,7 @@ iframe.style.width = '100%'; // Tell iframe that it needs to resize when our window resizes - var resize = function (event) { + var resize = function () { if (iframe.contentWindow) { // Limit resize calls to avoid flickering respond('resize'); @@ -64,7 +64,7 @@ * @param {Object} data Payload * @param {Function} respond Send a response to the iframe */ - actionHandlers.resize = function (iframe, data, respond) { + actionHandlers.resize = function (iframe, data) { // Resize iframe so all content is visible. Use scrollHeight to make sure we get everything iframe.style.height = data.scrollHeight + 'px'; }; diff --git a/js/h5p-utils.js b/js/h5p-utils.js index f47d1b6..b5aa333 100644 --- a/js/h5p-utils.js +++ b/js/h5p-utils.js @@ -1,3 +1,4 @@ +/* global H5PAdminIntegration*/ var H5PUtils = H5PUtils || {}; (function ($) { @@ -9,7 +10,7 @@ var H5PUtils = H5PUtils || {}; H5PUtils.createTable = function (headers) { var $table = $('
'); - if(headers) { + if (headers) { var $thead = $(''); var $tr = $(''); @@ -44,7 +45,7 @@ var H5PUtils = H5PUtils || {}; }; } - $('', value).appendTo($tr); + $('', value).appendTo($tr); }); return $tr; diff --git a/js/h5p-x-api-event.js b/js/h5p-x-api-event.js index 4cec937..e012ac1 100644 --- a/js/h5p-x-api-event.js +++ b/js/h5p-x-api-event.js @@ -217,7 +217,7 @@ H5P.XAPIEvent.prototype.setActor = function () { * @returns {number} * The max score, or null if not defined */ -H5P.XAPIEvent.prototype.getMaxScore = function() { +H5P.XAPIEvent.prototype.getMaxScore = function () { return this.getVerifiedStatementValue(['result', 'score', 'max']); }; @@ -227,7 +227,7 @@ H5P.XAPIEvent.prototype.getMaxScore = function() { * @returns {number} * The score, or null if not defined */ -H5P.XAPIEvent.prototype.getScore = function() { +H5P.XAPIEvent.prototype.getScore = function () { return this.getVerifiedStatementValue(['result', 'score', 'raw']); }; @@ -256,7 +256,7 @@ H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) { 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 @@ -267,7 +267,7 @@ H5P.XAPIEvent.prototype.isFromChild = function () { * @returns {*} * The value of the property if it is set, null otherwise. */ -H5P.XAPIEvent.prototype.getVerifiedStatementValue = function(keys) { +H5P.XAPIEvent.prototype.getVerifiedStatementValue = function (keys) { var val = this.data.statement; for (var i = 0; i < keys.length; i++) { if (val[keys[i]] === undefined) { diff --git a/js/h5p-x-api.js b/js/h5p-x-api.js index 8a27eb9..66971cd 100644 --- a/js/h5p-x-api.js +++ b/js/h5p-x-api.js @@ -92,7 +92,7 @@ H5P.EventDispatcher.prototype.triggerXAPIScored = function (score, maxScore, ver this.trigger(event); }; -H5P.EventDispatcher.prototype.setActivityStarted = function() { +H5P.EventDispatcher.prototype.setActivityStarted = function () { if (this.activityStartTime === undefined) { // Don't trigger xAPI events in the editor if (this.contentId !== undefined && diff --git a/js/h5p.js b/js/h5p.js index 1f9d052..7f63c1f 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -89,7 +89,7 @@ H5P.init = function (target) { } // H5Ps added in normal DIV. - var $containers = H5P.jQuery('.h5p-content:not(.h5p-initialized)', target).each(function () { + H5P.jQuery('.h5p-content:not(.h5p-initialized)', target).each(function () { var $element = H5P.jQuery(this).addClass('h5p-initialized'); var $container = H5P.jQuery('
').appendTo($element); var contentId = $element.data('content-id'); @@ -301,7 +301,7 @@ H5P.init = function (target) { }); // When resize has been prepared tell parent window to resize - H5P.communicator.on('resizePrepared', function (data) { + H5P.communicator.on('resizePrepared', function () { H5P.communicator.send('resize', { scrollHeight: document.body.scrollHeight }); @@ -494,7 +494,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body, forceSemiFull } var $container = $element; - var $classes, $iframe; + var $classes, $iframe, $body; if (body === undefined) { $body = H5P.$body; } @@ -1089,7 +1089,7 @@ H5P.findCopyrights = function (info, parameters, contentId, extras) { } } - function buildFromMetadata (data, name, contentId) { + function buildFromMetadata(data, name, contentId) { if (data.metadata) { const metadataCopyrights = H5P.buildMetadataCopyrights(data.metadata, name); if (metadataCopyrights !== undefined) { @@ -1105,12 +1105,12 @@ H5P.findCopyrights = function (info, parameters, contentId, extras) { } }; -H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { +H5P.buildMetadataCopyrights = function (metadata) { if (metadata && metadata.license !== undefined && metadata.license !== 'U') { var dataset = { contentType: metadata.contentType, title: metadata.title, - author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function(author) { + author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function (author) { return (author.role) ? author.name + ' (' + author.role + ')' : author.name; }).join(', ') : undefined, source: metadata.source, @@ -1118,7 +1118,7 @@ H5P.buildMetadataCopyrights = function (metadata, contentTypeName) { license: metadata.license, version: metadata.licenseVersion, licenseExtras: metadata.licenseExtras, - changes: (metadata.changes && metadata.changes.length > 0) ? metadata.changes.map(function(change) { + changes: (metadata.changes && metadata.changes.length > 0) ? metadata.changes.map(function (change) { return change.log + (change.author ? ', ' + change.author : '') + (change.date ? ', ' + change.date : ''); }).join(' / ') : undefined }; @@ -1181,10 +1181,10 @@ H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size) { updateEmbed(); // Select text and expand textareas - $dialog.find('.h5p-embed-code-container').each(function(index, value) { - H5P.jQuery(this).css('height', this.scrollHeight + 'px').focus(function() { - H5P.jQuery(this).select(); - }); + $dialog.find('.h5p-embed-code-container').each(function () { + H5P.jQuery(this).css('height', this.scrollHeight + 'px').focus(function () { + H5P.jQuery(this).select(); + }); }); $dialog.find('.h5p-embed-code-container').eq(0).select(); positionInner(); @@ -1201,7 +1201,7 @@ H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size) { $expander.addClass('h5p-open').text(H5P.t('hideAdvanced')); $content.show(); } - $dialog.find('.h5p-embed-code-container').each(function(index, value) { + $dialog.find('.h5p-embed-code-container').each(function () { H5P.jQuery(this).css('height', this.scrollHeight + 'px'); }); positionInner(); @@ -1614,7 +1614,8 @@ H5P.Coords = function (x, y, w, h) { this.y = x.y; this.w = x.w; this.h = x.h; - } else { + } + else { if (x !== undefined) { this.x = x; } @@ -1870,7 +1871,7 @@ H5P.on = function (instance, eventType, handler) { * @returns {string} UUID */ H5P.createUUID = function () { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (char) { var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8); return newChar.toString(16); }); @@ -2098,7 +2099,7 @@ H5P.createTitle = function (rawTitle, maxLength) { } preloadedData[options.subContentId][dataId] = data; - contentUserDataAjax(contentId, dataId, options.subContentId, function (error, data) { + contentUserDataAjax(contentId, dataId, options.subContentId, function (error) { if (options.errorCallback && error) { options.errorCallback(error); } From f661248b5a85a39a1dff5b99c71c40b24a23b591 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 23 Oct 2018 13:15:55 +0200 Subject: [PATCH 90/92] HFP-2329 Skip emty metadata fields for h5p.json --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 4e53fa3..b0099bc 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1647,7 +1647,7 @@ Class H5PExport { ); foreach(array('authors', 'source', 'license', 'licenseVersion', 'licenseExtras' ,'yearFrom', 'yearTo', 'changes', 'authorComments') as $field) { - if (isset($content['metadata'][$field])) { + if (isset($content['metadata'][$field]) && $content['metadata'][$field] !== '') { if (($field !== 'authors' && $field !== 'changes') || (count($content['metadata'][$field]) > 0)) { $h5pJson[$field] = json_decode(json_encode($content['metadata'][$field], TRUE)); } From 2e305ded71ecabd723b59fe570cfbfc953514272 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Tue, 23 Oct 2018 14:59:50 +0200 Subject: [PATCH 91/92] HFP-2328 Reset metadata fields if needed --- h5p-metadata.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index a7c42ac..6884e0e 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -85,13 +85,13 @@ abstract class H5PMetadata { continue; } - if (isset($metadata[$key])) { + if ($metadata[$key] === null || isset($metadata[$key])) { $value = $metadata[$key]; $db_field_name = strtolower(preg_replace('/(? $config['maxLength']) { + if ($value !== null && strlen($value) > $config['maxLength']) { $value = mb_substr($value, 0, $config['maxLength']); } $types[] = '%s'; @@ -103,7 +103,7 @@ abstract class H5PMetadata { break; case 'json': - $value = json_encode($value); + $value = ($value !== null) ? json_encode($value) : null; $types[] = '%s'; break; } From fd2ac997ef87a6fdbdfe61ba05d1cc125cf571c6 Mon Sep 17 00:00:00 2001 From: Paal Joergensen Date: Fri, 26 Oct 2018 08:45:20 +0200 Subject: [PATCH 92/92] HFP-2328 Fix bug --- h5p-metadata.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index 6884e0e..3a504e3 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -85,7 +85,7 @@ abstract class H5PMetadata { continue; } - if ($metadata[$key] === null || isset($metadata[$key])) { + if (array_key_exists($key, $metadata)) { $value = $metadata[$key]; $db_field_name = strtolower(preg_replace('/(?