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" ] } } 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-metadata.class.php b/h5p-metadata.class.php new file mode 100644 index 0000000..efff0cf --- /dev/null +++ b/h5p-metadata.class.php @@ -0,0 +1,117 @@ +<?php +/** + * Utility class for handling metadata + */ +abstract class H5PMetadata { + + const FIELDS = array( + 'title' => array( + 'type' => 'text', + 'maxLength' => 255 + ), + 'authors' => 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' + ) + ); + + /** + * 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 + * @param bool $include_title + * @param array $types + * @return array + */ + public static function toDBArray($metadata, $include_title = true, &$types = array()) { + $fields = array(); + + if (!is_array($metadata)) { + $metadata = (array) $metadata; + } + + 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('/(?<!^)[A-Z]/', '_$0', $key)); + + switch ($config['type']) { + case 'text': + if (strlen($value) > $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; + } +} diff --git a/h5p.classes.php b/h5p.classes.php index d9b4e4b..659f224 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -101,6 +101,21 @@ interface H5PFrameworkInterface { */ public function getUploadedH5pPath(); + /** + * Load addon libraries + * + * @return array + */ + 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 * @@ -195,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: @@ -665,6 +681,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}$/', @@ -1424,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 @@ -1642,7 +1662,7 @@ Class H5PExport { $library['minorVersion'] ); - if ($isDevLibrary !== NULL) { + if ($isDevLibrary !== NULL && isset($library['path'])) { $exportFolder = "/" . $library['path']; } } @@ -1806,7 +1826,7 @@ class H5PCore { public static $coreApi = array( 'majorVersion' => 1, - 'minorVersion' => 16 + 'minorVersion' => 19 ); public static $styles = array( 'styles/h5p.css', @@ -1919,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'], @@ -1971,6 +1995,25 @@ class H5PCore { } $validator->validateLibrary($params, (object) array('options' => array($params->library))); + // Handle addons: + $addons = $this->h5pF->loadAddons(); + foreach ($addons as $addon) { + $add_to = json_decode($addon['addTo']); + + if (isset($add_to->content->types)) { + foreach($add_to->content->types as $type) { + + if (isset($type->text->regex) && + $this->textAddonMatches($params->params, $type->text->regex)) { + $validator->addon($addon); + + // An addon shall only be added once + break; + } + } + } + } + $params = json_encode($params->params); // Update content dependencies. @@ -2003,6 +2046,75 @@ 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 any match. + * + * @param {object} params - Parameters. + * @param {string} [pattern] - Regular expression to identify pattern. + * @param {boolean} [found] - Used for recursion. + * @return {boolean} True, if params matches pattern. + */ + private function textAddonMatches($params, $pattern, $found = false) { + $type = gettype($params); + if ($type === 'string') { + if (preg_match($pattern, $params) === 1) { + return true; + } + } + elseif ($type === 'array' || $type === 'object') { + foreach ($params as $value) { + $found = $this->textAddonMatches($value, $pattern, $found); + if ($found === true) { + return true; + } + } + } + return false; + } + /** * Generate content slug * @@ -3237,6 +3349,19 @@ class H5PContentValidator { $this->dependencies = array(); } + /** + * Add Addon library. + */ + public function addon($library) { + $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. * @@ -3246,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 @@ -3434,8 +3574,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; + } } } @@ -3566,7 +3715,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 } } @@ -3754,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); @@ -4155,155 +4310,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') + ) ) ) ) @@ -4311,60 +4463,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 ) ); diff --git a/js/h5p-content-upgrade-process.js b/js/h5p-content-upgrade-process.js index 81e55de..d50a09c 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.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, extras, function (err, result) { + self.upgrade(name, oldVersion, newVersion, params.params, params.metadata, function (err, upgradedParams, upgradedMetadata) { if (err) { return done(err); } - done(null, JSON.stringify(params), JSON.stringify(extras)); + done(null, JSON.stringify({params: upgradedParams, metadata: upgradedMetadata})); }); } /** + * Run content upgrade. * + * @public + * @param {string} name + * @param {Version} oldVersion + * @param {Version} newVersion + * @param {Object} params + * @param {Object} metadata + * @param {Function} done */ - ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, extras, done) { + ContentUpgradeProcess.prototype.upgrade = function (name, oldVersion, newVersion, params, metadata, done) { 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,20 +179,13 @@ 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, params.metadata, 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); 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 7841be7..e0535a8 100644 --- a/js/h5p-content-upgrade.js +++ b/js/h5p-content-upgrade.js @@ -128,7 +128,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); @@ -196,7 +196,7 @@ self.token = inData.token; // Start processing - self.processBatch(inData.params, {metadata: inData.metadata}); + self.processBatch(inData.params); }); }; @@ -217,7 +217,7 @@ * * @param {Object} parameters */ - ContentUpgrade.prototype.processBatch = function (parameters, extras) { + ContentUpgrade.prototype.processBatch = function (parameters) { var self = this; // Track upgraded params @@ -225,7 +225,6 @@ // Track current batch self.parameters = parameters; - self.extras = extras; // Create id mapping self.ids = []; @@ -279,12 +278,11 @@ name: info.library.name, oldVersion: info.library.version, newVersion: self.version.toString(), - params: self.parameters[id], - extras: self.extras + params: self.parameters[id] }); } 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], id, function loadLibrary(name, version, next) { self.loadLibrary(name, version, function (err, library) { if (library.upgradesScript) { self.loadScript(library.upgradesScript, function (err) { @@ -299,13 +297,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); }); } }; @@ -313,7 +311,7 @@ /** * */ - ContentUpgrade.prototype.workDone = function (id, result, extras, worker) { + ContentUpgrade.prototype.workDone = function (id, result, worker) { var self = this; self.working--; @@ -335,8 +333,7 @@ self.nextBatch({ libraryId: self.version.libraryId, token: self.token, - params: JSON.stringify(self.upgraded), - extras: extras + params: JSON.stringify(self.upgraded) }); } }; 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 f10df8f..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 { @@ -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. * @@ -2242,6 +2256,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. *