From 907685be5c757798dbeb4432b93b10be216e2f83 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Tue, 26 Sep 2023 15:59:25 -0500 Subject: [PATCH 1/5] feat: allow to view in CMS --- h5pxblock/h5pxblock.py | 13 +- h5pxblock/static/js/src/h5pxblock.js | 184 +++++++++++++-------- h5pxblock/static/js/src/installRequired.js | 7 + 3 files changed, 130 insertions(+), 74 deletions(-) create mode 100644 h5pxblock/static/js/src/installRequired.js diff --git a/h5pxblock/h5pxblock.py b/h5pxblock/h5pxblock.py index 47863184..83a9c3d2 100644 --- a/h5pxblock/h5pxblock.py +++ b/h5pxblock/h5pxblock.py @@ -170,7 +170,7 @@ class H5PPlayerXBlock(XBlock, CompletableXBlockMixin): ) h5p_content_meta = Dict(scope=Scope.content) - has_author_view = True + #has_author_view = True def resource_string(self, path): """Handy helper for getting resources from our kit.""" @@ -241,10 +241,10 @@ def get_context_studio(self): "h5p_xblock": self, } - def author_view(self, context=None): - html = self.render_template("static/html/author_view.html", context) - frag = Fragment(html) - return frag + #def author_view(self, context=None): + # html = self.render_template("static/html/author_view.html", context) + # frag = Fragment(html) + # return frag def studio_view(self, context=None): context = self.get_context_studio() @@ -273,7 +273,8 @@ def student_view(self, context=None): template = self.render_template("static/html/h5pxblock.html", context) frag = Fragment(template) frag.add_css(self.resource_string("static/css/student_view.css")) - frag.add_javascript_url('https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/main.bundle.js') + frag.add_javascript(self.resource_string("static/js/src/installRequired.js")) + #frag.add_javascript_url('https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/main.bundle.js') frag.add_javascript(self.resource_string("static/js/src/h5pxblock.js")) user_service = self.runtime.service(self, 'user') user = user_service.get_current_user() diff --git a/h5pxblock/static/js/src/h5pxblock.js b/h5pxblock/static/js/src/h5pxblock.js index 9649c643..81573ff0 100644 --- a/h5pxblock/static/js/src/h5pxblock.js +++ b/h5pxblock/static/js/src/h5pxblock.js @@ -1,85 +1,133 @@ /* Javascript for H5PPlayerXBlock. */ function H5PPlayerXBlock(runtime, element, args) { - + function initH5P(H5PStandalone, service) { if (!window.H5PPlayerXBlockPromises) { - window.H5PPlayerXBlockPromises = []; + window.H5PPlayerXBlockPromises = []; } - const contentUserDataUrl = runtime.handlerUrl(element, 'user_interaction_data'); - const contentxResultSaveUrl = runtime.handlerUrl(element, 'result_handler'); + const contentUserDataUrl = runtime.handlerUrl( + element, + "user_interaction_data" + ); + const contentxResultSaveUrl = runtime.handlerUrl(element, "result_handler"); const playerPromise = function edXH5PPlayer(el) { - if (el && $(el).children('.h5p-iframe-wrapper').length == 0) { - const userObj = { 'name': args.user_full_name, 'mail': args.user_email }; - const options = { - h5pJsonPath: args.h5pJsonPath, - frameJs: 'https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/frame.bundle.js', - frameCss: 'https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/styles/h5p.css', - frame: args.frame, - copyright: args.copyright, - icon: args.icon, - fullScreen: args.fullScreen, - user: userObj, - saveFreq: args.saveFreq, - customJs: args.customJsPath, - contentUserData: [{ - state: args.userData - }], - ajax: { - contentUserDataUrl: contentUserDataUrl - } + if (el && $(el).children(".h5p-iframe-wrapper").length == 0) { + const userObj = { name: args.user_full_name, mail: args.user_email }; + const options = { + h5pJsonPath: args.h5pJsonPath, + frameJs: + "https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/frame.bundle.js", + frameCss: + "https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/styles/h5p.css", + frame: args.frame, + copyright: args.copyright, + icon: args.icon, + fullScreen: args.fullScreen, + user: userObj, + saveFreq: args.saveFreq, + customJs: args.customJsPath, + contentUserData: [ + { + state: args.userData, + }, + ], + ajax: { + contentUserDataUrl: contentUserDataUrl, + }, + }; + + return new H5PStandalone.H5P(el, options).then(function () { + $(el).siblings(".spinner-container").find(".spinner-border").hide(); + $(el).show(); + if (service === "cms") { + return; + } + H5P.externalDispatcher.on("xAPI", (event) => { + let hasStatement = event && event.data && event.data.statement; + if (!hasStatement) { + return; } - return new H5PStandalone.H5P(el, options).then(function(){ - $(el).siblings('.spinner-container').find('.spinner-border').hide(); - $(el).show(); - H5P.externalDispatcher.on("xAPI", (event) => { + let statement = event.data.statement; + let validVerb = + statement.verb && + statement.verb.display && + statement.verb.display["en-US"]; + if (!validVerb) { + return; + } - let hasStatement = event && event.data && event.data.statement; - if (!hasStatement) { - return; - } - - let statement = event.data.statement; - let validVerb = statement.verb && statement.verb.display && statement.verb.display['en-US']; - if (!validVerb) { - return; - } - - let isCompleted = statement.verb.display['en-US'] === 'answered' || - statement.verb.display['en-US'] === 'completed'; - let isChild = statement.context && statement.context.contextActivities && - statement.context.contextActivities.parent && - statement.context.contextActivities.parent[0] && - statement.context.contextActivities.parent[0].id; - - // Store only completed root events. - if (isCompleted && !isChild) { - $.ajax({ - type: "POST", - url: contentxResultSaveUrl, - data: JSON.stringify(event.data.statement) - }); - } - // uncomment to see all xAPI events triggered by H5P content - //console.log("xAPI event: ", event); - }); - - }); - } + let isCompleted = + statement.verb.display["en-US"] === "answered" || + statement.verb.display["en-US"] === "completed"; + let isChild = + statement.context && + statement.context.contextActivities && + statement.context.contextActivities.parent && + statement.context.contextActivities.parent[0] && + statement.context.contextActivities.parent[0].id; + + // Store only completed root events. + if (isCompleted && !isChild) { + $.ajax({ + type: "POST", + url: contentxResultSaveUrl, + data: JSON.stringify(event.data.statement), + }); + } + // uncomment to see all xAPI events triggered by H5P content + //console.log("xAPI event: ", event); + }); + }); + } }; - const h5pel = document.getElementById('h5p-' + args.player_id); + const h5pel = document.getElementById("h5p-" + args.player_id); window.H5PPlayerXBlockPromises.push(playerPromise(h5pel)); window.H5PXBlockPlayersInitlized = false; $(function ($) { - if (!H5PXBlockPlayersInitlized) { - window.H5PXBlockPlayersInitlized = true; - window.H5PPlayerXBlockPromises.reduce( - function(prevPromise, curPromise) { - return prevPromise.then(curPromise); - }, Promise.resolve() - ); - } + if (!H5PXBlockPlayersInitlized) { + window.H5PXBlockPlayersInitlized = true; + window.H5PPlayerXBlockPromises.reduce(function ( + prevPromise, + curPromise + ) { + return prevPromise.then(curPromise); + }, + Promise.resolve()); + } }); + } + + if (typeof require === "function") { + console.log("load CMS"); + require(["h5p"], function (H5PStandalone) { + initH5P(H5PStandalone, "cms"); + }); + } else { + console.log("load LMS"); + loadJS(function () { + initH5P(window.H5PStandalone, "lms"); + }); + } +} + +function loadJS(callback) { + if (window.H5PStandalone) { + callback(); + } else { + // Load jsMind dynamically using $.getScript + $.getScript( + "https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/main.bundle.js" + ) + .done(function () { + // Assign jsMind to the window object after it's loaded + window.H5PStandalone = H5PStandalone; + callback(); + }) + .fail(function () { + console.error("Error loading H5PStandalone."); + }); + } } diff --git a/h5pxblock/static/js/src/installRequired.js b/h5pxblock/static/js/src/installRequired.js new file mode 100644 index 00000000..dd70083f --- /dev/null +++ b/h5pxblock/static/js/src/installRequired.js @@ -0,0 +1,7 @@ +(function (require) { + require.config({ + paths: { + h5p: "https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/main.bundle", + }, + }); +}).call(this, require || RequireJS.require); From 81d011337dab2960ff75f5d218c8e80ef2803445 Mon Sep 17 00:00:00 2001 From: Jhon Vente Date: Thu, 28 Sep 2023 10:34:27 -0500 Subject: [PATCH 2/5] fix: h5p content in studio --- h5pxblock/h5pxblock.py | 1 + h5pxblock/static/js/src/h5pxblock.js | 36 ++++++++++------------ h5pxblock/static/js/src/installRequired.js | 2 +- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/h5pxblock/h5pxblock.py b/h5pxblock/h5pxblock.py index 83a9c3d2..83e88bea 100644 --- a/h5pxblock/h5pxblock.py +++ b/h5pxblock/h5pxblock.py @@ -251,6 +251,7 @@ def studio_view(self, context=None): template = self.render_template("static/html/studio.html", context) frag = Fragment(template) frag.add_css(self.resource_string("static/css/studio.css")) + frag.add_javascript(self.resource_string("static/js/src/installRequired.js")) frag.add_javascript(self.resource_string("static/js/src/studio.js")) frag.initialize_js( "H5PStudioXBlock", diff --git a/h5pxblock/static/js/src/h5pxblock.js b/h5pxblock/static/js/src/h5pxblock.js index 81573ff0..0a57fff4 100644 --- a/h5pxblock/static/js/src/h5pxblock.js +++ b/h5pxblock/static/js/src/h5pxblock.js @@ -10,7 +10,7 @@ function H5PPlayerXBlock(runtime, element, args) { ); const contentxResultSaveUrl = runtime.handlerUrl(element, "result_handler"); - const playerPromise = function edXH5PPlayer(el) { + const playerPromise = async function edXH5PPlayer(el) { if (el && $(el).children(".h5p-iframe-wrapper").length == 0) { const userObj = { name: args.user_full_name, mail: args.user_email }; const options = { @@ -36,12 +36,11 @@ function H5PPlayerXBlock(runtime, element, args) { }, }; - return new H5PStandalone.H5P(el, options).then(function () { + try { + await new H5PStandalone.H5P(el, options); $(el).siblings(".spinner-container").find(".spinner-border").hide(); $(el).show(); - if (service === "cms") { - return; - } + H5P.externalDispatcher.on("xAPI", (event) => { let hasStatement = event && event.data && event.data.statement; if (!hasStatement) { @@ -73,12 +72,20 @@ function H5PPlayerXBlock(runtime, element, args) { type: "POST", url: contentxResultSaveUrl, data: JSON.stringify(event.data.statement), + }) + .done(function () { + // handle fine request here + }) + .fail(function () { + // handle fails request here }); } - // uncomment to see all xAPI events triggered by H5P content - //console.log("xAPI event: ", event); }); - }); + + return Promise.resolve("Result successfully"); + } catch (error) { + return Promise.reject(error.message); + } } }; @@ -89,24 +96,16 @@ function H5PPlayerXBlock(runtime, element, args) { $(function ($) { if (!H5PXBlockPlayersInitlized) { window.H5PXBlockPlayersInitlized = true; - window.H5PPlayerXBlockPromises.reduce(function ( - prevPromise, - curPromise - ) { - return prevPromise.then(curPromise); - }, - Promise.resolve()); - } + Promise.all(window.H5PPlayerXBlockPromises).then((_) => {}); + } }); } if (typeof require === "function") { - console.log("load CMS"); require(["h5p"], function (H5PStandalone) { initH5P(H5PStandalone, "cms"); }); } else { - console.log("load LMS"); loadJS(function () { initH5P(window.H5PStandalone, "lms"); }); @@ -122,7 +121,6 @@ function loadJS(callback) { "https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/main.bundle.js" ) .done(function () { - // Assign jsMind to the window object after it's loaded window.H5PStandalone = H5PStandalone; callback(); }) diff --git a/h5pxblock/static/js/src/installRequired.js b/h5pxblock/static/js/src/installRequired.js index dd70083f..bc97dbe6 100644 --- a/h5pxblock/static/js/src/installRequired.js +++ b/h5pxblock/static/js/src/installRequired.js @@ -1,7 +1,7 @@ (function (require) { require.config({ paths: { - h5p: "https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/main.bundle", + h5p: "https://cdn.jsdelivr.net/npm/h5p-standalone@3.5.1/dist/main.bundle", }, }); }).call(this, require || RequireJS.require); From 917713f105434ff1e9e60648100982983540b5f4 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Wed, 11 Oct 2023 14:09:55 -0500 Subject: [PATCH 3/5] chore: remove commented code --- h5pxblock/h5pxblock.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/h5pxblock/h5pxblock.py b/h5pxblock/h5pxblock.py index 83e88bea..25790c91 100644 --- a/h5pxblock/h5pxblock.py +++ b/h5pxblock/h5pxblock.py @@ -170,7 +170,6 @@ class H5PPlayerXBlock(XBlock, CompletableXBlockMixin): ) h5p_content_meta = Dict(scope=Scope.content) - #has_author_view = True def resource_string(self, path): """Handy helper for getting resources from our kit.""" @@ -241,11 +240,6 @@ def get_context_studio(self): "h5p_xblock": self, } - #def author_view(self, context=None): - # html = self.render_template("static/html/author_view.html", context) - # frag = Fragment(html) - # return frag - def studio_view(self, context=None): context = self.get_context_studio() template = self.render_template("static/html/studio.html", context) @@ -275,7 +269,6 @@ def student_view(self, context=None): frag = Fragment(template) frag.add_css(self.resource_string("static/css/student_view.css")) frag.add_javascript(self.resource_string("static/js/src/installRequired.js")) - #frag.add_javascript_url('https://cdn.jsdelivr.net/npm/h5p-standalone@3.6.0/dist/main.bundle.js') frag.add_javascript(self.resource_string("static/js/src/h5pxblock.js")) user_service = self.runtime.service(self, 'user') user = user_service.get_current_user() From 5e231e36491fc2c67f0494aab676b9d1d13b9fdd Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Wed, 11 Oct 2023 14:30:48 -0500 Subject: [PATCH 4/5] fix: catch undefined require js --- h5pxblock/static/js/src/installRequired.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/h5pxblock/static/js/src/installRequired.js b/h5pxblock/static/js/src/installRequired.js index bc97dbe6..e75e4d58 100644 --- a/h5pxblock/static/js/src/installRequired.js +++ b/h5pxblock/static/js/src/installRequired.js @@ -1,7 +1,11 @@ -(function (require) { - require.config({ - paths: { - h5p: "https://cdn.jsdelivr.net/npm/h5p-standalone@3.5.1/dist/main.bundle", - }, - }); -}).call(this, require || RequireJS.require); +try { + (function (require) { + require.config({ + paths: { + h5p: "https://cdn.jsdelivr.net/npm/h5p-standalone@3.5.1/dist/main.bundle", + }, + }); + }).call(this, require || RequireJS.require); +} catch (e) { + console.log("Unable to load h5p-standalone via requirejs"); +} From 1e8ae48b9bdf11f8f572b5514b360371b7494076 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Thu, 7 Dec 2023 08:30:23 -0500 Subject: [PATCH 5/5] chore: remove author view.html --- h5pxblock/locale/ar/LC_MESSAGES/text.mo | Bin 3830 -> 3450 bytes h5pxblock/locale/ar/LC_MESSAGES/text.po | 10 ---------- h5pxblock/locale/en/LC_MESSAGES/text.po | 10 ---------- h5pxblock/locale/es_419/LC_MESSAGES/text.mo | Bin 4088 -> 3770 bytes h5pxblock/locale/es_419/LC_MESSAGES/text.po | 12 ------------ h5pxblock/locale/es_ES/LC_MESSAGES/text.mo | Bin 4088 -> 3770 bytes h5pxblock/locale/es_ES/LC_MESSAGES/text.po | 12 ------------ h5pxblock/static/html/author_view.html | 5 ----- 8 files changed, 49 deletions(-) delete mode 100644 h5pxblock/static/html/author_view.html diff --git a/h5pxblock/locale/ar/LC_MESSAGES/text.mo b/h5pxblock/locale/ar/LC_MESSAGES/text.mo index 562320a4b5a0fa8c61a43f99f63cb80ade46bffa..a5766890cdc5a60bb4262fc0706302cbd46841cb 100644 GIT binary patch delta 792 zcmXxiODIH99LMov?hND2c)y2aj7OssS&@-KS+G~*wei?s!NRTCSy*W-mA&2AC^j}q zi3L)MoyfvMgi^l0GvnW!^SNi{&OQJC|J={IpKA9Z;7u5!kx|QN&M+o|9Udm)8ME;P zGw~gB@CWnI=cN7@zzNn7)bDZh;Wp;u4sPN;=3>xm?{iHD7cG3zi`6)Ve9SsiCvIXH z9;7|Ra@KdK2fra7^UG9+9(E025S3sz`f&hN$Ox*~No+N)F>743^Ti$p@jQLQD^{}3 zr|bk>X~81a$62WgFHi~GVKF{o7{72Ao7lAmk5CERFsZ-?yv1*9W`A=>e) z_23w)<#8OvEmY>$*oB|zb%=f{(Gk=})2KuiFoX$If+tvrmso<2=xU>PE(WoUehuRi zj^iQrqeGdxFN!q6wwoet=$+BS6xu|gBow6#9&L|7YekSz&rl&W*tHo|vCx_Q2k(}x zTx;GJ8>&!Iz-X{nsfi}obEkPWbR;?^wOU;*XXyQ@V3^jSx%t@I;%eALW6LvhizXR$ UW;2pr--eUC_pf-8XSosQ4?A5%TmS$7 delta 1143 zcmXxi%TE(Q9Ki7biadPs5EVqmf=WYF4r;>5M9>gKLhz(;*|v)`Hd_K)qz9=WAaXMX z{{f;!5>)I#NlZMc2fdl#Y&0ZZG)kI0;Ni@1h-%XHexlFVk>ULE|l+ku?I(U^KY?`{wi+APdJ9(aH~>j z6$@-Gl*GeK+`vA}U^NE#nx?A5qu7AEu_xyMR?#0r*8!yQSj_&)(2$dU##Q`-4fvi_<;9=af|cZJ6ua;gzCmJ8f3X7#Ii3XCjm(8SE z{tAgftzihiV)_V;GQyQeTW}|y#|rF4*(iZ$@ByB~b?n0C9ZH?XG|GD~k;M4-EgMO8 z4p8N~q{y05IGJE!!KoUeM>wdQ;v_ zZ(4g(X2oRaPMe3C#v}tvX3?ARZfo<{WXvOPQZxCSxhZY#`(w+wcg><%kuP+kL?6=L zxHn;*Wj_?T`FER2>n@oEf5mA=mt}IsoACEAPw5BwddVz%vt~Ic3qLU#?Jvge6SCKW Xyg-<88xlyZ)QIl4!zhblr5y>D$TM$aA4q_d&i;9A%XrqmSs2PeF&|O4y zDRfW@DQ+d=P`bD}lnzcE>}Kd6P^se3?-Q@!A?JS1$-Vd7_kGX#7yj0mIS=~wj25Fe z&<8wbow(-ZLK}3k%sVL2vo9n<*GEMtEe zXh9E8b)X;h;7e3c0~o+%tiuiD75jt2knh_* z17&c85AXz)@hL{|4C8o-?HDdGYsNRI`zfr&EgYqKS*&F4VG*qtM6F+s?=g-c+{Gy0 zw=WEm_ybjm=UfQhUZXPU!>>4j6<9^x_*sO@3rwK?pT%dG#wt9-7W|IHWWVth=23yi zi7L~|pr3(UM7_9!bNCsF$pX}|9cxjM_oAK~L5gDYs7#lz8$VzV9=r2v>|ox-KFTD9 z@neAcQ?WvMv?p5petXJ9hf3uQx;LV(3Rq30R`yy@`^&cq*)^kkhOLJ2UDH{4y)(0utErVX>zQ1fo=N2n Vosc(ISDbZnSEbwD+#i31^B?kWMpXa+ delta 1210 zcmYk*Pe>F|9Ki9{u3Dy9tyyWAuk}wW8YCqOQ3R_bp%DE8!-(14x9;FDv(3(KtU-c@ z^3bJ&ojgQ`5)?uf!X${0=-RPUhfduh=ulni``ewM$Ig7_&AfT<_xrustuU3y!YlNrP{uvQdR)Uj zxPkYTT2ODfXyV3sp31;WIE2?x20p|-T*euEgDu!jG#!{grcg;di4)j{rO-!skp452 z`Cg;U^8xEvU-j%%s*@Z27{iMwncT!(IEzw&JRZk+lnSii0KP|!-^1rIUOG(w3{F$c zDcnc@1(R^8HgN>sVKeKiUtG*#1L?P-kG=Q+bGVNDR2yX?d8JV@>cb5@hkNlG@>72} zjbam7$o(-qjo0utF5!OsiVJZrese+as+0O$z+RM0v)GMGq3b9wencC;AhD}S_G18V zpd_$}GVTddWwnZu`Aa;Do0!BOE!1Cbj1uM$2BFWfi~c91g4KPKzU=G&t&fiEy?h4; z!Urc%f{+SIlR%{Wl8{u7wW~=<;t|<@Y3((t=R=&OywZAVC_e6L2J#P)5=&OHCDLTC zrF!!1XpVpF7#9hSBOLO5Nu?-sbxTxY%w(;i6AbmMq~)fKqmrH*7&p*q%hf5P^S&wB z=B~+T+b!4`qbH}XYR`4b6)V~rnRbj-FuIs`JS(FohA-=M_UtRWjQ30&O|Iu`=awB3Z1GuV0Ifj630kcE-~M&*4N!MME6b zzzRjnvF9ycX9(H%z>xqe9gUA8w>)2Gt&-;t>VmDU?Jbw3G*jd(oipj2H%N~DY|&_5 ks)a49ND?_KZ5x-4q@ax}BT`88xlyZ)QIl4!zhblr5y>D$TM$aA4q_d&i;9A%XrqmSs2PeF&|O4y zDRfW@DQ+d=P`bD}lnzcE>}Kd6P^se3?-Q@!A?JS1$-Vd7_kGX#7yj0mIS=~wj25Fe z&<8wbow(-ZLK}3k%sVL2vo9n<*GEMtEe zXh9E8b)X;h;7e3c0~o+%tiuiD75jt2knh_* z17&c85AXz)@hL{|4C8o-?HDdGYsNRI`zfr&EgYqKS*&F4VG*qtM6F+s?=g-c+{Gy0 zw=WEm_ybjm=UfQhUZXPU!>>4j6<9^x_*sO@3rwK?pT%dG#wt9-7W|IHWWVth=23yi zi7L~|pr3(UM7_9!bNCsF$pX}|9cxjM_oAK~L5gDYs7#lz8$VzV9=r2v>|ox-KFTD9 z@neAcQ?WvMv?p5petXJ9hf3uQx;LV(3Rq30R`yy@`^&cq*)^kkhOLJ2UDH{4y)(0utErVX>zQ1fo=N2n Vosc(ISDbZnSEbwD+#i31^B?kWMpXa+ delta 1210 zcmYk*Pe>F|9Ki9{u3Dy9tyyWAuk}wW8YCqOQ3R_bp%DE8!-(14x9;FDv(3(KtU-c@ z^3bJ&ojgQ`5)?uf!X${0=-RPUhfduh=ulni``ewM$Ig7_&AfT<_xrustuU3y!YlNrP{uvQdR)Uj zxPkYTT2ODfXyV3sp31;WIE2?x20p|-T*euEgDu!jG#!{grcg;di4)j{rO-!skp452 z`Cg;U^8xEvU-j%%s*@Z27{iMwncT!(IEzw&JRZk+lnSii0KP|!-^1rIUOG(w3{F$c zDcnc@1(R^8HgN>sVKeKiUtG*#1L?P-kG=Q+bGVNDR2yX?d8JV@>cb5@hkNlG@>72} zjbam7$o(-qjo0utF5!OsiVJZrese+as+0O$z+RM0v)GMGq3b9wencC;AhD}S_G18V zpd_$}GVTddWwnZu`Aa;Do0!BOE!1Cbj1uM$2BFWfi~c91g4KPKzU=G&t&fiEy?h4; z!Urc%f{+SIlR%{Wl8{u7wW~=<;t|<@Y3((t=R=&OywZAVC_e6L2J#P)5=&OHCDLTC zrF!!1XpVpF7#9hSBOLO5Nu?-sbxTxY%w(;i6AbmMq~)fKqmrH*7&p*q%hf5P^S&wB z=B~+T+b!4`qbH}XYR`4b6)V~rnRbj-FuIs`JS(FohA-=M_UtRWjQ30&O|Iu`=awB3Z1GuV0Ifj630kcE-~M&*4N!MME6b zzzRjnvF9ycX9(H%z>xqe9gUA8w>)2Gt&-;t>VmDU?Jbw3G*jd(oipj2H%N~DY|&_5 ks)a49ND?_KZ5x-4q@ax}BT` -

{% trans 'Content can be previewed inside LMS only'%}

-

{% trans 'Please upload H5P content by clicking the "Edit" button if you have not already uploaded it.' %}

- \ No newline at end of file