Wie macht man ein Review eines H5P-Inhaltstyps?

Wenn jemand einen H5P-Inhaltstyp programmiert, ist es üblich, dass jemand anderes ihn überprüft. Das bedeutet, dass jemand nachschaut, ob alles wie geplant funktioniert. Außerdem prüft man, ob etwas gegen allgemeine Standards verstößt, ob etwas nicht den selbst festgelegten Richtlinien folgt, oder ob im Quelltext etwas verbessert werden könnte.

Der gesamte Prozess besteht in der Regel aus vielen verschiedenen Aspekten und Aufgaben. Es ist daher sinnvoll, eine Art Nachschlagewerk zu haben, das auf H5P-spezifische Themen hinweist. Sinnvoll ist auch eine Checkliste, um die Ergebnisse zu dokumentieren und den Entwickler*innen zurückzumelden. Das ist es, wofür dieser Beitrag gedacht ist.

Dieser Beitrag ist kein alles umfassender Leitfaden. Er wird definitiv nicht alle eher allgemeinen Programmierthemen abdecken, die nicht speziell auf H5P bezogen sind. Und er wird sich im Laufe der Zeit wahrscheinlich weiterentwickeln, da er unvollständig ist. Jede*r Entwickler*in hat seine/ihre eigene Vorstellung davon, wie bestimmte Dinge erledigt werden sollten. Dieser Beitrag basiert beispielsweise auf der Prämisse, dass es viele eher unerfahrene Entwickler gibt, die gerne H5P-Programmierung erlernen würden, aber leicht durch verschiedene Frameworks, Werkzeugketten oder Ansätze verwirrt werden. Und erfahrenere Leute werden bestimmte hier gemachte Empfehlungen nicht mögen, die aus dieser Prämisse resultieren. Das ist jedenfalls wahrscheinlich und völlig in Ordnung! Dieser Beitrag zielt darauf ab, dir eine gute Grundlage für das Erstellen Deines eigenen Werkzeugs zur Unterstützung von Reviews zu bieten. Du wirst es auf deine Bedürfnisse anpassen wollen und müssen!

Es gibt verschiedene Aspekte bei H5P-Inhaltstypen, die man überprüfen sollte. Je nachdem, was man sich genau ansieht, sind nicht alle relevant. Und die hier aufgeführten Aspekte überschneiden sich ein wenig. Sie stehen jedoch für bestimmte Dinge, die für das Verständnis von H5P hilfreich sind. Also werden wir man nicht zu akademisch.

Wenn du einen eigenen Inhaltstyp erstellt hast und sicherstellen möchtest, dass er bereit für die Veröffentlichung ist, oder wenn du sicherstellen möchtest, dass ein vorhandener Inhaltstyp für dich auf deiner Plattform installiert werden kann – oder wenn du einfach etwas zu H5P beitragen möchtest und sicherstellen willst, dass die Qualität der Inhaltstypen hoch ist – solltest du Folgendes überprüfen:

  • Funktion: Tut der Code das, was er tun soll?
  • Barrierefreiheit: Kann das Programm von Menschen mit Beeinträchtigungen genutzt werden?
  • Responsiveness: Funktioniert das Programm auf verschiedenen Bildschirmgrößen und Geräten?
  • Cross-Browser-Lauffähigkeit: Läuft der Code auf jedem gängigen Browser?
  • Cross-Plattform-Funktionalität: Läuft der Inhalts auf verschiedenen H5P-Integrationen?
  • Übersetzungen: Werden Übersetzungen korrekt gehandhabt?
  • Sicherheit: Ist das Programm frei von offensichtlichen Sicherheitsproblemen?
  • Code: Folgt der Code den H5P-Programmierrichtlinien und scheint er verständlich zu sein?
  • Repository: Folgt das Code-Repository bewährten Praktiken und der H5P-Spezifikation?

Für all diese Aspekte findest du einen Abschnitt mit einigen allgemeinen Hintergrundinformationen, einigen zusätzlichen Informationen, die sich auf H5P-spezifische Aspekte konzentrieren, ohne alle Details abzudecken (dafür sind separate Beiträge gedacht), sowie eine Checkliste mit verschiedenen Punkten.

Funktion

Hintergrund

Wir müssen sicherstellen, dass der Code wie erwartet funktioniert. Natürlich 🙂 Idealerweise wird der Quelltext von automatisierten Tests begleitet, die die Funktionalität für definierte Testfälle garantieren können. Dafür kann etwa Jest benutzt werden. Es kann allerdings auch sein, dass keine automatisierten Tests vorhanden sind. Testfälle decken oft auch nicht jedes mögliche Problem ab. Deshalb kann manuelles Testen erforderlich oder zumindest empfehlenswert sein.

Normalerweise gibt es einen Satz von Testfällen, der aus „User Stories“ besteht. Diese beschreiben, was Benutzer*innen tun sollen, beinhalten ggf. eine Anleitung, wie man dies erreichen soll, und enthalten eine Beschreibung dessen, was als Ergebnis erwartet wird. Zum Beispiel könnte eine sehr einfache Kombination aus diesen drei Dingen sein:

  • „User Story“: „Als Lernender möchte ich in der Lage sein, Audio ein- und auszuschalten, um Ablenkungen zu setzen/zu entfernen.“
  • Schritte: Klicke auf den Knopf mit dem Notensymbol.
  • Erwartetes Ergebnis: Wenn Audio eingeschaltet war, ist es jetzt ausgeschaltet.

Ähnlich wie bei automatisierten Tests decken „User Stories“ möglicherweise nicht immer alles ab – weil Benutzer*innen Dinge ausprobieren können, die man nicht vorausgesehen hatte oder der Teufel in widersprüchlichen Einstellungsdetails stecken kann. Deshalb sollte man auch ganz frei Dinge ausprobieren, die nicht von den Testfällen abgedeckt werden – das bedeutet dann auch, dass man einen neuen Testfall erstellen könnte … Man kann natürlich auch nur einige Testinhalte erstellen und prüfen, ob alles in Ordnung ist, aber das ist natürlich weniger gründlich und anfällig dafür, Dinge zu übersehen.

Im Wesentlichen muss man „nur“ alle verschiedenen Inhalts-Einstellungen testen, die man in verschiedenen Kombinationen anwenden kann 😀

Zusatzinformationen

Es gibt einige H5P-spezifische Funktionen, die ein Inhaltstyp möglicherweise aufgreift. Diese erfordern eine gesonderte Betrachtung.

Wiederaufnahmefunktion

H5P bietet eine Wiederaufnahmefunktion resume feature, die Inhaltstypen unterstützen können. Der H5P-Kern kann den aktuellen Zustand speichern und ihn wieder zur Verfügung stellen, wenn Nutzer*innen ihren Inhalt wieder öffnen. So können Nutzer*innen dort fortfahren, wo sie aufgehört haben.

„Question type contract“

H5P-Inhaltypen, die frage-antwort-artig aufgebaut sind, z. B. Multiple Choice, Dictation, usw., sollten den Question type contract implementieren. Durch Bereitstellen der darin geforderten Funktionen und Variablen kann sichergestellt werden, dass andere Inhaltstypen den eigenen Inhaltstyp als Unterinhalt verwenden inklusive der entsprechenden Funktionalität nutzen können. Das kann beispielsweise das Abrufen der Punktzahl sein oder das Zurücksetzen des Inhaltstyps in den Anfangszustand.

Die Funktionen des „Question type contract“ können überprüft werden, indem man einfach den H5P-Inhalt startet, die Entwicklungskonsole des Browsers öffnet, in den Kontext des H5P-Iframes wechselt und dann prüft, was der jeweilige Funktionsaufruf zurückgibt. Gibt man beispielsweise

H5P.instances[0].getScore();

ein, sollte die aktuelle Punktzahl zurückgegeben werden (egal ob die Nutzer*innen sie sehen oder nicht).

xAPI

H5P-Inhaltstypen können das Experience Application Programming Interface (xAPI) nutzen, um die Erfahrungen der Nutzer festzuhalten. Es wird meist nur für die Auswertung von Punkten verwendet, kann aber noch viel mehr.

Um die xAPI-Funktionalität zu überprüfen, sollte man sicherstellen, dass die xAPI-Statements korrekt ausgegeben werden, also die erforderlichen Informationen enthalten und valide sind.

  • Man kann von H5P verschickte xAPI-Statements im Browser prüfen, indem man in der Entwicklungskonsole danach lauscht:
    H5P.externalDispatcher.on('xAPI', (event) => {
      console.log(event.data.statement);
    });
  • Die xAPI-Spezifikation hält allgemeine Informationen dazu bereit, was die Statements an Informationen enthalten sollten.
  • Die Gültigkeit eines xAPI-Statements kann beispielsweise via https://lrs.io/ui/tools/xapi-statement-validator/ geprüft werden.

xAPI-Statements werden dazu verwendet, um Antworten, Noten usw. an die H5P-Integration und damit an die Plattform, die H5P betreibt, zu melden. Darum sollte man überprüfen, ob die relevanten Werte auf diesem Wirtssystem funktionieren. Man könnte z. B. die Abschlussverfolgung von Moodle und das Moodle-Gradebook als Referenz verwenden, um festzustellen, ob „Scoring“ gut funktioniert. Die Funktion getXAPIData als Teil des „Question type contract“ ist hier relevant.

Aktualisieren bestehender Inhalte

Manchmal ändert sich die in der Datei semantics.json definierte Datenstruktur der Parameter. Und manchmal tut sie das in einer Weise, die eine Änderung der Konfiguration des bestehenden Inhalts erfordert. Das ist z. B. notwendig, wenn eine Eigenschaft innerhalb der JSON-Hierarchie verschoben wird – der bestehende Wert muss an die neue Stelle kopiert werden und die alte Eigenschaft kann gelöscht werden.

H5P bietet einen Mechanismus, um das zu erledigen. Ein Inhaltstyp kann eine upgrades.js-Datei bereitstellen, die die erforderliche Funktion implementieren kann, um bestehende Inhalte auf eine neuere Version zu aktualisieren. Man muss überprüfen, ob das wie erwartet funktioniert.

Checkliste

  • Alle automatisierten Tests (falls vorhanden) laufen ohne Fehler durch.
  • Alle „User stories“ (falls vorhanden) liefern das erwartete Verhalten.
  • Wenn man verschiedene Inhalte erstellt, die keiner „User story“ folgen, scheint das zu funktionieren.
  • “Seltsame Eingaben” führen nicht zu Problemen.
    • Wenn Inhalt ohne jegliche Eingabe außer dem Titel gespeichert wird, stürzt der Inhaltstyp nicht ab, sondern setzt ggf. passende Vorgabewerte und informiert die Autor*innen über die fehlenden Daten.
    • Wenn ein Texteingabefeld viel Text enthält, passt der Inhalt diesen entsprechend an, schneidet ihn also nicht unpassend ab oder lässt ihn nicht über Ränder fließen, usw.
    • Es gibt keine HTML-Codierungs-Fehler: wenn beispielsweise ein Apostroph in ein Texteingabefeld eingetragen wird, wird es im Inhalt nicht als ' dargestellt.
  • Der Inhalt wird korrekt zurückgesetzt und verhält sich dann normal (falls der Inhalt zurückgesetzt werden kann).
  • Ein vorheriger Zustand wird wie beabsichtigt wiederhergestellt (falls die „Wiederaufnahmefunktion“ von H5P unterstützt wird).
  • Die xAPI-Statements, die der Inhaltstyp aussended, sind gültig.
  • Der „Question type contract“ ist korrekt implementiert (falls vorhanden).
  • Bestehende Inhalte mit unterschiedlichen Parametern werden korrekt mittelsupgrades.js aktualisiert (falls nötig).

Barrierefreiheit

Hintergrund

Ein Teil der Aufgabe von H5P besteht darin, allen Menschen die Nutzung interaktiver Inhalte im Internet zu ermöglichen. Das schließt auch Menschen mit bestimmten Beeinträchtigungen über verschiedene Geräte hinweg ein. Im Gegenzug muss man z. B. sicherstellen:

  • ausreichenden Farbkontrast,
  • eine angemessene Größe der Elemente, insbesondere auf mobilen Geräten,
  • die Möglichkeit, den Inhalt vollständig über die Tastatur zu bedienen,
  • die korrekte Ausgabe durch Bildschirmleser,
  • das Anbieten von textbasierten Alternativen für Menschen mit eingeschränktem Hörvermögen,

Um all dies zu gewährleisten, sollte der H5P-Inhalt mindestens dem WCAG 2.1-Standard entsprechen. WCAG hat verschiedene Stufen für die Beschreibung von Leistungen und Inhaltstypen sollten AA-konform sein.

Es gibt zahlreiche Dinge, die man überprüfen muss, und dieses Dokument wird sie nicht im Detail behandeln. Und das Prüfen besteht aus zwei Teilen. Es gibt Dinge, die man durch Testen des Inhalts überprüfen sollte, und einige Dinge müssen durch einen Blick in den Code überprüft werden. Für ersteres ist es erforderlich, einen Bildschirmleser laufen zu haben, den Menschen mit Sehbeeinträchtigungen nutzen können.

Bestimmte Dinge könnten automatisiert werden, wenn man z. B. axe (lokal oder als Github-Action) oder einige andere Werkzeuge verwendet, aber das wäre ein eigenes Kapitel für sich 🙂

Zusatzinformationen

Das Thema Barrierefreiheit hat an Bedeutung gewonnen, und es gibt zahlreiche Werkzeuge, mit denen man eine Website oder einen interaktiven Inhalt überprüfen kann. Man kann zum Beispiel „Lighthouse“ verwenden, das Teil der Entwicklungswerkzeuge von Chrome ist. Lighthouse bietet unter anderem eine Barrierefreiheitsprüfung und kann auf mögliche Probleme hinweisen. Man könnte auch das Siteimprove Accessibility Checker-Plugin für Chrome verwenden, das speziell für die Überprüfung der Barrierefreiheit entwickelt wurde.

Für den Farbkontrast ist es wichtig, CSS-Pseudoelemente separat im Auge zu behalten, da die oben genannten Werkzeuge den Kontrast dieser Elemente nicht prüfen. In diesem Fall kann man den Color Contrast Checker verwenden und die jeweiligen Farbwerte für den Vorder- und Hintergrund eingeben.

Wenn du einen H5P-Inhaltstyp mit Werkzeugen prüfen willst, musst du beachten, dass H5P innerhalb eines iframe läuft. Wenn du die Seite scannst, in der der H5P-Inhalt eingebunden ist, scannst du möglicherweise nur die Seite, aber nicht den H5P-Inhalt. Und selbst wenn der H5P-Inhalt gescannt wurde, würdest du auch Ergebnisse auf der Seite selbst erhalten, die sich nicht auf den H5P-Inhalt beziehen. Die Lösung besteht darin, den H5P-Inhalt über den Link zum Einbetten in einem separaten Fenster zu öffnen. Dies kann zu Problemen bei der Größenänderung führen, aber die Werkzeuge können nun nur auf den H5P-Inhalt zugreifen und diesen einem Test unterziehen.

Bildschirmleser

Es gibt eine Reihe verschiedener Bildschirmleser für unterschiedliche Betriebssysteme und Browser, die sich alle ein wenig unterschiedlich verhalten können. Zum Beispiel gibt es

Im Idealfall wird mehr als ein Bildschirmlesee überprüft, um auszuschließen, dass ein bestimmtes Problem vorliegt.

„Design-Patterns“

Es gibt allgemeine Elemente, die auf einer Webseite zur Interaktion angeboten werden können, z. B. eine Breadcrumb-Navigation, ein modaler Dialog oder eine Symbolleiste. Es gibt Entwurfsmuster für die ARIA-Unterstützung zur Erstellung dieser Elemente, und man sollte diese Muster verwenden. Dadurch wird sichergestellt, dass die Elemente gängigen Praktiken folgen, die z. B. auch alle Bildschirmleser befolgen sollten.

Checkliste

  • Der Inhaltstyp kann auch ausschließlich per Tastatur bedient werden.
    • Es gibt keine Fallen oder Sackgassen, aus der man sich nicht mit der Tastatur herausbegeben kann.
    • Elemente zeigen an, wenn sie gerade im Fokus sind.
  • Der Inhaltstyp gibt korrekte Anweisungen und ausreichende Informationen, wenn ein Bildschirmleser verwendet wird.
  • Alle Elemente haben ausreichenden Kontrast.
  • Alle Elemente haben eine ausreichende Grüße (Apple’s Richtlinie schlägt beispielsweise per Berührung ansteuerbare Flächen von wenigstens 44 px mal 44 px auf Mobilgeräten vor).
  • Die korrekten HTML-Tags werden benutzt (z. B. <button> für ein Button-Element statt eines <div> mir dem role-Attribut button, außer es gibt einen guten Grund dafür)
  • Es gibt keine redundanten Attribuite, z. B. role=”button” für ein <button>-Element.
  • Elemente mit einer Aktion, die keinen Text enthalten, besitzen ein aria-label-Attribut oder ein aria-labelledby-Attribut.
  • Das title-Attribut für Tooltips wird nicht verwendet, da es zu Problemem bei Bildschirmlesern führen kann. Falls man Tooltips benötigt, sollte man eine eigene Lösung dafür verwenden (Der H5P Core soll eine Tooltip-Klasse erhalten, die man verwenden kann).
  • ARIA-Entwurfsmuster werden verwenden, wenn sie relevant sind.
  • Diskutierbar: <h>-Tags sollten in H5P-Inhaltstypen nicht verwendet werden. Inhaltstypen laufen (normalerweise) in iframes und wissen nicht, innerhalb welches Überschriften-Levels sie dargestellt werden. Es ist also wahrscheinlich, dass das gewählte Level nicht zur Umgebung passt und keinen Sinn ergibt.
  • Inhalt kann mit der Zoom-Funktion des Browsers vergrößert werden.
  • Der Fokus geht nicht verloren, zum Beispiel beim Löschen eines Elements.

Responsiveness

Hintergrund

Inhalte sollten responsive sein und sich an verschiedene Bildschirmgrößen anpassen. Das bedeutet nicht, dass einfach alles verkleinert wird, sondern dass das visuelle Layout oder sogar die Art und Weise, wie der Benutzer mit dem Inhalt interagiert, geändert wird.

Man kann zum Beispiel den responsiven Modus von Chrome verwenden, um verschiedene Bildschirmgrößen zu simulieren. Man kann auch BrowserStack verwenden.

Checkliste

  • Die Vollbildoption funktioniert wie erwartet (falls verfügbar).
  • Der Inhalt bleibt auf breiten Bildschirmen nutzbar.
  • Der Inhalt bleibt auf schmalen Bildschirmen nutzbar.
  • Der Inhalt läuft vernünftig sowohl im horizontalen wie auch im vertikalen Betrieb.
  • Alle relevanten Informationen können auf allen Geräten erhalten werden (Geräte mit einer Berührungssteuerung können beispielsweise keine hover-Effekte darstellen und benötigen eine Alternative, wenn die Informationen relevant sind).
  • Die rechte Taste der Maus wird nicht benötigt (funktioniert nicht auf Mobilgeräten).

Cross-Browser-Lauffähigkeit

Hintergrund

Nutzer*innen können viele verschiedene Browser auf unterschiedlichen Geräten verwenden. Browser können leider HTML und CSS unterschiedlich interpretieren oder nicht die gleichen JavaScript-Funktionen oder APIs unterstützen. Es gibt zwar einige technische Möglichkeiten, diese Probleme einzuschränken, doch sollte der H5P-Code (Inhaltstypen) in verschiedenen Browsern überprüft werden. Dies gilt sowohl für den Editor als auch für die Ansicht. Wenn allerdings kein benutzerdefiniertes Editor-Widget benutzt wird oder das normale Editor-Formular verwendet wird – statt ein visualler Editor wie in Branching Scenario – liegt der Schwerpunkt auf der Ansicht.

H5P Group verspricht, die letzten drei Versionen aller wichtigen Browser zu unterstützen. Daher ist es ratsam, diese ebenfalls zu überprüfen. Angesichts der hohen Veröffentlichungsfrequenz von Chrome, Firefox und Edge scheint es in Ordnung zu sein, nur die jeweils neueste Version zu prüfen.

Die relevante Codebasis der Browser ist bei allen Desktop-Betriebssystemen gleich, so dass es nicht notwendig ist, diese auf verschiedenen Betriebssystemen zu überprüfen. Safari auf MacOS unterscheidet sich jedoch von Safari auf iOS. Letzterem fehlen oft bestimmte Funktionen oder er implementiert Dinge anders.

Es besteht keine wirkliche Notwendigkeit, auch Chrome oder Firefox unter iOS zu überprüfen, da Apple andere Browserhersteller zwingt, ihre Webkit-Rendering-Engine zu verwenden, die von Safari unter iOS genutzt wird. Infolgedessen sind Chrome und Firefox (oder andere Browser auf iOS) im Wesentlichen Safari mit einigen anderen Funktionen.

Um einen Inhaltstyp auf all diesen verschiedenen Browsern und Betriebssystemen oder sogar Geräten zu testen, ohne dass man sie physisch besitzen muss, kann man den Online-Dienst BrowserStack nutzen.

Zusatzinformationen

Es gibt eine Reihe von Problemen, die bereits zu Problemen bei der Cross-Browser-Kompatibilität geführt haben. Aber da die Engines ständig weiterentwickelt werden, können diese Informationen hier veraltet sein. Eine gute Quelle, um festzustellen, welche Funktion von welchem Browser unterstützt wird, ist der Dienst “Can I use”.

Probleme, die in der Vergangenheit aufgetreten sind, sollten überprüft werden, wenn sie für den Inhaltstyp relevant sind:

  • Das webm-Videoformat wird von Safari auf iOS nicht voll unterstützt und wird von Safari auf MacOS erst ab Version 16 voll unterstützt. (H5P.Video und Inhaltstypen, die diese Bibliothek benutzen)
  • Safari auf iOS lädt auf Mobilgeräten (außer im WLAN) keine Audiodateien im Voraus herunter um Bandbreite zu sparen. Das führt in der Regel zum verzögerten Abspielen von Tondateien. Man sollte die Web Audio API nutzen und Tondateien puffern, falls das relevant ist. (z. B. H5P.SingleChoiceSet, H5P.JigsawPuzzle, H5P.GameMap)
  • Es gibt eine Web Speech API, und deren Untersützung für Spracherkennung durch Browser nimmt langsam Fahrt auf, aber es gibt unterschieliche Erfahrungen damit. Chrome ist der Browser, der die Schnittstelle schon sehr lange unterstützt, Edge sollte sie mittlerweile ebenfalls unterstützen, Firefox hat experimentelle Unterstützung, die in einem speziellen Menü aktiviert werden muss, Safari scheint sie seit Version 14.x ebenfalls zu unterstützen, aber scheint einen besonderen Prefix für die but also seems to require a special prefix for the SpeechRecognition API zu benötigen. (H5P.SpeakTheWords)
  • Wenn die „HTML Drag and Drop API“ genutzt wird, skaliert Firefox die ziehbaren Elemente gegebenenfalls herunter, auch wenn das nicht gewünscht ist. Eine Umgehungslösung ist es, das ziehbare Element in ein Bild zu kopieren und dieses zu ziehen. (H5P.SortParagraphs)
  • Die Date-Funktion von Firefox entspricht völlig der Spezifikation, aber andere Browser sind ein wenig toleranter beim Interpretieren des Funktionsarguments. (H5PEditor.DateTime)
  • Chrome (auf Android) gibt immer die tatsächliche horizontale and verticale Größe für screen.width und screen.height zurück, je nachdem, wie die Nutzer*innen das Gerät halten. Safari (auf iOS) ermittelt diese Werte jedoch im Porträtmodus und behält diese dann bei. Das heißt beispielsweise, dass auch wenn man ein Gerät mit einer Bildschirmauflösung von 1920 px mal 1080 px horizontal hält, screen.widthbei 1080 stehen wird und screen.height bei 1920. Eine Frage der Perspektive … (H5P.Bingo)

Checkliste

  • Code läuft auf (den letzten drei Versionen von) Chrome auf dem Desktop.
  • Code läuft auf (den letzten drei Versionen von) Firefox auf dem Desktop.
  • Code läuft auf (den letzten drei Versionen von) Edge auf dem Desktop.
  • Code läuft auf (den letzten drei Versionen von) Safari auf MacOS.
  • Code läuft auf (den letzten drei Versionen von) Safari auf iOS.
  • Code läuft auf (den letzten drei Versionen von) Chrome auf Android.

Cross-Plattform-Funktionalität

Hintergrund

Dieser Aspekt ist eher ein Sonderfall, da die H5P-Inhaltstypen nicht von einer bestimmten H5P-Integration oder Plattformen wie Moodle oder WordPress abhängen sollen. Die Plattform selbst kann jedoch Dinge tun, die mit dem, was der H5P-Inhalt zu erreichen versucht, in Konflikt geraten.

Leider ist es nicht möglich, im Vorfeld eine klar definierte Checkliste zu erstellen. Allerdings werden hier Themen aufgeführt, die in der Vergangenheit bekanntermaßen Probleme verursacht haben.

Checkliste

  • Wenn der Inhaltstyp und die Plattform beide URLs in der Browserzeile manipulieren(z. B. über das hashchange-Ereignis oder URL-Fragmente), gibt es keine Konflikte.

Übersetzungen

Hintergrund

H5P soll von allen genutzt werden, daher sollte es so viele Übersetzungen wie möglich geben. H5P verfügt über einen Mechanismus, der es ermöglicht, dass Inhaltstypen übersetzbar sind.

Kurz und bündig: Übersetzbare Felder werden in semantics.json definiert und als solche mit den Parametern des Inhalts an die Ansicht übergeben. Im Verzeichnis languages sollte eine .en.json-Datei vorhanden sein, die eine abgespeckte Version von semantics.json ist und nur die zu übersetzenden Textpassagen enthält. Diese .en.json-Datei ist eine Vorlage für Übersetzungen in andere Sprachen, z. B. nb.json für norwegisches Bokmål oder sw.json für Suaheli.

Es gibt mindestens zwei Möglichkeiten, um zu prüfen, ob alles übersetzbar ist. Eine Möglichkeit besteht darin, den Code durchzugehen und sicherzustellen, dass jeder Textstring aus der Definition in semantics.json stammt. Die andere Möglichkeit ist, die Übersetzungen im Editor zu überschreiben. Und zu sehen, ob diese Übersetzung verwendet wird. Es ist wichtig zu bedenken, dass nicht alle übersetzbaren Felder sichtbar sind, aber sie können auch in „ARIA-Labels“ verwendet werden.

Die Gültigkeit der Übersetzungen kann mit dem H5P CLI Werkzeug mit dem Befehl h5p check-translations --diff $LANGUAGE_CODE $LIBRARY_DIR überprüft werden

Es gibt auch eine Beta-Github-Aktion zur Überprüfung von H5P-Übersetzungen, die ein Bash-Skript enthält, mit dem die Gültigkeit der Dateien (einschließlich der Gültigkeit der json-Datei) kontinuierlich überprüft werden kann, oder mit dem alles manuell überprüft werden kann.

Die Korrektheit der Übersetzungen kann natürlich nicht überprüft werden, es sei denn man ist sehr polyglott 🙂

Checkliste

  • Es gibt eine .en.json-Vorlage im language-Ordner.
  • Alle Textphrasen, die im Inhaltstyp verwendet werden, sind übersetzbar.
  • Es gibt keine ungenutzten Felder für Übersetzungen in semantics.json.
  • Die Struktur der Übersetzungsdateien ist identisch mit der Struktur von semantics.json (z. B. keine fehlenden Felder. weil semantics.json aktualisiert wurde aber nicht die Übersetzungsdateien).
  • Der Dateiname der Übersetungsdateien enthält keine Großbuchstaben.

Sicherheit

Hintergrund

Sicherheit ist wichtig, um sicherzustellen, dass niemand böswillige Angriffe durchführen kann, insbesondere nicht durch Cross-Site-Scripting (XSS), um JavaScript-Code in H5P-Inhalte einzuschleusen.

Für H5P-Inhaltstypen bedeutet dies im Wesentlichen, dass alle Möglichkeiten geprüft werden müssen, wie Autor*innen einen Text in den Editor eingeben können. Zum Beispiel könnte man JavaScript in jedes Textfeld des Editors einfügen, etwa <script>alert('XSS!');</script>. Wenn der Inhaltstyp angezeigt wird, sollte dieser Code nicht ausgeführt werden, so dass die entsprechende Warnung nicht erscheint.

Der nächste offensichtliche Aspekt ist die Überprüfung von Abhängigkeiten und Code von Drittanbietern auf bekannte Sicherheitslücken. Es ist ziemlich einfach, npm-Abhängigkeiten zu überprüfen, indem man ein Repository klont und npm install ausführt, das dann über bekannte Sicherheitslücken informiert. Bei anderem Code von Drittanbietern kann die Überprüfung auf Sicherheitslücken einen enormen Aufwand bedeuten, wenn man sie ernst nimmt.

Theoretisch sollte man auch überprüfen, ob alle npm-Abhängigkeiten gepflegt werden, damit jemand tatsächlich sicherheitsrelevante Fehler beheben kann. In der Praxis ist das aber kaum möglich.

Checkliste

  • Es kann kein JavaScript-Code über Texteingabefelder im Editor eingeschleust werden.
  • Alle npm-Abhängigkeiten sind frei von bekannten Sicherheitslücken (falls npm genutzt wird).
  • Alle npm-Abhängigkeiten werden noch gepflegt (falls npm genutzt wird).
  • Alle anderen Codebstandteile Dritter sind frei von bekannten Sicherheitslücken.
  • Der Inhaltstyp stellt einem Dateinamen immer den Pfad der Bibliothek oder des Inhalts voran (um zu verhindern das über Manipulation der URL zu anderen Seiten geleitet wird).

Code

Hintergrund

Die Codeüberprüfung ist nicht wirklich dazu gedacht, Fehler zu finden, auch wenn das vorkommen kann. Sie soll vielmehr sicherstellen, dass der Code auf einer Architekturebene gut geschrieben ist, auch wenn dies in vielen Fällen nicht objektiv beurteilt werden kann. Vor allem, wenn mehr als eine Person an dem Code arbeitet, ist es wichtig, dass er Richtlinien folgt, damit er leichter zu verstehen und zu pflegen ist.

H5P Group hat einige Best Practices und Coderichtlinien herausgegeben. Man mag ihnen folgen oder denken, dass man es besser machen könnte. Aber das ist nicht der Punkt. Das Ziel ist es, den Code ähnlich aussehen zu lassen, egal wer ihn geschrieben hat, so dass es einfacher wird, jeden H5P-Code zu verstehen. Glücklicherweise gibt es eine gebrauchsfertige eslint-Konfigurationen, die deinen Code-Editor auf Verstöße gegen die H5P-Coderichtlinien hinweist und viele davon automatisch beheben kann.

Zusatzinformationen

Allgemein

H5P-Inhaltstypen sind in JavaScript geschrieben. Die meisten von ihnen verwenden kein Framework wie React oder Svelte, weil Inhaltstypen oft sehr klein sind und es sich nicht lohnt, sie mit dem Overhead eines Frameworks zu belasten. Es gibt keine Richtlinie, die besagt, dass man sie nicht verwenden könnte, aber wenn man unerfahrenen Entwicklern den Weg durch den H5P-Code erleichtern will, sollte man keine neuen Konzepte einführen, ohne dass dies wirklich notwendig ist.

Das Gleiche gilt für TypeScript. Es ist nichts dagegen einzuwenden, Typsicherheit einzuführen, aber auch hier gilt – angesichts des ganz anderen Aussehens und der Handhabung des Codes und der eher geringen Größe der meisten H5P-Inhaltstypen: die Verwendung ist nicht unbedingt notwendig und weniger erfahrene Entwickler könnte TypeScript verwirren. Das ist nicht mit einem Argument gegen TypeScript zu verwechseln. Es ist lediglich ein Argument für den Versuch, den Code in verschiedenen Inhaltstypen ähnlich zu halten. Es ist wahrscheinlicher, dass das H5P-Kernteam JavaScript-Annotations unterstützen wird (da es sich zu einem Standard entwickelt) als TypeScript (oder Flow oder Hegel).

H5P-Group verwendet JSDoc, um Typinformationen hinzuzufügen. Insbesondere gehört auch eine verbale Beschreibung dessen dazu, was eine Funktion tut oder wofür eine Variable verwendet wird. Ja, diese Informationen sollten ohnehin durch gute Benennung klar sein werden.

Stylelint könnte genutzt werden, um SCSS zu „linten“ (verwendet lediglich die empfohlene Konfiguration).

Ein Framework, das jeder H5P-Inhaltstyp mindestens einmal verwenden muss (aber nicht bereitstellen muss), ist jQuery. Es ist immer noch Teil des H5P-Kerns, wird aber irgendwann entfernt werden, da JavaScript sich weiterentwickelt hat und die Verwendung von jQuery weitgehend überflüssig gemacht hat. Neuer Code sollte nicht darauf zurückgreifen. Der Code würde mit jQuery definitiv anders aussehen und anders zu handhaben sein.

Das H5P-Kernteam verwendet JSDoc-Kommentare, um auf die Typen von Variablen und Rückgabewerten hinzuweisen. Das hat den Vorteil, dass man kurze Erklärungen für Variablen und Funktionen hat (zusätzlich zur hoffentlich klaren Namensgebung). Man kann die eslint-Konfiguration so ändern, dass jsdoc verwendet wird, um auch die korrekte Verwendung von JSDoc-Kommentaren zu prüfen.

Many older H5P content types created by the H5P core team still use ES5 syntax that did not allow import modules and had no class notation or arrow functions. Using those should be the preferred way, as the handling of this is particularly confusing for developers without much JavaScript experience.

Viele ältere H5P-Inhaltstypen, die vom H5P-Kernteam erstellt wurden, verwenden noch die ES5-Syntax, die keine Importmodule zuließ und keine Klassennotation oder Pfeilfunktionen hatte. Diese sollten aber bevorzugt verwendet werden, da die Handhabung von ES5 besonders für Entwickler ohne viel JavaScript-Erfahrung verwirrend ist.

Bei Verwendung einer „Build-Chain“ ist es in Ordnung, SCSS/SASS zu verwenden, da die Verschachtelungssyntax das Verständnis der Stylesheet-Hierarchie sehr erleichtert.

Größenänderung

H5P-Inhaltstypen werden in der Regel in einem iframe ausgeführt, und Browser können programmatische Änderungen der Höhe des Inhalts nicht automatisch erkennen. Inhaltstypen müssen den H5P-Kern explizit darüber informieren, dass sie ihre Größe geändert haben, so dass der H5P-Kern seinerseits die Größe des iframe ändern kann. Umgekehrt kann der iframe in der Größe verändert werden (z. B. bei der Größenänderung des Browserfensters), und der H5P-Kern wird den Inhaltstyp über diese Änderung informieren, damit er sich bei Bedarf an die neue Größe anpassen kann. Es kann auch vorkommen, dass ein übergeordneter Inhaltstyp seine „Kinder“ über eine Größenänderung unabhängig von der Größe des iframe informiert. H5P bietet für diesen Zweck einen speziellen Nachrichtenmechanismus an, der verwendet werden sollte, und nicht etwa benutzerdefinierte Browser-Resize-Listener oder ähnliches.

Man sollte ein resize-Ereignis in einem Inhaltstyp nicht zu oft auslösen, da der dadurch anlaufende Prozess ziemlich viele Ressourcen benötigen kann. Eine Größenänderung sollte nur ausgelöst werden, wenn sie wirklich benötigt wird.

„Sanitizing“

Autor*innen füllen möglicherweise nicht alle Felder im Editor aus. Und übergeordnete Inhaltstypen mit einem visuellen Editor instanziieren einen untergeordneten Inhaltstyp ohne jegliche Parameter. Das heißt, dass alle relevanten Parameter beim Instanziieren eines Inhaltstyps bereinigt werden sollten, um einen Absturz zu vermeiden. Das ist auch ein Sicherheitsaspekt.

Elemente und ihre „id“-Attribute

Wenn es erforderlich ist, einem DOM-Element ein id-Attribut zu geben, sollte sichergestellt werden, dass es eindeutig ist. Es ist zu bedenken, dass ein übergeordneter Inhaltstyp mehrere Instanzen eines untergeordneten Inhaltstyps enthalten kann, so dass es nicht ausreicht, z. B. eine statische id zu verwenden. Man kann H5P.createUUID vom H5P-Kern verwenden, um einen Universally Unique Identifier (UUID) zu erhalten.

Elementklassen

Auch wenn es nicht unbedingt erforderlich ist, sollten alle DOM-Elemente einen eigenen CSS-Klassennamen haben, um die Anpassung des visuellen Erscheinungsbildes zu erleichtern.

Exponierte Funktionen und Variablen

Es wird empfohlen, alle wichtigen Funktionen und Variablen öffentlich zu machen, so dass sie von außen leicht gelesen und verändert werden können, z. B. um den Zustand einer Variablen zu ermitteln oder eine Funktion zu überschreiben, um das Verhalten zu ändern. Die Sicherheit sollte nicht vernachlässigt werden, aber wir schreiben ja auch keinen Code für ein Atomkraftwerk. Wenn du es vorziehst, dass Variablen und Funktionen abgeschottet sind, bleibt dir das unbenommen.

Annahmen über das DOM

Man sollte nicht davon ausgehen, dass das DOM eines H5P-Inhaltstyps an den DOM-Baum des Dokuments angehängt wird, wenn der Inhaltstyp instanziert oder angehängt wird. Dies kann zu einem späteren Zeitpunkt geschehen. Man könnte einen einmaligen intersectionObserver für die Fälle verwenden, in denen das DOM des Inhaltstyps an das DOM des Dokuments angehängt werden muss, bevor eine Aktion durchgeführt wird.

library.json

Die Datei library.json beschreibt einige grundlegende Eigenschaften der H5P-Bibliothek. Eine der zu setzenden Eigenschaften ist embedTypes. Sie kann die Werte div und iframe annehmen und bestimmt, ob der Inhaltstyp in einem bloßen div-Element oder in einem iframe dargestellt wird. Zwar haben iframes ihre eigenen Probleme, aber sie schützen die H5P-Inhaltstypen vor den Stylesheets der übergeordneten Seite, die bei Verwendung von div zu oft in den Inhalt einfließen. Deshalb sollte iframe bevorzugt werden.

semantics.json

Das group-Element in semantics.json verhält sich unintuitiv. Es gibt ein einzelnes  Objekt zurück, wenn das fields-Array nur ein Element enthält, aber ein Array, wenn es mehrere Elemente enthält. Wenn man eine Gruppe mit nur einem Element verwenden möchte, sollte man ein „Dummy“-Element vom Typ boolean hinzufügen und ihm die Eigenschaft/Wert "widget": "none" geben. Auf diese Weise verhält sich der Editor so, als gäbe es nur ein Element, während in den Parametern ein Array mit den Werten für das Editor-Element und das unsichtbare Dummy-Element vorhanden ist. Auf diese Weise muss man, wenn die Gruppe später mehr als ein Element aufnehmen muss, die Parameterdatenstruktur der bestehenden Inhalte in upgrades.js nicht aktualisieren.  Das würde sonst eine Erhöhung der Minor-Versionsnummer erfordern, was möglicherweise zu mehreren Versions-Upgrades in anderen Inhaltstypen führen würde – eine Aufgabe, die bei H5P-Releases nicht so viel Spaß macht 🙂

Checkliste

  • Größenänderung funktioniert.
    • Wenn sich der Inhalt in der Höhe ändern, wird auch der H5P-iframe entsprechend in der Höhe geändert und es wird nichts am Ende abgeschnitten.
    • Wenn es Unterinhalts gibt, werden sie vom Oberinhalt über eine notwendige Größenänderung informiert.
    • Zirkuläre Anhängkeiten – ein Unterinhalt, der eine Größenänderung beim Oberinhalt auslöst, der eine Größenänderung beim Unterinhalt auslöst, der … – werden vermieden.
    • Eigene resize-Ereignisse werden sparsam ausgeführt.
    • Es werden keine eigenen resize-Mechanismen benutzt.
  • Die Handhabung des DOMs ist in Ordnung.
      • Alle id-Attribute von DOM-Elementen sind eindeutig und haben eine ggf. zufällige Komponente.
      • Alle DOM-Elemente haben eine CSS-Klasse.
      • Der Inhaltstyp funktioniert als Unterinhalt und geht ggf. davon aus, dass das eigene DOM noch nicht am DOM des Oberinhalts angedockt wurde.
  • Alle relevanten Parameter werden bereinigt, wenn der Inhaltstyp instantiiert wird.
  • Es werden keine Bilbiotheken verwendet, die einen bestimmten Browser erfordern.
  • Es werden keine serverseitigen Komponenten verwendet, die nicht vom H5P-Kern gesteuert werden.
  • Der Code folgt komplett den H5P-Coderichtlinien.
  • TypeScript wird ordentlich verwendet, also beispielsweise ohne any, usw. (falls TypeScript überhaupt verwendet wird).
  • Alle Funktionen haben ordentliche JSDoc-Kommentare (falls JSDoc verwendet wird, nicht TypeScript).
  • Das jQuery-Framework wird nicht benutzt, außer um Aufrufe vom H5P-Kern zu beantworten oder dessen Funktionen zu nutzen.
  • Alle wichtigen Funktionen sind exponiert.
  • Der Code verwendet Klassennotaton und Pfeilfunktionen, keine ES5-Definitionen.
  • Es gibt keine „magic numbers“.
  • Funktionen und Variablen sind ordentlich benannt und selbsterklärend.
  • Das  H5PIntegration-Objekt wird nicht direkt abgefragt (außer es gibt noch keinen anderen Weg, um an die gewünschten Informationen zu gelangen).
  • Kommentare werden hauptsächlich dafür genutzt, um zu erklären, warum etwas getan wird, nicht was der Quelltext tut (das sollte durch den Quelltext klar sein).
  • Die library.json-Datei folgt der H5P-Spezifikation für library.json.
    • Alle benötigten Abhängigkeiten für die Ansicht und den Editor sind angegeben.
    • Die embedTypes-Eigenschaft nutzt [“iframe”] als Wert.
  • Die semantics.json-Datei folgt der H5P-Spezification für semantics.json.
    • Es gibt keine Konflikte zwischen Versionen eines Inhaltstyps im kompletten Abhängigkeitsbaum.
    • Ein group-Feld hat wenigstens zwei Einträge in seinem fields-Feld.
  • library.json/semantics.json: Die aktuellsten Versionen von Unterinhaltstypen werden genutzt, sofern keine spezielle Version benötigt wird.
  • Es gibt keine gar nicht verwendeten H5P-Biblioteken als Abhängigkeit in library.json.
  • Es gibt keine TODO-Kommentare für bedeutsame Dinge, die jetzt erledigt werden sollten oder die eine größere Diskussion erfordern oder andere größere Aufgaben nötig machen.

Repository

Es gibt bestimmte Dinge, die ein Repository für einen H5P-Inhaltstyp befolgen muss oder zumindest befolgen sollte, um in das H5P-Entwicklungsökosystem zu passen. Einige sind direkt von der H5P-Spezifikation abgeleitet, andere basieren auf der eingangs erwähnten Prämisse, um die Dinge für eher unerfahrene Entwickler einfach zu halten.

Zusatzinformationen

.h5pignore

Wenn du H5P-Bibliotheken mit dem H5P-CLI-Werkzeug packst, solltest du darauf achten, dass nur relevante Dateien in die Bibliothek aufgenommen werden. Beispielsweise könnte eine package.json-Datei in der Bibliothek landen und auf einem Hostsystem installiert werden, da json-Dateien in H5P-Bibliotheken erlaubt sind. Das würde nichts kaputt machen, aber unnötig die Ressourcen der Plattform verschwenden.

Die .h5pignore-Datei funktioniert auf die gleiche Weise wie eine .gitignore-Datei. Während git letztere verwendet, um Dateien und Verzeichnisse zu identifizieren, die nicht auf einen entfernten Server übertragen werden sollen, verwendet H5P-CLI erstere, um Dateien innerhalb des Repositorys zu identifizieren, die für die Erstellung von Distributionsdateien relevant sind, aber nicht in die H5P-Bibliothek aufgenommen werden sollen, z. B. Quellcode und Konfigurationsdateien.

package.json

Bei der Verwendung von npm enthält die Datei package.json eine Reihe von Konfigurationseinträgen. Um unerfahrenen Entwickler*innen die Arbeit mit H5P-Repositories so einfach wie möglich zu machen, sollten die Skripte überall gleich benannt werden:

  • build, um die Verteilungsdateien für die Produktion zu erstellen
  • watch, um die Distributionsdateien kontinuierlich für die Entwicklung zu bauen
  • lint, um Dateien zu überprüfen, ohne etwas zu ändern

Werkzeuge in der „Build chain“

Es gibt viele Werkzeuge, die man für die Erstellung einer Build-Chain verwenden kann. Und es wird viele geben, die besser sind als die, die jetzt vorgeschlagen werden. Der Grundgedanke hier ist, dass wir versuchen, Repositories für unerfahrene Entwickler*innen zugänglich zu machen, also solltest du die Werkzeuge benutzen, die H5P Group normalerweise benutzt:

  • eslint für das Linting und das Formatieren (und nicht etwa prettier, was für den H5P-Styleguide ohnehin nicht funktionieren würde) – das kann sich ändert, sobald eslint Regeln zum Formatieren aus seinem Funktionsumfang entfernt
  • webpack für die Bündelung (anstelle von vite)

Es ist üblich, Babel als Teil einer Build-Chain zu verwenden, um Code in ältere JavaScript-Versionen zu transpilieren, die von älteren Browsern möglicherweise sonst nicht verstanden werden. Wenn man die neuesten JavaScript-Mätzchen nicht verwendet, kann man möglicherweise auch ohne Babel leben, aber es ist gut, es zu haben.

H5P-Anhängigkeiten

Der Kern von H5P kann immer nur eine Version einer H5P-Bibliothek verarbeiten. Wenn du andere H5P-Bibliotheken als Abhängigkeiten verwendst, entweder über library.json oder über semantics.json, musst du sicherstellen, dass entlang des gesamten Abhängigkeitsbaums keine Abhängigkeiten zu zwei verschiedenen Versionen einer Bibliothek bestehen. Andernfalls kann der Inhalt abstürzen.

Zum Beispiel könnte man annehmen, dass H5P.Column Abhängigkeiten zu H5P.CoursePresentation 1.0 und H5P.TrueFalse 1.3 hat. Diese Version von Course Presentation könnte eine Abhängigkeit zu H5P.TrueFalse 1.2 haben. Für eine True-False-Frage wären zwei verschiedene Versionen erforderlich. Wenn ein/e Autor*in eine True-False-Frage und eine Kurspräsentation mit einer True-False-Frage in eine Column einfügt, könnte dies zu Problemen führen. Die Abhängigkeit in der Column müsste ebenfalls H5P.TrueFalse 1.2 sein.

Das Werkzeug H5P CLI verfügt über einen Befehl, um diese Art von Inkonsistenzen zu überprüfen: h5p find-inconsistencies $LIBRARY_DIR. Alle Abhängigkeiten müssen sich jedoch im selben Verzeichnis befinden, damit sie überprüft werden können. Daher ist es am einfachsten, dies innerhalb des libraries-Verzeichnisses einer H5P-Integration durchzuführen, wenn man nicht ohnehin das H5P CLI-Werkzeug für die Entwicklung verwendet.

Checkliste

  • Es befindet sich eine icon.svg-Datei im Repository, das den Inhaltstyp im H5P-Hub repräsentiert.
  • Die package.json-Datei ist OK (falls benutzt).
    • Es gibt ein build-Skript in package.json, das die Distributionsdateien für den Produktiveinsatz baut.
    • Es gibt ein watch-Skript in package.json, das die Distributionsdateien kontinuierlich für die Entwicklung baut.
    • Es gibt ein lint-Skript in package.json`, das die Dateien ohne Beheben von Fehlern überprüft (falls ein Linter benutzt wird).
  • Alle Dateien, die nicht in der Bibliothek benötigt werden, sind in .h5pignore aufgeführt.
  • Die Distributionsdateien, die in Produktion gehen sollen (mittels build-Skript) sind „minified“ und enthalten keine Sourcemaps.
  • Eine passende Lizenz ist in einer LICENSE-Datei beschrieben und in library.json gesetzt.
  • eslint wird für das Linting benutzt (falls relevant).
  • webpack wird für das Bundling benutzt (falls relevant).
  • „Branch“-Namen
    • Der Haupt-„Branch“ des Repositories heißt master.
    • Es gibt einen „Branch“ namens release, in dem die aktuell veröffentlichte Fassung zu finden ist.

Dieser Beitrag wurde gemeinsam mit der NDLA erstellt und steht unter einer CC0-Lizenz.