H5P Guides

Creating Your Own H5P Plugin

This guide will provide all the necessary information and help you through the process of implementing your very own H5P plugin in a programming language of your choice. It will describe the different actions and what happens when H5P is used, and how you can implement this into your own system.

The target audience for this guide is Web Developers and Programmers.

Before continuing, I would like to encourage you to create generic components that can be shared with others and re-used. This way we can prevent people from doing the same work over again and instead focus on growing H5P.

A Brief Introduction To How H5P Works

H5P allows you to upload JavaScript libraries to your web application. Some of these libraries are known as Content Types, and they have a file which describes the start parameters they need to run.

The H5P Editor provides a generic set of widgets that can be used for lists, texts, numbers, files, images, boolean etc. The Editor reads the "recipe" for the content type start parameters, generates a form using the different widgets and, in turn, creates the parameters needed to “start” the content based on the user input. Different user input alters the start parameters and creates new interactivities, of the same kind, but with different content.

Data model and relations

Before creating your plugin, you must know which different Data Objects your plugin needs to hold and manage, and what the relationships between them are. This is also a great starting point for creating your Database Model. For further details on which properties each object holds, you should take a look at the H5P Specification.

  • Library – Represents a JavaScript library(/Content Type), e.g. H5P.MultiChoice.
  • Library Dependency – Represents a requirement to another Library, e.g. H5P.MultiChoice requires H5P.JoubelUI.
  • Library Translation – Represents an available translation for the given Library, e.g. the Italian translation for H5P.MultiChoice.
  • Content – Represents an instance of a Content Type(JavaScript library). It will hold the start parameters needed and a reference to the Library.
  • Content Dependency – Connects the Content to multiple Libraries which are required by the content. This is essentially a cache, but it makes it a whole lot easier to find out which files you must load when displaying the content.
  • Content Result – Simple tracking of how many points the User scores on each content. Not really required, but very nice to have.
  • Content User Data – Keeps track of different user data for each content, e.g. it can store the previous content state so that the user may resume where he left off. Not really required either.

Uploading H5P Files

When you've set up the database you'll want to get some test content into it before you continue. Adding support for uploading H5P files makes this a whole lot easier.

The H5P Content Creation Form

The first step to uploading H5P files is to set up a simple form with a file upload field. This will be the H5P Content Creation Form. Next, you’ll need to handle the form submission and do some checks to make sure that the file is a valid H5P file. This means that it must have all the required properties and assets for the content to work.

Validating the H5P file

You should check that the file has an .h5p extension and then try to extract the file into a temporary directory using a library for handling Zip files.

If the package is extracted without issues the next step is to go through the content of the temporary directory to check that everything is there. Start by trying to load the file named h5p.json. This is the file which describes what the package contains. You must verify that the properties of the file are in accordance with the H5P Specification.

You must remember to check that the coreApi property of h5p.json is compatible with the version of the H5P that you are implementing.

Next, you have to check for a directory named content and try to load the file content/content.json. The contents of this file is the Start Parameters required for the Content Type specified in h5p.json

Go through all the folders in the temporary directory and look for a file named library.json. If the file exists that means that it is is the directory of a Library. All other files and folders are not a part of the .h5p specification and may therefore be ignored.

For each library you should check to see if it has already been installed. If you do not have it in your database or you have an older Patch Version of the library(see version info?) you should install it.

At this stage you may also want to do a access check to see if the current user is allowed to install new libraries. It could also be useful to display a confirmation dialog to user, listing all the libraries that are going to be installed.

Afterwards, you must go through all of the depedencies specified in h5p.json. If you are missing one of the dependent libraries you should not save the content.

When that is done, all of the validation is done. Optionally, you can also filter all of the files against a file extension whitelist to avoid storing potentially harmful files. 

Storing the H5P content

After the content of the package have been validated you'll want to store the contents of the content folder and also all the libraries that you're installing. You must store the files from these in a public accessible location on your server. 

It’s recommend to have a separate folder for all H5P files. Content should go into h5p/content/[id], libraries into h5p/libraries/[name] and later on a directory for exports and one for tmp files used by the editor.

When everything is stored you may delete the .h5p file and the temporary directory it was extracted into.

Later on you may want to have a Cron job or something similar that cleans up any old tmp files or directories.

Viewing H5P content

For H5P content to be rendered properly on the web page, both settings and files need to be loaded, and an embed code must be inserted into the page where the content is to appear.

Including JavaScript Settings

For H5P content to be viewed properly an JavaScript object has to be included into the DOM before the content is “started”. The object must be named H5PIntegration and contain the following properties for all H5Ps:

window.H5PIntegration = {
  "baseUrl": "http://www.mysite.com", // No trailing slash
  "url": "/path/to/h5p-dir",          // Relative to web root
  "postUserStatistics": true,         // Only if user is logged in 
  "ajaxPath": "/path/to/h5p-ajax"     // Only used by older Content Types
  "ajax": {
    // Where to post user results
    "setFinished": "/interactive-contents/123/results/new", 
    // Words beginning with : are placeholders
    "contentUserData": "/interactive-contents/:contentId/user-data?data_type=:dataType&subContentId=:subContentId"
  },
  "saveFreq": 30, // How often current content state should be saved. false to disable.
  "user": { // Only if logged in !
    "name": "User Name",
    "mail": "user@mysite.com"
  },
  "siteUrl": "http://www.mysite.com", // Only if NOT logged in!
  "l10n": { // Text string translations
    "H5P": { 
      "fullscreen": "Fullscreen",
      "disableFullscreen": "Disable fullscreen",
      "download": "Download",
      "copyrights": "Rights of use",
      "embed": "Embed",
      "size": "Size",
      "showAdvanced": "Show advanced",
      "hideAdvanced": "Hide advanced",
      "advancedHelp": "Include this script on your website if you want dynamic sizing of the embedded content:",
      "copyrightInformation": "Rights of use",
      "close": "Close",
      "title": "Title",
      "author": "Author",
      "year": "Year",
      "source": "Source",
      "license": "License",
      "thumbnail": "Thumbnail",
      "noCopyrights": "No copyright information available for this content.",
      "downloadDescription": "Download this content as a H5P file.",
      "copyrightsDescription": "View copyright information for this content.",
      "embedDescription": "View the embed code for this content.",
      "h5pDescription": "Visit H5P.org to check out more cool content.",
      "contentChanged": "This content has changed since you last used it.",
      "startingOver": "You'll be starting over.",
      "by": "by",
      "showMore": "Show more",
      "showLess": "Show less",
      "subLevel": "Sublevel"
    } 
  },
  "loadedJs": ['multichoice.js'], // Only required when Embed Type = div
  "loadedCss": [],
  "core": { // Only required when Embed Type = iframe
    "scripts": ['jquery.js', 'h5p.js', ...], 
    "styles": ['h5p.css']
  }
};

The object also requires content specific properties. You’d want to load the following data for each content: Content Type, Start Parameters, Embed Type and a list of all the JavaScript and CSS files used by all the Content Dependencies for the Content.

You'll also have to load all Content User Data for the current user and content where preloaded = true. These are loaded into the contentUserData property, see below.

Before the start parameters are included on the page they should be run through a filter that make sure that they are compatible with the Content Type. See next sub-section.

The content specific part will look like this:

window.H5PIntegration.contents['cid-1234'] = {
  "library": "H5P.MultiChoice 1.5", // Library name + major version.minor version
  "jsonContent": "{\"filtered start parameters in json format\"}",
  "fullScreen": false, // No fullscreen support
  "exportUrl": "/path/to/download.h5p",
  "embedCode": "<iframe src=\"https://mysite.com/h5p/1234/embed\" width=":w" height=\":h\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"></iframe>",
  "resizeCode": "<script src=\"https://mysite.com/h5p-resizer.js\" charset=\"UTF-8\"></script>",
  "mainId": 1234,
  "url": "https://mysite.com/h5p/1234",
  "title": "How long is a rope?",
  "contentUserData": {
    0: { // Sub ID (0 = main content/no id)
      'state' => FALSE // Data ID
    }
  },
  "disable": 0,  // Flags for disabling frame and action buttons
  "styles": ['multichoice.css'],
  "scripts" ['multichoice.js']
};

Filtering Start Parameters

Not finished

Loading JavaScript files and stylesheets

You will also need to load the required JavaScript and CSS styles onto the page, and just like with the settings there are some files that are global and some that are content specific. It’s recommended that you reuse the existing H5P Core JS from the h5p-php-library. The following files are required on all pages where you wish to include H5Ps:
jquery.js, h5p.js, h5p-event-dispatcher.js, h5p-x-api-event.js, h5p-x-api.js, h5p-content-type.js and h5p.css.

You should not have to make changes to these files, but if you do please make sure to create Pull Requests for the originals. In the future these common files might be split into a separate project/repo.

When it comes to the content specific files only content with the embed type div should be included on the page. For other embed types the files are simply specified in the settings, under scripts and styles.

Embed Code

Now that all the scripts and setting are loaded onto the page there’s only one thing missing; the HTML where the interactive content will be inserted. The HTML code willl vary depending on the Embed Type.

For div it looks like:

<div class="h5p-content" data-content-id="1234"></div>

For iframe it looks like:

<div class="h5p-iframe-wrapper"><iframe id="h5p-iframe-1234" class="h5p-iframe" data-content-id="1234" style="height:1px" src="about:blank" frameBorder="0" scrolling="no"></iframe></div>

External Embed Code

Simply modify the example code above.

Tracking of Results

If the postUserStatistics and ajax.setFinished properties are present all that is needed is to create the AJAX endpoint which is specified by the ajax.setFinished property. The task of the endpoint is simply to store the posted data.

Content User Data

Not finished

Editing H5P content

Not finished

Adding options to disable the frame?

Library Managment

Not finished

Content Upgrade

Please read Content Upgrade.