AJAX call in block plugin

AJAX call in block plugin

by Kevin Burton -
Number of replies: 21

Some of the HTML that I would like to generate for display on the block takes a long time to generate. I would rather it didn't stop the display of the reset of the screen and was "put in the background" until the results are ready. How do I start t background task in Moodle that like jQuery (client side) or node.js (server side) will not block the rest of the page?

 

Average of ratings: -
In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

Cehckout this plugin I made which does exactly that.

https://github.com/dmiletic/moodle/tree/sample/laterdude/blocks/laterdude

If you have any questions just ask here.

 

Average of ratings: Useful (2)
In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

From this link I don't see a way to download the full project. I can see the source of the .php files in the root but I don't seem to be able to drill down say in the yui folder.

Also from what I can tell it seems that data.php gets called as a callback. Right? Is there an example of the usage for this plugin?

 

Thank you.

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

This is an online view of GIT repository. Register on github and checkout the code or go to 

https://github.com/dmiletic/moodle/tree/sample/laterdude

and click on donwload zip button.

I created this plugin based on your initial message so it is just a dummy example demonstrating how to do an ajax call replace html etc.

Average of ratings: Useful (1)
In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

Thank you. This is very helpful. I wasn't expecting to download 19+ Mb of code but I found the project.

As I see it there will be nothing displayed if the user has not logged in. Right?

If I just replace the 'require_login()' and handle the output with isloggedin() will that still work? Even though there is no login we will still have a session key?

Thanks again.

 

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

You can remove all protection if you want it is up to you. This is just a standard example. Try and see what happens

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

Sorry, I will bet you didn't plan on spending this much time on this request. Please stay with me, I think I am almost there.

On the initial Moodle page I get:

Capability "block/lateredude:view" was not found! This has to be fixed in code.
  • line 389 of \lib\accesslib.php: call to debugging()
  • line 17 of \blocks\laterdude\block_laterdude.php: call to has_capability()
  • line 296 of \blocks\moodleblock.class.php: call to block_mediasite->get_content()
  • line 238 of \blocks\moodleblock.class.php: call to block_base->formatted_contents()
  • line 956 of \lib\blocklib.php: call to block_base->get_content_for_output()
  • line 1008 of \lib\blocklib.php: call to block_manager->create_block_contents()
  • line 353 of \lib\blocklib.php: call to block_manager->ensure_content_created()
  • line 3 of \theme\base\layout\frontpage.php: call to block_manager->region_has_content()
  • line 866 of \lib\outputrenderers.php: call to include()
  • line 796 of \lib\outputrenderers.php: call to core_renderer->render_page_layout()
  • line 101 of \index.php: call to core_renderer->header()

The access.php file has the capability as original:

'block/laterdude:view' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => array(
'user' => CAP_ALLOW
),
),

Ideas?

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

Works on my machine. Try reinstalling it.

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

I reinstalled it an it works after login. Is there anyway to show the block when a user has not logged in?

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

remove the has_capability check in the get_content method of the block class

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

One more question. I changed the code to actually do the work that I would like it to do. I think I am running into a timeout issue. It works when displaying the 'We are the champions" like you originally coded it. It also works when I change the code to read 2 records from the database and make about 20 web service calls. But when I increase it to about 1200 webservice calls nothing shows up in the block. In later.js there is a line timeout: 10000 that I changed to 300000 but I still don't get any output. Is there someplace else that timeout would be an issue?

Thank you.

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

Show us the code in question. 

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

Here is the code from data.php:

<?php
define('AJAX_SCRIPT', true);
require('../../config.php');
require_once("$CFG->dirroot/mod/mediasite/webapiclient.php");
try {
//    require_login();
//    require_sesskey();
header('Content-Type: text/html; charset=utf-8');
global $DB;
$records = $DB->get_records('mediasite_sites');
$sitenames = array();
if(isloggedin() && confirm_sesskey()) {
foreach($records as $record) {
$client = new WebApiExternalAccessClient($record->endpoint, $record->username,$record->password);
//$presentations = $client->QueryAllPresentations('?$orderby=Title&$select=full');
$catalogs = $client->QueryCatalogShares('?$orderby=Name');
//$sitenames[] = $record->sitename."(".count($presentations)." presentations) "."(".count($catalogs)." catalogs)";
$sitenames[] = $record->sitename."(".count($catalogs)." catalogs)";
       }
    } else {
foreach($records as $record) {
$sitenames[] = $record->sitename;
        }
    }
echo html_writer::alist($sitenames);
} catch (Exception $e) {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request', true, 400);
if (isloggedin()) {
header('Content-Type: text/plain; charset=utf-8');
echo $e->getMessage();
    }
}

This code works. If I uncomment the lines involving $presentations and make the cooresponding change to building the $sitenames array then it fails to produce any output. From experience I know that there are about 1200 presentations and they are recieved in batches of 10 so this equates to about 120 web service calls to get all the presentations. There are two sites that I am reading from in the database and both these sites are identical so it means about 240 web service calls which again from experience seems to take upward of 5 minutes. Hence the need to put it in the background.

 

To make it more granular I added a call to sleep(5) before the return in the logged in case. This also works. But increase it to 10 seconds and it fails.

 

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

It is simple, either the calls are timing out or there is some catastrophic error that crashes the script. I would suggest to run this script directly from browser without having try catch and see if it dies at some point or not, also measure the time to see how long it takes. If it is extremely long than do not specify timeout at all. Let it run as long as it takes.

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

I have gone back to your original code to rule out a catastropic failure. I then added sleep(5) just before the echo html_writer. If I increase the sleep from 5 to 10 seconds there is no output. If you add a similar sleep() you will see what I mean. Being relatively new to YUI I am not entirely clear on how to set the timeout. Right now I have what I think is the timeout

 

timeout: 20000,

with the sleep set to 10 seconds. No output. Changing the sleep to 8 seconds seems to work. So as long as this task takes less than 8 seconds it seems OK. Ideas on how to increase this? It seems that the timeout: value is not the real timeout.

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

Ajax calls are generally not ment for something taking longer than few seconds. I think you should rethink the way you do things.

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

So what are my options for something that my take 5 minutes or more? In this case is there an alternative to AJAX that you know of?

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

It is obvious that you should fetch this information outside of user browser and cache it in database. Good place for that is cron. Almost all Moodle plugins have special method which when implemented is being invoked by Moodle cron process.

For block see here:

http://docs.moodle.org/dev/Blocks#Responding_to_Cron

http://docs.moodle.org/20/en/Cron

So you should add a table or two to your block and store there the information obtained from your web services. Later in your block get_content you just read that cached data from database which is rather fast.

http://docs.moodle.org/dev/Blocks#Database_support

 

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

Thank you for the tip. 

One more question. If I use cron to generate my content at a regular intervals should I just wait for the page to refresh and then rely on get_content() or as I think I understand the documentation I can essentially recreate each instance of the block and set the content from the cron handler directly? Do you have any experience with either approach?

 

 

http://docs.moodle.org/dev/Blocks

NOTE: The block cron is designed to call the cron script for that block type only. That is, cron does not care about individual instances of blocks. Inside your cron function $this is defined, but it has almost nothing in it (only title and content fields are populated). If you need to execute cron for individual instances it is your own responsibility to iterate over them in the block's cron function. Example:

public function cron() {
 
global $DB; // Global database object
 
// Get the instances of the block
$instances = $DB->get_records( 'block_instance', array('blockid'=>'simplehtml') );
 
// Iterate over the instances
foreach ($instances as $instance) {
 
// Recreate block object
$block = block_instance('simplehtml', $instance);
 
// $block is now the equivalent of $this in 'normal' block
// usage, e.g.
$someconfigitem = $block->config->item2;
}
}

TIP: This also means that creating a block is a possible way to create code that can respond to cron with a reasonably low overhead. No actual instances of the block are required.

 

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

The code you show is intended to alter individual block instance configuration and nothing else. The block display content is always generated with call to get_content method.

In reply to Darko Miletić

Re: AJAX call in block plugin

by Kevin Burton -

So to display new content as generated by the cron handler I will need to wait for a refresh. Right? Is there a way to just refresh the block content?

In reply to Kevin Burton

Re: AJAX call in block plugin

by Darko Miletić -

If you want to periodically refresh content of block without reloading the page set some javascript function to be executed every x seconds and in there do the ajax call like we did in the first place. Y.later is already supporting repetition. Checkout the documentation for YUI class.