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 @@ - - - - - - -{ - "fontFamily": "h5p", - "description": "Font generated by IcoMoon.", - "majorVersion": 1, - "minorVersion": 1, - "version": "Version 1.1", - "fontId": "h5p", - "psName": "h5p", - "subFamily": "Regular", - "fullName": "h5p" -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fonts/h5p-core-18.ttf b/fonts/h5p-core-18.ttf deleted file mode 100755 index 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 @@ + + + + + + +{ + "fontFamily": "h5p-core-18", + "description": "Font generated by IcoMoon.", + "majorVersion": 1, + "minorVersion": 1, + "version": "Version 1.1", + "fontId": "h5p-core-18", + "psName": "h5p-core-18", + "subFamily": "Regular", + "fullName": "h5p-core-18" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/h5p-core-19.ttf b/fonts/h5p-core-19.ttf new file mode 100644 index 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; }