diff --git a/sourcecode/apis/contentauthor/app/H5PContentLibrary.php b/sourcecode/apis/contentauthor/app/H5PContentLibrary.php index 52c826e585..315c7b3718 100644 --- a/sourcecode/apis/contentauthor/app/H5PContentLibrary.php +++ b/sourcecode/apis/contentauthor/app/H5PContentLibrary.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class H5PContentLibrary extends Model { @@ -14,4 +15,12 @@ class H5PContentLibrary extends Model protected $guarded = []; public $timestamps = false; + + /** + * @return BelongsTo + */ + public function library(): BelongsTo + { + return $this->belongsTo(H5PLibrary::class, 'library_id'); + } } diff --git a/sourcecode/apis/contentauthor/app/H5PContentsMetadata.php b/sourcecode/apis/contentauthor/app/H5PContentsMetadata.php index fecbe502e7..baac6197be 100644 --- a/sourcecode/apis/contentauthor/app/H5PContentsMetadata.php +++ b/sourcecode/apis/contentauthor/app/H5PContentsMetadata.php @@ -6,8 +6,19 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Support\Carbon; /** + * @property int $id + * @property int $content_id + * @property string $authors + * @property string $license + * @property string $license_version + * @property string $license_extras + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $default_language + * * @method static self make(array $attributes = []) */ class H5PContentsMetadata extends Model diff --git a/sourcecode/apis/contentauthor/app/Http/Controllers/Admin/AdminH5PDetailsController.php b/sourcecode/apis/contentauthor/app/Http/Controllers/Admin/AdminH5PDetailsController.php index b68e60ba9f..eca664da15 100644 --- a/sourcecode/apis/contentauthor/app/Http/Controllers/Admin/AdminH5PDetailsController.php +++ b/sourcecode/apis/contentauthor/app/Http/Controllers/Admin/AdminH5PDetailsController.php @@ -11,11 +11,13 @@ use App\Http\Controllers\Controller; use App\Http\Requests\AdminTranslationUpdateRequest; use App\Libraries\ContentAuthorStorage; +use App\Libraries\H5P\AdminConfig; use Cerpus\VersionClient\VersionData; use Exception; use H5PCore; use H5PValidator; use Illuminate\Contracts\View\View; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Carbon; @@ -204,6 +206,10 @@ public function libraryTranslation(H5PLibrary $library, string $locale): View 'translationFile' => Storage::disk()->get( sprintf('libraries/%s/language/%s.json', $library->getFolderName(), $locale) ), + 'contentCount' => H5PContent::where('library_id', $library->id) + ->whereHas('metadata', function (Builder $query) use ($locale) { + $query->where('default_language', $locale); + })->count(), ]); } @@ -252,6 +258,33 @@ public function libraryTranslationUpdate(AdminTranslationUpdateRequest $request, sprintf('libraries/%s/language/%s.json', $library->getFolderName(), $locale) ), 'messages' => $messages, + 'contentCount' => H5PContent::where('library_id', $library->id) + ->whereHas('metadata', function (Builder $query) use ($locale) { + $query->where('default_language', $locale); + })->count(), + ]); + } + + public function contentTranslationUpdate(H5PLibrary $library, string $locale): View + { + $config = app(AdminConfig::class); + $config->getConfig(); + $config->addContentLanguageScripts(); + + $content = H5PContent::where('library_id', $library->id) + ->whereHas('metadata', function (Builder $query) use ($locale) { + $query->where('default_language', $locale); + })->get(); + + return view('admin.content-language-update', [ + 'library' => $library, + 'languageCode' => $locale, + 'contentCount' => $content->count(), + //'h5pAdminIntegration' => $configuration, + 'h5pIntegration' => $config->config, + 'scripts' => $config->getScriptAssets(), + 'styles' => $config->getStyleAssets(), + 'contents' => $content->random(), ]); } diff --git a/sourcecode/apis/contentauthor/app/Libraries/H5P/AdminConfig.php b/sourcecode/apis/contentauthor/app/Libraries/H5P/AdminConfig.php index 63c1efeb87..3862d6fbea 100644 --- a/sourcecode/apis/contentauthor/app/Libraries/H5P/AdminConfig.php +++ b/sourcecode/apis/contentauthor/app/Libraries/H5P/AdminConfig.php @@ -71,6 +71,34 @@ public function addPresaveScripts() $this->addAsset('scripts', asset('/js/h5p/h5peditor-pre-save.js')); } + public function addContentLanguageScripts(): void + { + $this->addCoreAssets(); + $this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor.js')); + $this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-editor.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-form.js')); + $this->addAsset('scripts', $this->getAssetUrl('editor', 'language/en.js')); + + // Widgets + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-file-uploader.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-av.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-semantic-structure.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-boolean.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-coordinates.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-dimensions.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-file.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-group.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-html.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-image.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-library.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-list.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-none.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-number.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-select.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-text.js')); + //$this->addAsset('scripts', $this->getAssetUrl('editor', 'scripts/h5peditor-textarea.js')); + } + public function getSettings(H5PLibrary $library) { $upgrades = $library->getUpgrades(false) diff --git a/sourcecode/apis/contentauthor/resources/assets/entrypoints/ndla-content-language.js b/sourcecode/apis/contentauthor/resources/assets/entrypoints/ndla-content-language.js new file mode 100644 index 0000000000..99e6beb4f2 --- /dev/null +++ b/sourcecode/apis/contentauthor/resources/assets/entrypoints/ndla-content-language.js @@ -0,0 +1 @@ +import '../js/ndla-content-language-update'; diff --git a/sourcecode/apis/contentauthor/resources/assets/js/ndla-content-language-update.js b/sourcecode/apis/contentauthor/resources/assets/js/ndla-content-language-update.js new file mode 100644 index 0000000000..43d47de758 --- /dev/null +++ b/sourcecode/apis/contentauthor/resources/assets/js/ndla-content-language-update.js @@ -0,0 +1,266 @@ +/** + * Update the translations stored in the content + */ + +var H5PEditor = window.H5PEditor = window.H5PEditor || {}; +var ns = H5PEditor; + +ns.getAjaxUrl = function (action, parameters) { + let url = H5PIntegration.ajaxPath + action; + + if (parameters !== undefined) { + let separator = url.indexOf('?') === -1 ? '?' : '&'; + for (const property in parameters) { + if (parameters.hasOwnProperty(property)) { + url += separator + property + '=' + parameters[property]; + separator = '&'; + } + } + } + + return url; +}; + +// CSS is not used, so prevent CSS from being loaded +H5P.cssLoaded = function (path) { + return true; +} + +// Don't need to load the JS either +H5P.jsLoaded = function (path) { + return true; +} + +// Additional libraries that should be loaded +ns.librariesToLoad = []; + +ns.ContentLanguageUpdateProcess = async function (params, metadata, library, lang) { + if (typeof ns.libraryCache[library] === "undefined") { + ns.librariesToLoad.push(library); + } + console.log(JSON.stringify(params)); + getSubContentLibraries(params, lang); + // Load language when loading library + ns.contentLanguage = lang; + ns.defaultLanguage = lang; + Promise.all( + ns.librariesToLoad.map(library => { + return loadLibrary(library, lang); + }) + ) + .then(async (values) => { + if (await loadTranslations(lang)) { + values.forEach(({library: machineName, semantics}) => { + console.log(machineName, semantics); + if (!ns.renderableCommonFields[machineName] || !ns.renderableCommonFields[machineName].fields) { + processSemanticsChunk(semantics, machineName); + } + }); + // console.log(ns.renderableCommonFields); + updateCommonFields(params, lang, library); + console.log(JSON.stringify(params)); + } + }) + + // Todo: Write new ns.loadLibrary using Promise + async function loadLibrary (library) { + return new Promise((resolve, reject) => { + // The loadLibrary() doesn't callback on error, so we give it 60s to reply + const timeout = setTimeout(() => reject(library), 60 * 1000); + ns.loadLibrary(library, semantics => { + console.log(semantics); + clearTimeout(timeout); + resolve({ + library, + semantics + }); + }); + }); + } + + /** + * Recursively traverse params and check if any additional libraries are used + * Based on ns.Form.prototype.setSubContentDefaultLanguage in vendor/h5p/h5p-editor/js/h5peditor-form.js + * + * @param {Object|Array} params Parameters + * @param {string} lang Default language that will be set + * + * @return {Object|Array} Parameters with default language set for sub-content + */ + function getSubContentLibraries (params, lang) { + if (!params) { + return params; + } + + if (Array.isArray(params)) { + for (let i = 0; i < params.length; i++) { + getSubContentLibraries(params[i], lang); + } + } else if (typeof params === 'object') { + if (params.library && !ns.libraryCache[params.library] && !ns.librariesToLoad.includes(params.library)) { + ns.librariesToLoad.push(params.library); + } + + for (const parameter of Object.keys(params)) { + getSubContentLibraries(params[parameter], lang); + } + } + } + + /** + * Verify that we have the translations for the libraries, load them if not + * Based on loadTranslations in vendor/h5p/h5p-editor/js/h5peditor-form.js + * + * @param {string} lang + * @return {Promise} + */ + async function loadTranslations(lang) { + const loadLibs = []; + for (let li in ns.libraryCache) { + if (ns.libraryCache[li] === 0 || ns.libraryCache[li].translation[lang] === undefined) { + loadLibs.push(li); + } + } + + if (loadLibs.length) { + try { + await fetch( + ns.getAjaxUrl('translations', { language: lang }), + { + method: "POST", + headers: { + 'Content-Type': "application/json", + }, + body: JSON.stringify({ + libraries: loadLibs, + }), + } + ).then(response => response.json() + ).then(data => { + for (let lib in data.data) { + ns.libraryCache[lib].translation[lang] = JSON.parse(data.data[lib]).semantics; + } + }); + } catch (e) { + console.log(e); + return false; + } + } + + return true; + } + + /** + * Recursive processing of the semantics chunks. + * Based on ns.processSemanticsChunk in vendor/h5p/h5p-editor/js/h5peditor.js + * + * @param {object} semanticsChunk + * @param {string} machineName Machine name of library that is being processed + * @returns {undefined} + */ + function processSemanticsChunk (semanticsChunk, machineName) { + for (let i = 0; i < semanticsChunk.length; i++) { + const field = semanticsChunk[i]; + + // Find common fields + if ((field.common !== undefined && field.common) || (field?.type === "text" && field?.default)) { + ns.renderableCommonFields[machineName] = ns.renderableCommonFields[machineName] || {}; + ns.renderableCommonFields[machineName].fields = ns.renderableCommonFields[machineName].fields || []; + + // Add renderable if it doesn't exist + ns.renderableCommonFields[machineName].fields.push({ + field: field, + parent: machineName, + }); + } else if (field.type && field.type === "group") { + processSemanticsChunk(field.fields, machineName); + } + } + } + + /** + * Got through the collected fields and update the translation + * Based on updateCommonFields in vendor/h5p/h5p-editor/js/h5peditor-form.js + * + * @param {object} params + * @param {string} lang + * @param {string} baseLibrary + */ + function updateCommonFields (params, lang, baseLibrary) { + for (let lib in ns.libraryCache) { + if (ns.renderableCommonFields[lib] && ns.renderableCommonFields[lib].fields) { + for (let j = 0; j < ns.renderableCommonFields[lib].fields.length; j++) { + const fields = ns.renderableCommonFields[lib].fields[j]; + setSubLibraryCommonFields(params, fields, lib); + } + } + } + } + + /** + * Go through the params and replace the translations for the given field(s) + * + * @param {object} params + * @param {object} fields + * @param {string} currentLibrary + */ + function setSubLibraryCommonFields (params, fields, currentLibrary) { + for(const propName of Object.keys(params)) { + // console.log(propName, currentLibrary); + if (propName === 'library') { + // found subcontent using a different library + currentLibrary = params[propName]; + } + if (params[propName] !== null && typeof params[propName] === "object") { + // Dig deeper + setSubLibraryCommonFields(params[propName], fields, currentLibrary); + } else if (currentLibrary === fields.parent) { + // Found the library the field(s) belong to in the params + // console.log(params[propName]); + if (params[fields.field.name]) { + // and the correct property found, update the translation(s) + if (fields.field.fields) { + // it contains multiple fields + fields.field.fields.forEach(field => { + params[fields.field.name][field.name] = field.default; + }); + } else if (params[fields.field.name]) { + // it's a single field + params[fields.field.name] = fields.field.default; + } + } + } + } + } +} + +//595 +let params = "{\"presentation\":{\"slides\":[{\"elements\":[{\"x\":5.082592121982211,\"y\":22.613065326633166,\"width\":7.6238881829733165,\"height\":7.5376884422110555,\"action\":{\"library\":\"H5P.AdvancedText 1.1\",\"params\":{\"text\":\"

Nynorsk<\\/p>\\n\"},\"subContentId\":\"527602d8-ea22-472a-a843-86a6b87ae439\",\"metadata\":{\"contentType\":\"Text\",\"license\":\"U\",\"title\":\"Nynorsk\",\"defaultLanguage\":\"nb\",\"authors\":[],\"changes\":[]}},\"alwaysDisplayComments\":false,\"backgroundOpacity\":0,\"displayAsButton\":false,\"buttonSize\":\"big\",\"goToSlideType\":\"specified\",\"invisible\":false,\"solution\":\"\"},{\"x\":30.495552731893266,\"y\":22.613065326633166,\"width\":7.6238881829733165,\"height\":7.5376884422110555,\"action\":{\"library\":\"H5P.AdvancedText 1.1\",\"params\":{\"text\":\"

Bokm\u00e5l<\\/p>\\n\"},\"subContentId\":\"5fcff721-b2d5-47ee-8d53-ca6b6accd8e4\",\"metadata\":{\"contentType\":\"Text\",\"license\":\"U\",\"title\":\"Bokm\u00e5l\",\"defaultLanguage\":\"nb\",\"authors\":[],\"changes\":[]}},\"alwaysDisplayComments\":false,\"backgroundOpacity\":0,\"displayAsButton\":false,\"buttonSize\":\"big\",\"goToSlideType\":\"specified\",\"invisible\":false,\"solution\":\"\"},{\"x\":55.90851334180432,\"y\":25.12562814070352,\"width\":6.353240152477763,\"height\":7.5376884422110555,\"action\":{\"library\":\"H5P.AdvancedText 1.1\",\"params\":{\"text\":\"

English<\\/p>\\n\"},\"subContentId\":\"b008b0a6-5d31-4b4b-9b3c-b8052efe2910\",\"metadata\":{\"contentType\":\"Text\",\"license\":\"U\",\"title\":\"English\",\"defaultLanguage\":\"nb\",\"authors\":[],\"changes\":[]}},\"alwaysDisplayComments\":false,\"backgroundOpacity\":0,\"displayAsButton\":false,\"buttonSize\":\"big\",\"goToSlideType\":\"specified\",\"invisible\":false,\"solution\":\"\"},{\"x\":5.082592121982211,\"y\":32.663316582914575,\"width\":6.353240152477763,\"height\":12.56281407035176,\"action\":{\"library\":\"H5P.Audio 1.5\",\"params\":{\"fitToWrapper\":true,\"playerMode\":\"minimalistic\",\"controls\":true,\"autoplay\":false,\"playAudio\":\"Toista audio\",\"pauseAudio\":\"Keskeyt\u00e4 audio\",\"contentName\":\"\u00c4\u00e4ni\",\"audioNotSupported\":\"Selaimesi ei tue t\u00e4t\u00e4 \u00e4\u00e4nt\u00e4.\",\"files\":[{\"path\":\"https:\\/\\/api.ndla.no\\/audio\\/files\\/bnyHQFFe.mp3\",\"mime\":\"audio\\/mp3\",\"copyright\":{\"license\":\"U\"}}]},\"subContentId\":\"f897c96b-4863-4581-96b2-ea19c1f08fc1\",\"metadata\":{\"contentType\":\"Audio\",\"license\":\"U\",\"title\":\"Test Marc Van Opstal eksempel nn\",\"authors\":[{\"name\":\"Mark Knopfler\",\"role\":\"Licensee\"}],\"defaultLanguage\":\"nb\",\"changes\":[],\"extraTitle\":\"Test Marc Van Opstal eksempel nn\"}},\"alwaysDisplayComments\":false,\"backgroundOpacity\":0,\"displayAsButton\":false,\"buttonSize\":\"big\",\"goToSlideType\":\"specified\",\"invisible\":false,\"solution\":\"\"},{\"x\":30.495552731893266,\"y\":32.663316582914575,\"width\":6.353240152477763,\"height\":12.56281407035176,\"action\":{\"library\":\"H5P.Audio 1.5\",\"params\":{\"fitToWrapper\":true,\"playerMode\":\"minimalistic\",\"controls\":true,\"autoplay\":false,\"playAudio\":\"Toista audio\",\"pauseAudio\":\"Keskeyt\u00e4 audio\",\"contentName\":\"\u00c4\u00e4ni\",\"audioNotSupported\":\"Selaimesi ei tue t\u00e4t\u00e4 \u00e4\u00e4nt\u00e4.\",\"files\":[{\"path\":\"https:\\/\\/api.ndla.no\\/audio\\/files\\/lk5cn8qd.mp3\",\"mime\":\"audio\\/mp3\",\"copyright\":{\"license\":\"U\"}}]},\"subContentId\":\"6bccbfc1-6f68-4dd6-ac5b-43627886d656\",\"metadata\":{\"contentType\":\"Audio\",\"license\":\"U\",\"title\":\"Test Marc Van Opstal eksempel\",\"authors\":[{\"name\":\"Mark Knopfler\",\"role\":\"Licensee\"}],\"changes\":[],\"extraTitle\":\"Test Marc Van Opstal eksempel\"}},\"alwaysDisplayComments\":false,\"backgroundOpacity\":0,\"displayAsButton\":false,\"buttonSize\":\"big\",\"goToSlideType\":\"specified\",\"invisible\":false,\"solution\":\"\"}],\"slideBackgroundSelector\":{}}],\"keywordListEnabled\":true,\"globalBackgroundSelector\":{},\"keywordListAlwaysShow\":false,\"keywordListAutoHide\":false,\"keywordListOpacity\":90},\"override\":{\"activeSurface\":false,\"hideSummarySlide\":false,\"summarySlideSolutionButton\":true,\"summarySlideRetryButton\":true,\"enablePrintButton\":false,\"social\":{\"showFacebookShare\":false,\"facebookShare\":{\"url\":\"@currentpageurl\",\"quote\":\"I scored @score out of @maxScore on a task at @currentpageurl.\"},\"showTwitterShare\":false,\"twitterShare\":{\"statement\":\"I scored @score out of @maxScore on a task at @currentpageurl.\",\"url\":\"@currentpageurl\",\"hashtags\":\"h5p, course\"},\"showGoogleShare\":false,\"googleShareUrl\":\"@currentpageurl\"}},\"l10n\":{\"slide\":\"Sivu\",\"score\":\"Pisteet\",\"yourScore\":\"Pisteesi\",\"maxScore\":\"Maksimipisteet\",\"total\":\"Yhteens\u00e4\",\"totalScore\":\"Kokonaispisteet\",\"showSolutions\":\"N\u00e4yt\u00e4 oikeat vastaukset\",\"retry\":\"Yrit\u00e4 uudelleen\",\"exportAnswers\":\"Vie teksti\u00e4\",\"hideKeywords\":\"Piilota sis\u00e4llysluettelo\",\"showKeywords\":\"N\u00e4yt\u00e4 sis\u00e4llysluettelo\",\"fullscreen\":\"Koko ruutu\",\"exitFullscreen\":\"Poistu kokoruudun tilasta\",\"prevSlide\":\"Edellinen sivu\",\"nextSlide\":\"Seuraava sivu\",\"currentSlide\":\"Nykyinen sivu\",\"lastSlide\":\"Viimeinen sivu\",\"solutionModeTitle\":\"Palaa yhteenvetosivulle\",\"solutionModeText\":\"Palaa yhteenvetosivulle\",\"summaryMultipleTaskText\":\"Useita teht\u00e4vi\u00e4\",\"scoreMessage\":\"Tulos:\",\"shareFacebook\":\"Jaa Facebookissa\",\"shareTwitter\":\"Jaa Twitteriss\u00e4\",\"shareGoogle\":\"Jaa Google+ palvelussa\",\"summary\":\"Yhteenveto\",\"solutionsButtonTitle\":\"N\u00e4yt\u00e4 kommentit\",\"printTitle\":\"Tulosta\",\"printIngress\":\"Miten haluat tulostaa t\u00e4m\u00e4n esityksen?\",\"printAllSlides\":\"Tulosta kaikki sivut\",\"printCurrentSlide\":\"Tulosta nykyinen sivu\",\"noTitle\":\"Ei otsikkoa\",\"accessibilitySlideNavigationExplanation\":\"K\u00e4yt\u00e4 vasenta tai oikeaa nuolin\u00e4pp\u00e4int\u00e4 siirty\u00e4ksesi eteen- tai taaksep\u00e4in t\u00e4ss\u00e4 esityksess\u00e4 silloin kun dia on valittuna.\",\"accessibilityCanvasLabel\":\"Piirtoalue. K\u00e4yt\u00e4 vasenta tai oikeaa nuolin\u00e4pp\u00e4int\u00e4 siirty\u00e4ksesi eteen- tai taaksep\u00e4in t\u00e4ss\u00e4 esityksess\u00e4 silloin kun dia on valittuna.\",\"containsNotCompleted\":\"@slideName sis\u00e4lt\u00e4\u00e4 suorittamattoman interaktion\",\"containsCompleted\":\"@slideName sis\u00e4lt\u00e4\u00e4 suoritetun interaktion\",\"slideCount\":\"Dia @index kautta @total\",\"containsOnlyCorrect\":\"@slideName sis\u00e4lt\u00e4\u00e4 vain oikeita vastauksia\",\"containsIncorrectAnswers\":\"@slideName sis\u00e4lt\u00e4\u00e4 v\u00e4\u00e4ri\u00e4 vastauksia\",\"shareResult\":\"Jaa tuloksesi sosiaaliseen mediaan\",\"accessibilityTotalScore\":\"Sait @score pistett\u00e4 @maxScore pisteest\u00e4\",\"accessibilityEnteredFullscreen\":\"Koko n\u00e4yt\u00f6n tila k\u00e4yt\u00f6ss\u00e4\",\"accessibilityExitedFullscreen\":\"Koko n\u00e4yt\u00f6n tilasta poistuttu\",\"confirmDialogHeader\":\"Submit your answers\",\"confirmDialogText\":\"This will submit your results, do you want to continue?\",\"confirmDialogConfirmText\":\"Submit and see results\",\"accessibilityProgressBarLabel\":\"Choose slide to display\",\"slideshowNavigationLabel\":\"Slideshow navigation\"}}"; +let library = 'H5P.CoursePresentation 1.24'; +let lang = 'de'; + +//596 +// params = "{\"media\":{\"disableImageZooming\":false,\"type\":{\"params\":{\"decorative\":false,\"contentName\":\"Image\",\"file\":{\"path\":\"https:\\/\\/api.test.ndla.no\\/image-api\\/raw\\/sye64b87.jpg\",\"mime\":\"image\\/jpeg\",\"externalId\":\"17845\",\"metadataUrl\":\"https:\\/\\/api.test.ndla.no\\/image-api\\/v3\\/images\\/17845\",\"alt\":\" \",\"title\":\"\",\"width\":1000,\"height\":672},\"alt\":\"wd\",\"title\":\"dds\"},\"library\":\"H5P.Image 1.1\",\"metadata\":{\"contentType\":\"Image\",\"license\":\"CC BY-NC\",\"title\":\"Untitled Image\",\"authors\":[{\"name\":\"Susana Vera\",\"role\":\"Licensee\",\"readonly\":true},{\"name\":\"Reuters\",\"role\":\"Licensee\",\"readonly\":true},{\"name\":\"NTB scanpix\",\"role\":\"Licensee\",\"readonly\":true}],\"changes\":[],\"licenseVersion\":\"4.0\",\"licenseExtras\":\"\",\"authorComments\":\"\",\"source\":\"http:\\/\\/www.scanpix.no\"},\"subContentId\":\"0b962c9f-2bfa-40a8-9b94-8dec057a1ad8\"}},\"text\":\"