Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions contentcuration/contentcuration/frontend/shared/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const kindToIconMap = {
channel: 'apps',
document: 'description',
exercise: 'star',
h5p: 'widgets',
html5: 'widgets',
image: 'image',
slideshow: 'photo_library',
Expand Down Expand Up @@ -205,6 +206,7 @@ export const AccessibilityCategoriesMap = {
video: ['CAPTIONS_SUBTITLES', 'AUDIO_DESCRIPTION', 'SIGN_LANGUAGE'],
exercise: ['ALT_TEXT'],
html5: ['ALT_TEXT', 'HIGH_CONTRAST'],
h5p: ['ALT_TEXT', 'HIGH_CONTRAST'],
audio: ['CAPTIONS_SUBTITLES'],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ const FormatPresetsMap = new Map([
supplementary: false,
thumbnail: false,
subtitle: false,
display: false,
display: true,
order: 1,
kind_id: 'h5p',
allowed_formats: ['h5p'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ const LanguagesMap = new Map([
lang_direction: 'ltr',
},
],
[
'rsn',
{
id: 'rsn',
lang_code: 'rsn',
lang_subcode: null,
readable_name: 'Rwandan Sign Language',
native_name: "Amarenga y'Ikinyarwanda",
lang_direction: 'ltr',
},
],
[
'an',
{
Expand Down Expand Up @@ -198,6 +209,17 @@ const LanguagesMap = new Map([
lang_direction: 'ltr',
},
],
[
'csx',
{
id: 'csx',
lang_code: 'csx',
lang_subcode: null,
readable_name: 'Cambodian Sign Language',
native_name: 'Cambodian Sign Language',
lang_direction: 'ltr',
},
],
[
'ca',
{
Expand Down Expand Up @@ -1430,6 +1452,17 @@ const LanguagesMap = new Map([
lang_direction: 'ltr',
},
],
[
'xki',
{
id: 'xki',
lang_code: 'xki',
lang_subcode: null,
readable_name: 'Kenyan Sign Language',
native_name: 'Swahili Lugha ya ishara',
lang_direction: 'ltr',
},
],
[
'be-tara',
{
Expand Down Expand Up @@ -2564,6 +2597,18 @@ const LanguagesMap = new Map([
lang_direction: 'ltr',
},
],
[
'nsp',
{
id: 'nsp',
lang_code: 'nsp',
lang_subcode: null,
readable_name: 'Nepalese Sign Language',
native_name:
'\u0928\u0947\u092a\u093e\u0932\u0940 \u0938\u093e\u0902\u0915\u0947\u0924\u093f\u0915 \u092d\u093e\u0937\u093e',
lang_direction: 'ltr',
},
],
[
'pi',
{
Expand Down Expand Up @@ -3011,6 +3056,7 @@ export const LanguagesNames = {
AF: 'af',
AK: 'ak',
AKA: 'aka',
RSN: 'rsn',
AN: 'an',
EN_PT: 'en-PT',
IG: 'ig',
Expand All @@ -3023,6 +3069,7 @@ export const LanguagesNames = {
EN_GB: 'en-GB',
BUG: 'bug',
BXK: 'bxk',
CSX: 'csx',
CA: 'ca',
CEB: 'ceb',
CH: 'ch',
Expand Down Expand Up @@ -3135,6 +3182,7 @@ export const LanguagesNames = {
PBT: 'pbt',
SV_SE: 'sv-SE',
SV_FI: 'sv-FI',
XKI: 'xki',
BE_TARA: 'be-tara',
VI: 'vi',
TPI: 'tpi',
Expand Down Expand Up @@ -3237,6 +3285,7 @@ export const LanguagesNames = {
NEW: 'new',
NE: 'ne',
NE_NP: 'ne-NP',
NSP: 'nsp',
PI: 'pi',
BH: 'bh',
BHO: 'bho',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
return this.$tr('exercise');
case 'document':
return this.$tr('document');
case 'h5p':
return this.$tr('html5');
case 'html5':
return this.$tr('html5');
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default function theme() {
audio: '#f06292',
document: '#ff3d00',
exercise: '#4db6ac',
h5p: '#ff8f00',
html5: '#ff8f00',
zim: '#ff8f00',
slideshow: '#4ece90',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,17 @@ describe('file store', () => {
it('getH5PMetadata should check for h5p.json file', () => {
const zip = new JSZip();
return zip.generateAsync({ type: 'blob' }).then(async function(h5pBlob) {
await expect(Promise.resolve(getH5PMetadata(h5pBlob))).resolves.toThrowError(
await expect(getH5PMetadata(h5pBlob)).rejects.toThrow(
'h5p.json not found in the H5P file.'
);
});
});
it('getH5PMetadata should exract metadata from h5p.json', async () => {
it('getH5PMetadata should extract metadata from h5p.json', async () => {
const manifestFile = get_metadata_file({ title: 'Test file' });
const zip = new JSZip();
zip.file('h5p.json', manifestFile);
await zip.generateAsync({ type: 'blob' }).then(async function(h5pBlob) {
await expect(Promise.resolve(getH5PMetadata(h5pBlob))).resolves.toEqual({
await expect(getH5PMetadata(h5pBlob)).resolves.toEqual({
title: 'Test file',
});
});
Expand All @@ -169,7 +169,55 @@ describe('file store', () => {
const zip = new JSZip();
zip.file('h5p.json', manifestFile);
await zip.generateAsync({ type: 'blob' }).then(async function(h5pBlob) {
await expect(Promise.resolve(getH5PMetadata(h5pBlob))).resolves.toEqual({
await expect(getH5PMetadata(h5pBlob)).resolves.toEqual({
title: 'Test file',
});
});
});
it.each([
['CC BY', 1],
['CC BY-SA', 2],
['CC BY-ND', 3],
['CC BY-NC', 4],
['CC BY-NC-SA', 5],
['CC BY-NC-ND', 6],
['CC0 1.0', 8],
])('getH5PMetadata should parse CC license %s', async (licenseName, licenseId) => {
const manifestFile = get_metadata_file({ title: 'Test file', license: licenseName });
const zip = new JSZip();
zip.file('h5p.json', manifestFile);
await zip.generateAsync({ type: 'blob' }).then(async function(h5pBlob) {
await expect(getH5PMetadata(h5pBlob)).resolves.toEqual({
title: 'Test file',
license: licenseId,
});
});
});
it.each([
[{ role: 'Author', name: 'Testing' }, 'author'],
[{ role: 'Editor', name: 'Testing' }, 'aggregator'],
[{ role: 'Licensee', name: 'Testing' }, 'copyright_holder'],
[{ role: 'Originator', name: 'Testing' }, 'provider'],
])('getH5PMetadata should parse CC license %s', async (authorObj, field) => {
const manifestFile = get_metadata_file({ title: 'Test file', authors: [authorObj] });
const zip = new JSZip();
zip.file('h5p.json', manifestFile);
await zip.generateAsync({ type: 'blob' }).then(async function(h5pBlob) {
await expect(getH5PMetadata(h5pBlob)).resolves.toEqual({
title: 'Test file',
[field]: authorObj.name,
});
});
});
it('getH5PMetadata should not extract Firstname Surname author', async () => {
const manifestFile = get_metadata_file({
title: 'Test file',
authors: [{ name: 'Firstname Surname', role: 'Author' }],
});
const zip = new JSZip();
zip.file('h5p.json', manifestFile);
await zip.generateAsync({ type: 'blob' }).then(async function(h5pBlob) {
await expect(getH5PMetadata(h5pBlob)).resolves.toEqual({
title: 'Test file',
});
});
Expand All @@ -178,17 +226,13 @@ describe('file store', () => {
const manifestFile = get_metadata_file({
title: 'Test file',
language: 'en',
authors: 'author1',
license: 'license1',
});
const zip = new JSZip();
zip.file('h5p.json', manifestFile);
await zip.generateAsync({ type: 'blob' }).then(async function(h5pBlob) {
await expect(Promise.resolve(getH5PMetadata(h5pBlob))).resolves.toEqual({
await expect(getH5PMetadata(h5pBlob)).resolves.toEqual({
title: 'Test file',
language: 'en',
author: 'author1',
license: 'license1',
});
});
});
Expand Down
38 changes: 31 additions & 7 deletions contentcuration/contentcuration/frontend/shared/vuex/file/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import SparkMD5 from 'spark-md5';
import JSZip from 'jszip';
import { FormatPresetsList, FormatPresetsNames } from 'shared/leUtils/FormatPresets';
import { LicensesList } from 'shared/leUtils/Licenses';
import LanguagesMap from 'shared/leUtils/Languages';

const BLOB_SLICE = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
const CHUNK_SIZE = 2097152;
Expand Down Expand Up @@ -64,6 +66,13 @@ export function storageUrl(checksum, file_format) {
return `/content/storage/${checksum[0]}/${checksum[1]}/${checksum}.${file_format}`;
}

const AuthorFieldMappings = {
Author: 'author',
Editor: 'aggregator',
Licensee: 'copyright_holder',
Originator: 'provider',
};

export async function getH5PMetadata(fileInput) {
const zip = new JSZip();
const metadata = {};
Expand All @@ -82,19 +91,34 @@ export async function getH5PMetadata(fileInput) {
if (Object.prototype.hasOwnProperty.call(data, 'title')) {
metadata.title = data['title'];
}
if (Object.prototype.hasOwnProperty.call(data, 'language') && data['language'] !== 'und') {
if (
Object.prototype.hasOwnProperty.call(data, 'language') &&
LanguagesMap.has(data['language']) &&
data['language'] !== 'und'
) {
metadata.language = data['language'];
}
if (Object.prototype.hasOwnProperty.call(data, 'authors')) {
metadata.author = data['authors'];
for (const author of data['authors']) {
// Ignore obvious placedholders created by online H5P editor tools
if (author.role && author.name !== 'Firstname Surname') {
if (AuthorFieldMappings[author.role]) {
metadata[AuthorFieldMappings[author.role]] = author.name;
}
}
}
}
if (Object.prototype.hasOwnProperty.call(data, 'license')) {
metadata.license = data['license'];
const license = LicensesList.find(license => license.license_name === data['license']);
if (license) {
metadata.license = license.id;
} else if (data['license'] == 'CC0 1.0') {
// Special case for CC0 1.0
// this is the hard coded license id for CC0 1.0
metadata.license = 8;
}
}
return metadata;
})
.catch(function(error) {
return error;
});
}

Expand Down Expand Up @@ -130,7 +154,7 @@ export function extractMetadata(file, preset = null) {
return new Promise(resolve => {
if (isH5P) {
getH5PMetadata(file).then(data => {
if (data.constructor !== Error) Object.assign(metadata, ...data);
Object.assign(metadata, data);
});
resolve(metadata);
} else {
Expand Down