Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No result for simple Course Presentation #111

Open
Adrian-S opened this issue Jan 21, 2020 · 5 comments
Open

No result for simple Course Presentation #111

Adrian-S opened this issue Jan 21, 2020 · 5 comments

Comments

@Adrian-S
Copy link

I'm using the WordPress plugin and I have created a 3 slide ‘Course Presentation’ with no quiz in them.

It seams that it will not send a finish statement when all slides are viewed.

I would expect a h5p_setFinished action to be sent, but I only get h5p_contents_user_data. I have installed “H5PxAPIkatchu” plugin to check for xapi calls. There are no ‘completed’ verbs registered for this simple course with no quiz. No summary page also.

test-22.zip - This is my course. It can't get more basic than this.

Not sure what to do in order to track completion of these courses.

@fmarc-mdsol
Copy link

Hi @Adrian-S did you manage to get this work? I am also looking for a solution to this.

@Adrian-S
Copy link
Author

Yes, but it's ugly. I had to add some JS that looks like this:

if(typeof H5P !== 'undefined') {
	var $iframes = jQuery('iframe');
	if($iframes.length > 0) {
		var frameH5P = $iframes[0].contentWindow.H5P;
		if(typeof frameH5P !== 'undefined') {
			var hInst = frameH5P.instances[0];
			hInst.on('changedSlide', function(e) {
				// is this the last slide ?
				if(hInst.currentSlideIndex == hInst.slides.length - 1) {
					// finish it with score some score
					frameH5P.setFinished(hInst.contentId, 1, 1, 0);
				}
			});
		}
	}
}

H5P exists on main window and inside the frame. But the main idea is to get the H5P and Instance that is running.

If you have multiple types of H5P moduled it might be whise to try and check what they are. For example: if( frameH5P.instances[0] instanceof frameH5P.QuestionSet ) {}

Some info here: https://h5p.org/node/2466#setFinished
and here: https://h5p.org/documentation/api/H5P.html#.setFinished

Hope it helps, althow I still expect an official fix / response.

@otacke
Copy link
Contributor

otacke commented Apr 18, 2020

Hi @Adrian-S and @fmarc-mdsol
I have a few comments, hope they are useful.

Using setFinished() 1

If you really require to invoke setFinished() with those parameters, you should use

hInst.trigger('finish', {
  score: 1,
  maxScore: 1,
  time: 0
});

Calling setFinished() directly is not recommended.

Using setFinished() 2

If you just require setFinished to be called and can spare the time property - seems you set it to 0 anyway, you can just trigger the xAPI completed event which will result in setFinished to be called. This would achieve the same (except for time to be passed, but Course Presentation doesn't either) and not omit other things that are supposed to be triggered.

hInst.triggerXAPICompleted(1, 1);

Adding scripts to H5P content

If you want to add scripts to modyify H5P content, you should use the alter_scripts hook that H5P offers. Please see https://h5p.org/node/2692.

Not only will it spare you the hassle of finding your way inside the iframe, but you also won't have to deal with finding the correct Course Presentation instance or dealing with all of them one by one.

You can use the alter_scripts hook to filter for Course Presentation contents and then pass custom scripts only to those. This would leave you with something like this:

H5P.externalDispatcher.on('initialized', function() {
  // Is always Course Presentation if filtered in alter_scripts
  var instance = H5P.instances[0];
  var done = false;

  // This will trigger setFinished (not supposed to be called directly)
  var triggerFinish = function() {
    instance.triggerXAPICompleted(1, 1);
  };

  // setFinished will fire anyway when there's a summary slide
  if (instance.summarySlideObject.$summarySlide) {
    return;
  }

  if (instance.slides.length === 1) {
    // Just one slide
    triggerFinish();
  }
  else {
    // Multiple slides, wait for last
    H5P.on(instance, 'changedSlide', function() {
      if (!done && instance.getCurrentSlideIndex() === instance.slides.length - 1) {
        done = true; // Only trigger once (feels more correct than retriggering)
        triggerFinish();
      }
    });
  }
});

@fmarc-mdsol
Copy link

Hi @Adrian-S and @otacke , thanks for your prompt replies and suggestions. This is really useful!

@DrizzlyOwl
Copy link

Thanks @otacke!

We're using the H5P WordPress integration for LearnDash and ran into this exact same issue.

For those interested, you can inject the code displayed above by doing the following:

functions.php

Add this code block to your functions.php

/**
 * This action allows us to enqueue our own JS files to be loaded whenever H5P content is being rendered.
 *
 * @param array &$scripts List of JavaScripts to be included.
 * @param array $libraries The list of libraries that has the scripts.
*  @param string $embed_type Possible values are: div, iframe, external, editor.
 */
add_action('h5p_alter_library_scripts', function (&$scripts, $libraries, $embed_type) {
    $scripts[] = (object) [
        'path' => get_stylesheet_directory_uri() . '/h5p-presentation-completion.js',
        'version' => ''
    ];
}, 10, 3);

JS

Create a file in your theme folder called h5p-presentation-completion.js and add the code block referenced above (included here for brevity)

H5P.externalDispatcher.on('initialized', function() {
  // Is always Course Presentation if filtered in alter_scripts
  var instance = H5P.instances[0];
  var done = false;

  // This will trigger setFinished (not supposed to be called directly)
  var triggerFinish = function() {
    instance.triggerXAPICompleted(1, 1);
  };

  // setFinished will fire anyway when there's a summary slide
  if (instance.summarySlideObject.$summarySlide) {
    return;
  }

  if (instance.slides.length === 1) {
    // Just one slide
    triggerFinish();
  }
  else {
    // Multiple slides, wait for last
    H5P.on(instance, 'changedSlide', function() {
      if (!done && instance.getCurrentSlideIndex() === instance.slides.length - 1) {
        done = true; // Only trigger once (feels more correct than retriggering)
        triggerFinish();
      }
    });
  }
});

That's it! The Course presentation H5P module should now send the 'completed' verb once you reach the end of the presentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants