diff --git a/fonts/h5p-core-18.eot b/fonts/h5p-core-18.eot
deleted file mode 100755
index eba9d6c..0000000
Binary files a/fonts/h5p-core-18.eot and /dev/null differ
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 @@
-
-
-
\ 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 37f845e..0000000
Binary files a/fonts/h5p-core-18.ttf and /dev/null differ
diff --git a/fonts/h5p-core-18.woff b/fonts/h5p-core-18.woff
deleted file mode 100755
index 8450f3d..0000000
Binary files a/fonts/h5p-core-18.woff and /dev/null differ
diff --git a/fonts/h5p-core-19.eot b/fonts/h5p-core-19.eot
new file mode 100644
index 0000000..2348e29
Binary files /dev/null and b/fonts/h5p-core-19.eot differ
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 @@
+
+
+
\ 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 0000000..729aea4
Binary files /dev/null and b/fonts/h5p-core-19.ttf differ
diff --git a/fonts/h5p-core-19.woff b/fonts/h5p-core-19.woff
new file mode 100644
index 0000000..be9ead2
Binary files /dev/null and b/fonts/h5p-core-19.woff differ
diff --git a/h5p.classes.php b/h5p.classes.php
index 2efebbd..9d21c50 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' => '/^\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]?://.+)$',
+ '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,27 @@ 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,
+ '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)) {
+ unset($h5pJson[$key]);
+ }
+ }
+
// Add dependencies to h5p
foreach ($content['dependencies'] as $dependency) {
$library = $dependency['library'];
@@ -1770,7 +1794,7 @@ abstract class H5PHubEndpoints {
* Functions and storage shared by the other H5P classes
*/
class H5PCore {
-
+
public static $coreApi = array(
'majorVersion' => 1,
'minorVersion' => 16
@@ -3533,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
}
}
@@ -3667,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) {
@@ -3712,10 +3749,11 @@ 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);
}
+
$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);
@@ -4082,6 +4120,248 @@ 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' => $this->h5pF->t('Undisclosed')
+ ),
+ (object) array(
+ 'type' => 'optgroup',
+ 'label' => $this->h5pF->t('Creative Commons'),
+ 'options' => [
+ (object) array(
+ 'value' => 'CC BY',
+ 'label' => $this->h5pF->t('Attribution'),
+ 'versions' => $cc_versions
+ ),
+ (object) array(
+ 'value' => 'CC BY-SA',
+ 'label' => $this->h5pF->t('Attribution-ShareAlike'),
+ 'versions' => $cc_versions
+ ),
+ (object) array(
+ 'value' => 'CC BY-ND',
+ 'label' => $this->h5pF->t('Attribution-NoDerivs'),
+ 'versions' => $cc_versions
+ ),
+ (object) array(
+ 'value' => 'CC BY-NC',
+ 'label' => $this->h5pF->t('Attribution-NonCommercial'),
+ 'versions' => $cc_versions
+ ),
+ (object) array(
+ 'value' => 'CC BY-NC-SA',
+ 'label' => $this->h5pF->t('Attribution-NonCommercial-ShareAlike'),
+ 'versions' => $cc_versions
+ ),
+ (object) array(
+ 'value' => 'CC BY-NC-ND',
+ 'label' => $this->h5pF->t('Attribution-NonCommercial-NoDerivs'),
+ 'versions' => $cc_versions
+ ),
+ (object) array(
+ 'value' => 'CC0 1.0',
+ 'label' => $this->h5pF->t('Public Domain Dedication')
+ ),
+ (object) array(
+ 'value' => 'CC PDM',
+ 'label' => $this->h5pF->t('Public Domain Mark')
+ ),
+ ]
+ ),
+ (object) array(
+ 'value' => 'GNU GPL',
+ 'label' => $this->h5pF->t('General Public License v3')
+ ),
+ (object) array(
+ 'value' => 'PD',
+ 'label' => $this->h5pF->t('Public Domain')
+ ),
+ (object) array(
+ 'value' => 'ODC PDDL',
+ 'label' => $this->h5pF->t('Public Domain Dedication and Licence')
+ ),
+ (object) array(
+ 'value' => 'C',
+ 'label' => $this->h5pF->t('Copyright')
+ )
+ )
+ ),
+ (object) array(
+ 'name' => 'licenseVersion',
+ 'type' => 'select',
+ 'label' => $this->h5pF->t('License Version'),
+ 'options' => array(),
+ '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' => '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')
+ )
+ )
+ )
+ )
+ ),
+ (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('Photo cropped, text changed, etc.'),
+ '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-content-upgrade-process.js b/js/h5p-content-upgrade-process.js
index e54dbb7..e440341 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,19 @@ 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;
+ /**
+ * "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);
}
catch (err) {
if (console && console.log) {
@@ -127,7 +154,7 @@ H5P.ContentUpgradeProcess = (function (Version) {
}, nextMajor);
}
}, function (err) {
- next(err, params);
+ next(err, params, extras);
});
};
@@ -168,11 +195,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
});
}
};
diff --git a/js/h5p.js b/js/h5p.js
index ef56834..1c73e70 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) {
@@ -163,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, contentId, library.metadata);
if (!copyrights) {
displayOptions.copyright = false;
}
@@ -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!
@@ -973,9 +978,11 @@ H5P.Dialog = function (name, title, content, $element) {
* Parameters 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) {
+H5P.getCopyrights = function (instance, parameters, contentId, metadata) {
var copyrights;
if (instance.getCopyrights !== undefined) {
@@ -994,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();
@@ -1010,8 +1022,19 @@ H5P.getCopyrights = function (instance, parameters, contentId) {
* 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) {
if (!parameters.hasOwnProperty(field)) {
@@ -1021,6 +1044,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.");
@@ -1030,12 +1055,21 @@ H5P.findCopyrights = function (info, parameters, contentId) {
var value = parameters[field];
+ if (value && value.library && typeof value.library === 'string') {
+ lastContentTypeName = value.library.split(' ')[0];
+ }
+ else if (value && 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
H5P.findCopyrights(info, value, contentId);
}
else if (value instanceof Object) {
- // Check if object is a file with copyrights
+ buildFromMetadata(value, lastContentTypeName, contentId);
+
+ // Check if object is a file with copyrights (old core)
if (value.copyright === undefined ||
value.copyright.license === undefined ||
value.path === undefined ||
@@ -1054,6 +1088,54 @@ 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) {
+ 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.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,
+ 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 = 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}
+ );
+ }
};
/**
@@ -1175,6 +1257,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.
*
@@ -1294,7 +1387,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 + '';
@@ -1341,6 +1434,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));
}
}
@@ -2041,9 +2137,187 @@ 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) {
+ var self = this;
+
+ /**
+ * 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) {
+ self.contentId = H5PEditor.contentId;
+ }
+
+ if (!self.specific.width && !self.specific.height) {
+ setDimensionsFromFile();
+ }
+ };
+
+ /**
+ * 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));
+
+ // Clear cache
+ parsedClipboard = null;
+
+ // Trigger an event so all 'Paste' buttons may be enabled.
+ H5P.externalDispatcher.trigger('datainclipboard', {reset: false});
+ };
+
+ /**
+ * 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;
+ }
+
+ // 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
+ 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.generic) {
+ // Use reference instead of key
+ clipboardData.generic = clipboardData.specific[clipboardData.generic];
+
+ // Avoid multiple content with same ID
+ delete clipboardData.generic.subContentId;
+ }
+
+ return clipboardData;
+ };
+
+ /**
+ * Update file URLs. Useful when copying content.
+ *
+ * @private
+ * @param {object} params Reference
+ * @param {function} handler Modifies the path to work when pasted
+ */
+ var 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 {
+ 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') {
+ // Clear cache
+ parsedClipboard = null;
+
+ // Trigger an event so all 'Paste' buttons may be enabled.
+ H5P.externalDispatcher.trigger('datainclipboard', {reset: event.newValue === null});
+ }
+ });
+
var ccVersions = {
'default': '4.0',
'4.0': H5P.t('licenseCC40'),
@@ -2062,34 +2336,38 @@ 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': {
+ 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',
@@ -2119,7 +2397,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'),
};
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;
}