Merge branch 'master' into contentupgrade

pull/48/head
Thomas Horn Sivertsen 2018-09-17 14:14:07 +02:00 committed by GitHub
commit 1d00397a24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 499 additions and 238 deletions

View File

@ -28,7 +28,8 @@
"h5p-development.class.php", "h5p-development.class.php",
"h5p-file-storage.interface.php", "h5p-file-storage.interface.php",
"h5p-default-storage.class.php", "h5p-default-storage.class.php",
"h5p-event-base.class.php" "h5p-event-base.class.php",
"h5p-metadata.class.php"
] ]
} }
} }

View File

@ -86,6 +86,9 @@ class H5PDevelopment {
// Save/update library. // Save/update library.
$library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']); $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); $this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE);
$library['path'] = 'development/' . $contents[$i]; $library['path'] = 'development/' . $contents[$i];

117
h5p-metadata.class.php Normal file
View File

@ -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;
}
}

View File

@ -101,6 +101,21 @@ interface H5PFrameworkInterface {
*/ */
public function getUploadedH5pPath(); 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 * Get a list of the current installed libraries
* *
@ -195,6 +210,7 @@ interface H5PFrameworkInterface {
* - minorVersion: The library's minorVersion * - minorVersion: The library's minorVersion
* - patchVersion: The library's patchVersion * - patchVersion: The library's patchVersion
* - runnable: 1 if the library is a content type, 0 otherwise * - 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 * - fullscreen(optional): 1 if the library supports fullscreen, 0 otherwise
* - embedTypes(optional): list of supported embed types * - embedTypes(optional): list of supported embed types
* - preloadedJs(optional): list of associative arrays containing: * - preloadedJs(optional): list of associative arrays containing:
@ -665,6 +681,7 @@ class H5PValidator {
'author' => '/^.{1,255}$/', 'author' => '/^.{1,255}$/',
'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT|GPL1|GPL2|GPL3|MPL|MPL2)$/', 'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT|GPL1|GPL2|GPL3|MPL|MPL2)$/',
'description' => '/^.{1,}$/', 'description' => '/^.{1,}$/',
'metadata' => '/^(0|1)$/',
'dynamicDependencies' => array( 'dynamicDependencies' => array(
'machineName' => '/^[\w0-9\-\.]{1,255}$/i', 'machineName' => '/^[\w0-9\-\.]{1,255}$/i',
'majorVersion' => '/^[0-9]{1,5}$/', 'majorVersion' => '/^[0-9]{1,5}$/',
@ -1424,6 +1441,9 @@ class H5PStorage {
$library['saveDependencies'] = TRUE; $library['saveDependencies'] = TRUE;
// Save library meta data // Save library meta data
if (!isset($library['metadata'])) {
$library['metadata'] = 0;
}
$this->h5pF->saveLibraryData($library, $new); $this->h5pF->saveLibraryData($library, $new);
// Save library folder // Save library folder
@ -1642,7 +1662,7 @@ Class H5PExport {
$library['minorVersion'] $library['minorVersion']
); );
if ($isDevLibrary !== NULL) { if ($isDevLibrary !== NULL && isset($library['path'])) {
$exportFolder = "/" . $library['path']; $exportFolder = "/" . $library['path'];
} }
} }
@ -1806,7 +1826,7 @@ class H5PCore {
public static $coreApi = array( public static $coreApi = array(
'majorVersion' => 1, 'majorVersion' => 1,
'minorVersion' => 16 'minorVersion' => 19
); );
public static $styles = array( public static $styles = array(
'styles/h5p.css', 'styles/h5p.css',
@ -1919,6 +1939,10 @@ class H5PCore {
$content = $this->h5pF->loadContent($id); $content = $this->h5pF->loadContent($id);
if ($content !== NULL) { if ($content !== NULL) {
// Validate main content's metadata
$validator = new H5PContentValidator($this->h5pF, $this);
$validator->validateMetadata($content['metadata']);
$content['library'] = array( $content['library'] = array(
'id' => $content['libraryId'], 'id' => $content['libraryId'],
'name' => $content['libraryName'], 'name' => $content['libraryName'],
@ -1971,6 +1995,25 @@ class H5PCore {
} }
$validator->validateLibrary($params, (object) array('options' => array($params->library))); $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); $params = json_encode($params->params);
// Update content dependencies. // Update content dependencies.
@ -2003,6 +2046,75 @@ class H5PCore {
return $params; 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 * Generate content slug
* *
@ -3237,6 +3349,19 @@ class H5PContentValidator {
$this->dependencies = array(); $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. * Get the flat dependency tree.
* *
@ -3246,6 +3371,21 @@ class H5PContentValidator {
return $this->dependencies; 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. * Validate given text value against text semantics.
* @param $text * @param $text
@ -3434,8 +3574,17 @@ class H5PContentValidator {
// We have a strict set of options to choose from. // We have a strict set of options to choose from.
$strict = TRUE; $strict = TRUE;
$options = array(); $options = array();
foreach ($semantics->options as $option) { 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)) { if (isset($file->copyright)) {
$this->validateGroup($file->copyright, $this->getCopyrightSemantics()); $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]; $library = $this->libraries[$value->library];
} }
// Validate parameters
$this->validateGroup($value->params, (object) array( $this->validateGroup($value->params, (object) array(
'type' => 'group', 'type' => 'group',
'fields' => $library['semantics'], 'fields' => $library['semantics'],
), FALSE); ), FALSE);
// Validate subcontent's metadata
if (isset($value->metadata)) {
$this->validateMetadata($value->metadata);
}
$validKeys = array('library', 'params', 'subContentId', 'metadata'); $validKeys = array('library', 'params', 'subContentId', 'metadata');
if (isset($semantics->extraAttributes)) { if (isset($semantics->extraAttributes)) {
$validKeys = array_merge($validKeys, $semantics->extraAttributes); $validKeys = array_merge($validKeys, $semantics->extraAttributes);
@ -4155,155 +4310,152 @@ class H5PContentValidator {
) )
); );
$semantics = (object) array( $semantics = array(
(object) array( (object) array(
'name' => 'copyright', 'name' => 'title',
'type' => 'group', 'type' => 'text',
'label' => $this->h5pF->t('Copyright information'), 'label' => $this->h5pF->t('Title'),
'fields' => array( 'placeholder' => 'La Gioconda'
),
(object) array(
'name' => 'license',
'type' => 'select',
'label' => $this->h5pF->t('License'),
'default' => 'U',
'options' => array(
(object) array( (object) array(
'name' => 'title', 'value' => 'U',
'type' => 'text', 'label' => $this->h5pF->t('Undisclosed')
'label' => $this->h5pF->t('Title'),
'placeholder' => 'La Gioconda'
), ),
(object) array( (object) array(
'name' => 'license', 'type' => 'optgroup',
'type' => 'select', 'label' => $this->h5pF->t('Creative Commons'),
'label' => $this->h5pF->t('License'), 'options' => [
'default' => 'U',
'options' => array(
(object) array( (object) array(
'value' => 'U', 'value' => 'CC BY',
'label' => $this->h5pF->t('Undisclosed') 'label' => $this->h5pF->t('Attribution (CC BY)'),
'versions' => $cc_versions
), ),
(object) array( (object) array(
'type' => 'optgroup', 'value' => 'CC BY-SA',
'label' => $this->h5pF->t('Creative Commons'), 'label' => $this->h5pF->t('Attribution-ShareAlike (CC BY-SA)'),
'options' => [ 'versions' => $cc_versions
(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)')
),
]
), ),
(object) array( (object) array(
'value' => 'GNU GPL', 'value' => 'CC BY-ND',
'label' => $this->h5pF->t('General Public License v3') 'label' => $this->h5pF->t('Attribution-NoDerivs (CC BY-ND)'),
'versions' => $cc_versions
), ),
(object) array( (object) array(
'value' => 'PD', 'value' => 'CC BY-NC',
'label' => $this->h5pF->t('Public Domain') 'label' => $this->h5pF->t('Attribution-NonCommercial (CC BY-NC)'),
'versions' => $cc_versions
), ),
(object) array( (object) array(
'value' => 'ODC PDDL', 'value' => 'CC BY-NC-SA',
'label' => $this->h5pF->t('Public Domain Dedication and Licence') 'label' => $this->h5pF->t('Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)'),
'versions' => $cc_versions
), ),
(object) array( (object) array(
'value' => 'C', 'value' => 'CC BY-NC-ND',
'label' => $this->h5pF->t('Copyright') '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( (object) array(
'name' => 'licenseVersion', 'value' => 'GNU GPL',
'type' => 'select', 'label' => $this->h5pF->t('General Public License v3')
'label' => $this->h5pF->t('License Version'),
'options' => array(),
'optional' => TRUE
), ),
(object) array( (object) array(
'name' => 'yearFrom', 'value' => 'PD',
'type' => 'number', 'label' => $this->h5pF->t('Public Domain')
'label' => $this->h5pF->t('Years (from)'),
'placeholder' => '1991',
'min' => '-9999',
'max' => '9999',
'optional' => TRUE
), ),
(object) array( (object) array(
'name' => 'yearTo', 'value' => 'ODC PDDL',
'type' => 'number', 'label' => $this->h5pF->t('Public Domain Dedication and Licence')
'label' => $this->h5pF->t('Years (to)'),
'placeholder' => '1992',
'min' => '-9999',
'max' => '9999',
'optional' => TRUE
), ),
(object) array( (object) array(
'name' => 'source', 'value' => 'C',
'type' => 'text', 'label' => $this->h5pF->t('Copyright')
'label' => $this->h5pF->t('Source'),
'placeholder' => 'https://',
'optional' => TRUE
) )
) )
), ),
(object) array( (object) array(
'name' => 'authorWidget', 'name' => 'licenseVersion',
'type' => 'group', 'type' => 'select',
'fields'=> array( 'label' => $this->h5pF->t('License Version'),
(object) array( 'options' => $cc_versions,
'label' => $this->h5pF->t("Author's name"), 'optional' => TRUE
'name' => "name", ),
'optional' => TRUE, (object) array(
'type' => "text" 'name' => 'yearFrom',
), 'type' => 'number',
(object) array( 'label' => $this->h5pF->t('Years (from)'),
'name' => 'role', 'placeholder' => '1991',
'type' => 'select', 'min' => '-9999',
'label' => $this->h5pF->t("Author's role"), 'max' => '9999',
'default' => 'Author', 'optional' => TRUE
'options' => array( ),
(object) array( (object) array(
'value' => 'Author', 'name' => 'yearTo',
'label' => $this->h5pF->t('Author') 'type' => 'number',
), 'label' => $this->h5pF->t('Years (to)'),
(object) array( 'placeholder' => '1992',
'value' => 'Editor', 'min' => '-9999',
'label' => $this->h5pF->t('Editor') 'max' => '9999',
), 'optional' => TRUE
(object) array( ),
'value' => 'Licensee', (object) array(
'label' => $this->h5pF->t('Licensee') 'name' => 'source',
), 'type' => 'text',
(object) array( 'label' => $this->h5pF->t('Source'),
'value' => 'Originator', 'placeholder' => 'https://',
'label' => $this->h5pF->t('Originator') '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( (object) array(
'name' => 'licenseExtras', 'name' => 'licenseExtras',
'type' => 'textarea', 'type' => 'text',
'widget' => 'textarea',
'label' => $this->h5pF->t('License Extras'), 'label' => $this->h5pF->t('License Extras'),
'optional' => TRUE, 'optional' => TRUE,
'description' => $this->h5pF->t('Any additional information about the license') 'description' => $this->h5pF->t('Any additional information about the license')
), ),
(object) array( (object) array(
'name' => 'changeLog', 'name' => 'changes',
'type' => 'group', 'type' => 'list',
'expanded' => FALSE, 'field' => (object) array(
'label' => $this->h5pF->t('Change Log'), 'name' => 'change',
'fields' => array( 'type' => 'group',
(object) array ( 'label' => $this->h5pF->t('Change Log'),
'name' => 'changeLogForm', 'fields' => array(
'type' => 'group', (object) array(
'label' => $this->h5pF->t('Question'), 'name' => 'date',
'expanded' => TRUE, 'type' => 'text',
'fields' => array( 'label' => $this->h5pF->t('Date'),
(object) array( 'optional' => TRUE
'name' => 'date', ),
'type' => 'text', (object) array(
'label' => $this->h5pF->t('Date'), 'name' => 'author',
'optional' => TRUE 'type' => 'text',
), 'label' => $this->h5pF->t('Changed by'),
(object) array( 'optional' => TRUE
'name' => 'author', ),
'type' => 'text', (object) array(
'label' => $this->h5pF->t('Changed by'), 'name' => 'log',
'optional' => TRUE 'type' => 'text',
), 'widget' => 'textarea',
(object) array( 'label' => $this->h5pF->t('Description of change'),
'name' => 'log', 'placeholder' => $this->h5pF->t('Photo cropped, text changed, etc.'),
'type' => 'textarea', 'optional' => TRUE
'label' => $this->h5pF->t('Description of change'),
'placeholder' => $this->h5pF->t('Photo cropped, text changed, etc.'),
'optional' => TRUE
)
) )
) )
) )
), ),
(object) array( (object) array (
'name' => 'authorComments', 'name' => 'authorComments',
'label' => $this->h5pF->t('Additional Information'), 'type' => 'text',
'type' => 'group', 'widget' => 'textarea',
'expanded' => FALSE, 'label' => $this->h5pF->t('Author comments'),
'fields' => array( 'description' => $this->h5pF->t('Comments for the editor of the content (This text will not be published as a part of copyright info)'),
(object) array ( 'optional' => TRUE
'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
)
)
) )
); );

View File

@ -7,7 +7,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
* @class * @class
* @namespace H5P * @namespace H5P
*/ */
function ContentUpgradeProcess(name, oldVersion, newVersion, params, extras, id, loadLibrary, done) { function ContentUpgradeProcess(name, oldVersion, newVersion, params, id, loadLibrary, done) {
var self = this; var self = this;
// Make params possible to work with // 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.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) { if (err) {
return done(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; var self = this;
// Load library details and upgrade routines // Load library details and upgrade routines
@ -65,7 +55,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
} }
// Run upgrade routines on params // 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) { if (err) {
return done(err); return done(err);
} }
@ -79,7 +69,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
next(err); next(err);
}); });
}, function (err) { }, function (err) {
done(err, params, extras); done(err, params, metadata);
}); });
}); });
}); });
@ -95,7 +85,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
* @param {Object} params * @param {Object} params
* @param {Function} next * @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 (H5PUpgrades[library.name] === undefined) {
if (library.upgradesScript) { if (library.upgradesScript) {
// Upgrades script should be loaded so the upgrades should be here. // Upgrades script should be loaded so the upgrades should be here.
@ -130,17 +120,11 @@ H5P.ContentUpgradeProcess = (function (Version) {
try { try {
unnecessaryWrapper(params, function (err, upgradedParams, upgradedExtras) { unnecessaryWrapper(params, function (err, upgradedParams, upgradedExtras) {
params = upgradedParams; params = upgradedParams;
/** if (upgradedExtras && upgradedExtras.metadata) { // Optional
* "params" (and "extras", e.g. metadata) flow through each update function and are changed metadata = upgradedExtras.metadata;
* 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); nextMinor(err);
}, extras); }, {metadata: metadata});
} }
catch (err) { catch (err) {
if (console && console.log) { if (console && console.log) {
@ -154,7 +138,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
}, nextMajor); }, nextMajor);
} }
}, function (err) { }, 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 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 // 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) { if (!err) {
params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor; params.library = availableLib[0] + ' ' + availableVer.major + '.' + availableVer.minor;
params.params = upgraded; params.params = upgradedParams;
if (extras) { if (upgradedMetadata) {
if (extras.metadata) { params.metadata = upgradedMetadata;
params.metadata = extras.metadata;
}
} }
} }
done(err, params); done(err, params);

View File

@ -9,7 +9,7 @@ var libraryLoadedCallback;
var messageHandlers = { var messageHandlers = {
newJob: function (job) { newJob: function (job) {
// Start new 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? // TODO: Cache?
postMessage({ postMessage({
action: 'loadLibrary', action: 'loadLibrary',
@ -17,7 +17,7 @@ var messageHandlers = {
version: version.toString() version: version.toString()
}); });
libraryLoadedCallback = next; libraryLoadedCallback = next;
}, function done(err, result, extras) { }, function done(err, result) {
if (err) { if (err) {
// Return error // Return error
postMessage({ postMessage({
@ -33,8 +33,7 @@ var messageHandlers = {
postMessage({ postMessage({
action: 'done', action: 'done',
id: job.id, id: job.id,
params: result, params: result
extras: extras
}); });
}); });
}, },

View File

@ -128,7 +128,7 @@
// Register message handlers // Register message handlers
var messageHandlers = { var messageHandlers = {
done: function (result) { done: function (result) {
self.workDone(result.id, result.params, result.extras, this); self.workDone(result.id, result.params, this);
}, },
error: function (error) { error: function (error) {
self.printError(error.err); self.printError(error.err);
@ -196,7 +196,7 @@
self.token = inData.token; self.token = inData.token;
// Start processing // Start processing
self.processBatch(inData.params, {metadata: inData.metadata}); self.processBatch(inData.params);
}); });
}; };
@ -217,7 +217,7 @@
* *
* @param {Object} parameters * @param {Object} parameters
*/ */
ContentUpgrade.prototype.processBatch = function (parameters, extras) { ContentUpgrade.prototype.processBatch = function (parameters) {
var self = this; var self = this;
// Track upgraded params // Track upgraded params
@ -225,7 +225,6 @@
// Track current batch // Track current batch
self.parameters = parameters; self.parameters = parameters;
self.extras = extras;
// Create id mapping // Create id mapping
self.ids = []; self.ids = [];
@ -279,12 +278,11 @@
name: info.library.name, name: info.library.name,
oldVersion: info.library.version, oldVersion: info.library.version,
newVersion: self.version.toString(), newVersion: self.version.toString(),
params: self.parameters[id], params: self.parameters[id]
extras: self.extras
}); });
} }
else { 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) { self.loadLibrary(name, version, function (err, library) {
if (library.upgradesScript) { if (library.upgradesScript) {
self.loadScript(library.upgradesScript, function (err) { self.loadScript(library.upgradesScript, function (err) {
@ -299,13 +297,13 @@
} }
}); });
}, function done(err, result, extras) { }, function done(err, result) {
if (err) { if (err) {
self.printError(err); self.printError(err);
return ; 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; var self = this;
self.working--; self.working--;
@ -335,8 +333,7 @@
self.nextBatch({ self.nextBatch({
libraryId: self.version.libraryId, libraryId: self.version.libraryId,
token: self.token, token: self.token,
params: JSON.stringify(self.upgraded), params: JSON.stringify(self.upgraded)
extras: extras
}); });
} }
}; };

View File

@ -133,9 +133,10 @@ H5P.XAPIEvent.prototype.setObject = function (instance) {
} }
} }
else { 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 = { 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) { H5P.XAPIEvent.prototype.setContext = function (instance) {
if (instance.parent && (instance.parent.contentId || instance.parent.subContentId)) { 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 = { this.data.statement.context = {
"contextActivities": { "contextActivities": {
"parent": [ "parent": [

View File

@ -1657,8 +1657,8 @@ H5P.libraryFromString = function (library) {
if (res !== null) { if (res !== null) {
return { return {
'machineName': res[1], 'machineName': res[1],
'majorVersion': res[2], 'majorVersion': parseInt(res[2]),
'minorVersion': res[3] 'minorVersion': parseInt(res[3])
}; };
} }
else { else {
@ -2140,6 +2140,20 @@ H5P.createTitle = function (rawTitle, maxLength) {
contentUserDataAjax(contentId, dataId, subContentId, undefined, null); 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. * Prepares the content parameters for storing in the clipboard.
* *
@ -2242,6 +2256,17 @@ H5P.createTitle = function (rawTitle, maxLength) {
H5P.externalDispatcher.trigger('datainclipboard', {reset: false}); 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. * Get item from the H5P Clipboard.
* *