diff --git a/grade/grading/form/rubric/edit.php b/grade/grading/form/rubric/edit.php index c1849f2a268..15bda1e6f96 100644 --- a/grade/grading/form/rubric/edit.php +++ b/grade/grading/form/rubric/edit.php @@ -65,6 +65,10 @@ if ($mform->is_cancelled()) { redirect($returnurl, $warning, null, \core\output\notification::NOTIFY_ERROR); } +// Try to keep the session alive on this page as it may take some time +// before significant interaction happens with the server. +\core\session\manager::keepalive(); + echo $OUTPUT->header(); $mform->display(); -echo $OUTPUT->footer(); \ No newline at end of file +echo $OUTPUT->footer(); diff --git a/lang/en/error.php b/lang/en/error.php index 825a6b35294..e0b5519f08a 100644 --- a/lang/en/error.php +++ b/lang/en/error.php @@ -496,6 +496,7 @@ $string['sendmessage'] = 'Send message'; $string['serverconnection'] = 'Error connecting to the server'; $string['servicedonotexist'] = 'The service does not exist'; $string['sessionwaiterr'] = 'Timed out while waiting for session lock.
Wait for your current requests to finish and try again later.'; +$string['sessionexpired'] = 'Session expired'; $string['sessioncookiesdisable'] = 'Incorrect use of require_key_login() - session cookies must be disabled!'; $string['sessiondiskfull'] = 'The session partition is full. It is not possible to log in at this time. Please notify the server administrator.'; $string['sessionhandlerproblem'] = 'Session handler is misconfigured'; diff --git a/lang/en/moodle.php b/lang/en/moodle.php index df841d662f3..6c3294b8b11 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -802,6 +802,7 @@ $string['existingcoursedeleting'] = 'Existing course, deleting it first'; $string['existingcreators'] = 'Existing course creators'; $string['existingstudents'] = 'Enrolled students'; $string['existingteachers'] = 'Existing teachers'; +$string['extendsession'] = 'Extend session'; $string['expand'] = 'Expand'; $string['expandall'] = 'Expand all'; $string['expandcategory'] = 'Expand {$a}'; @@ -1807,7 +1808,8 @@ $string['separateandconnected'] = 'Separate and Connected ways of knowing'; $string['separateandconnectedinfo'] = 'The scale based on the theory of separate and connected knowing. This theory describes two different ways that we can evaluate and learn about the things we see and hear.'; $string['servererror'] = 'An error occurred whilst communicating with the server'; $string['serverlocaltime'] = 'Server\'s local time'; -$string['sessionforceclean'] = 'As a security precaution, user-generated scripts have been disabled within this session.'; +$string['sessionforceclean'] = 'As a security precaution, user-generated scripts have been disabled within this session'; +$string['sessiontimeoutsoon'] = 'Your session is about to timeout. Do you want to extend your current session?'; $string['setcategorytheme'] = 'Set category theme'; $string['setpassword'] = 'Set password'; $string['setpasswordinstructions'] = 'Please enter your new password below, then save changes.'; diff --git a/lib/ajax/service.php b/lib/ajax/service.php index 3298c0e7a75..caaf86c66f9 100644 --- a/lib/ajax/service.php +++ b/lib/ajax/service.php @@ -29,6 +29,10 @@ define('AJAX_SCRIPT', true); +if (!empty($_GET['nosessionupdate'])) { + define('NO_SESSION_UPDATE', true); +} + require_once(__DIR__ . '/../../config.php'); require_once($CFG->libdir . '/externallib.php'); @@ -63,5 +67,4 @@ foreach ($requests as $request) { break; } } - echo json_encode($responses); diff --git a/lib/amd/build/ajax.min.js b/lib/amd/build/ajax.min.js index f8c54537985..adc66eb5b3a 100644 --- a/lib/amd/build/ajax.min.js +++ b/lib/amd/build/ajax.min.js @@ -1 +1 @@ -define(["jquery","core/config","core/log","core/url"],function(a,b,c,d){var e=!1,f=function(a){var b,c,e=this,f=null,g=0;if(a.error)for(;g0&&setTimeout(o,h),!0}).fail(function(){d.alert("",j)})},p=function(){var a={methodname:"core_session_time_remaining",args:{}};return k=!1,b.call([a],!0,!0,!0)[0].then(function(a){return!(a.userid<=0)&&(a.timeremaining<0?e.get_strings([{key:"sessionexpired",component:"error"},{key:"sessionerroruser",component:"error"}]).then(function(a){return d.alert(a[0],a[1]),!0}).fail(d.exception):1e3*a.timeremaining0?setTimeout(o,h):setTimeout(p,5*l)},r=function(){f||(f=!0,q())},s=function(a,b,c){f||(f=!0,h=1e3*a,j=c,i=1e3*b,q())};return{keepalive:s,init:r}}); \ No newline at end of file diff --git a/lib/amd/build/page_global.min.js b/lib/amd/build/page_global.min.js index 838ab91a5f5..a02a46c08e8 100644 --- a/lib/amd/build/page_global.min.js +++ b/lib/amd/build/page_global.min.js @@ -1 +1 @@ -define(["jquery","core/custom_interaction_events","core/str"],function(a,b,c){var d=function(){var d=a("body");b.define(d,[b.events.activate]),d.on(b.events.activate,"[data-show-active-item]",function(b){var d=a(b.target).closest(".dropdown-item"),e=d.closest("[data-show-active-item]");if(d.hasClass("dropdown-item")&&!d.hasClass("active")){var f=e.find(".dropdown-item");f.removeClass("active"),f.removeAttr("aria-current"),e.attr("data-skip-active-class")||d.addClass("active"),d.attr("aria-current",!0);var g=d.text(),h=e.parent().find('[data-toggle="dropdown"]'),i=h.find("[data-active-item-text]");i.length?i.html(g):h.html(g);var j=e.attr("data-active-item-button-aria-label-components");if(j){var k=j.split(",");k.push(g),c.get_string(k[0].trim(),k[1].trim(),k[2].trim()).then(function(a){return h.attr("aria-label",a),a})["catch"](function(){return!1})}}})},e=function(){d()};return{init:e}}); \ No newline at end of file +define(["jquery","core/custom_interaction_events","core/str","core/network"],function(a,b,c,d){var e=function(){var d=a("body");b.define(d,[b.events.activate]),d.on(b.events.activate,"[data-show-active-item]",function(b){var d=a(b.target).closest(".dropdown-item"),e=d.closest("[data-show-active-item]");if(d.hasClass("dropdown-item")&&!d.hasClass("active")){var f=e.find(".dropdown-item");f.removeClass("active"),f.removeAttr("aria-current"),e.attr("data-skip-active-class")||d.addClass("active"),d.attr("aria-current",!0);var g=d.text(),h=e.parent().find('[data-toggle="dropdown"]'),i=h.find("[data-active-item-text]");i.length?i.html(g):h.html(g);var j=e.attr("data-active-item-button-aria-label-components");if(j){var k=j.split(",");k.push(g),c.get_string(k[0].trim(),k[1].trim(),k[2].trim()).then(function(a){return h.attr("aria-label",a),a})["catch"](function(){return!1})}}})},f=function(){e(),d.init()};return{init:f}}); \ No newline at end of file diff --git a/lib/amd/src/ajax.js b/lib/amd/src/ajax.js index a13cbcf2cda..98e982c5ee4 100644 --- a/lib/amd/src/ajax.js +++ b/lib/amd/src/ajax.js @@ -40,11 +40,12 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo */ var requestSuccess = function(responses) { // Call each of the success handlers. - var requests = this; - var exception = null; - var i = 0; - var request; - var response; + var requests = this, + exception = null, + i = 0, + request, + response, + nosessionupdate; if (responses.error) { // There was an error with the request as a whole. @@ -69,6 +70,7 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo request.deferred.resolve(response.data); } else { exception = response.exception; + nosessionupdate = requests[i].nosessionupdate; break; } } else { @@ -80,7 +82,7 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo // Something failed, reject the remaining promises. if (exception !== null) { // Redirect to the login page. - if (exception.errorcode === "servicerequireslogin") { + if (exception.errorcode === "servicerequireslogin" && !nosessionupdate) { window.location = URL.relativeUrl("/login/index.php"); } else { requests.forEach(function(request) { @@ -132,9 +134,12 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo * If false - this function will call the faster nologin ajax script - but * will fail unless all functions have been marked as 'loginrequired' => false * in services.php + * @param {Boolean} nosessionupdate Optional, defaults to false. + * If true, the timemodified for the session will not be updated. + * @param {Integer} timeout number of milliseconds to wait for a response. Defaults to no limit. * @return {Promise[]} Array of promises that will be resolved when the ajax call returns. */ - call: function(requests, async, loginrequired) { + call: function(requests, async, loginrequired, nosessionupdate, timeout) { $(window).bind('beforeunload', function() { unloading = true; }); @@ -150,6 +155,13 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo if (typeof async === "undefined") { async = true; } + if (typeof timeout === 'undefined') { + timeout = 0; + } + + if (typeof nosessionupdate === "undefined") { + nosessionupdate = false; + } for (i = 0; i < requests.length; i++) { var request = requests[i]; ajaxRequestData.push({ @@ -157,6 +169,7 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo methodname: request.methodname, args: request.args }); + request.nosessionupdate = nosessionupdate; request.deferred = $.Deferred(); promises.push(request.deferred.promise()); // Allow setting done and fail handlers as arguments. @@ -185,7 +198,8 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo dataType: 'json', processData: false, async: async, - contentType: "application/json" + contentType: "application/json", + timeout: timeout }; var script = 'service.php'; @@ -195,6 +209,10 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo var url = config.wwwroot + '/lib/ajax/' + script + '?sesskey=' + config.sesskey + '&info=' + requestInfo; + if (nosessionupdate) { + url += '&nosessionupdate=true'; + } + // Jquery deprecated done and fail with async=false so we need to do this 2 ways. if (async) { $.ajax(url, settings) diff --git a/lib/amd/src/network.js b/lib/amd/src/network.js new file mode 100644 index 00000000000..5e21a6a6fba --- /dev/null +++ b/lib/amd/src/network.js @@ -0,0 +1,196 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Poll the server to keep the session alive. + * + * @module core/network + * @package core + * @copyright 2019 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define(['jquery', 'core/ajax', 'core/config', 'core/notification', 'core/str'], + function($, Ajax, Config, Notification, Str) { + + var started = false; + var warningDisplayed = false; + var keepAliveFrequency = 0; + var requestTimeout = 0; + var keepAliveMessage = false; + var sessionTimeout = false; + // 1/10 of session timeout, max of 10 minutes. + var checkFrequency = Math.min((Config.sessiontimeout / 10), 600) * 1000; + // 1/5 of sessiontimeout. + var warningLimit = checkFrequency * 2; + + /** + * The session time has expired - we can't extend it now. + */ + var timeoutSessionExpired = function() { + sessionTimeout = true; + }; + + /** + * Ping the server to keep the session alive. + * + * @return {Promise} + */ + var touchSession = function() { + var request = { + methodname: 'core_session_touch', + args: { } + }; + + if (sessionTimeout) { + // We timed out before we extended the session. + return Str.get_strings([ + {key: 'sessionexpired', component: 'error'}, + {key: 'sessionerroruser', component: 'error'} + ]).then(function(strings) { + Notification.alert( + strings[0], // Title. + strings[1] // Message. + ); + return true; + }).fail(Notification.exception); + } else { + return Ajax.call([request], true, true, false, requestTimeout)[0].then(function() { + if (keepAliveFrequency > 0) { + setTimeout(touchSession, keepAliveFrequency); + } + return true; + }).fail(function() { + Notification.alert('', keepAliveMessage); + }); + } + }; + + /** + * Ask the server how much time is remaining in this session and + * show confirm/cancel notifications if the session is about to run out. + * + * @return {Promise} + */ + var checkSession = function() { + var request = { + methodname: 'core_session_time_remaining', + args: { } + }; + + sessionTimeout = false; + return Ajax.call([request], true, true, true)[0].then(function(args) { + if (args.userid <= 0) { + return false; + } + if (args.timeremaining < 0) { + Str.get_strings([ + {key: 'sessionexpired', component: 'error'}, + {key: 'sessionerroruser', component: 'error'} + ]).then(function(strings) { + Notification.alert( + strings[0], // Title. + strings[1] // Message. + ); + return true; + }).fail(Notification.exception); + + } else if (args.timeremaining * 1000 < warningLimit && !warningDisplayed) { + // If we don't extend the session before the timeout - warn. + setTimeout(timeoutSessionExpired, args.timeremaining * 1000); + warningDisplayed = true; + Str.get_strings([ + {key: 'norecentactivity', component: 'moodle'}, + {key: 'sessiontimeoutsoon', component: 'moodle'}, + {key: 'extendsession', component: 'moodle'}, + {key: 'cancel', component: 'moodle'} + ]).then(function(strings) { + Notification.confirm( + strings[0], // Title. + strings[1], // Message. + strings[2], // Extend session. + strings[3], // Cancel. + function() { + touchSession(); + warningDisplayed = false; + // First wait is half the session timeout. + setTimeout(checkSession, checkFrequency * 5); + return true; + }, + function() { + warningDisplayed = false; + setTimeout(checkSession, checkFrequency); + } + ); + return true; + }).fail(Notification.exception); + } else { + setTimeout(checkSession, checkFrequency); + } + return true; + }); + // We do not catch the fails from the above ajax call because they will fail when + // we are not logged in - we don't need to take any action then. + }; + + /** + * Start calling a function to check if the session is still alive. + */ + var start = function() { + if (keepAliveFrequency > 0) { + setTimeout(touchSession, keepAliveFrequency); + } else { + // First wait is half the session timeout. + setTimeout(checkSession, checkFrequency * 5); + } + }; + + /** + * Don't allow more than one of these polling loops in a single page. + */ + var init = function() { + // We only allow one concurrent instance of this checker. + if (started) { + return; + } + started = true; + + start(); + }; + + /** + * Start polling with more specific values for the frequency, timeout and message. + * + * @param {number} freq How ofter to poll the server. + * @param {number} timeout The time to wait for each request to the server. + * @param {string} message The message to display if the session is going to time out. + */ + var keepalive = function(freq, timeout, message) { + // We only allow one concurrent instance of this checker. + if (started) { + return; + } + started = true; + + keepAliveFrequency = freq * 1000; + keepAliveMessage = message; + requestTimeout = timeout * 1000; + start(); + }; + + return { + keepalive: keepalive, + init: init + }; +}); diff --git a/lib/amd/src/page_global.js b/lib/amd/src/page_global.js index 88360b8bf29..b92c97d8f20 100644 --- a/lib/amd/src/page_global.js +++ b/lib/amd/src/page_global.js @@ -26,11 +26,13 @@ define( 'jquery', 'core/custom_interaction_events', 'core/str', + 'core/network' ], function( $, CustomEvents, - Str + Str, + Network ) { /** @@ -127,6 +129,7 @@ function( */ var init = function() { initActionOptionDropdownHandler(); + Network.init(); }; return { diff --git a/lib/classes/session/external.php b/lib/classes/session/external.php new file mode 100644 index 00000000000..e6e0f0e70d6 --- /dev/null +++ b/lib/classes/session/external.php @@ -0,0 +1,95 @@ +. + +/** + * This class contains a list of webservice functions related to session. + * + * @package core + * @copyright 2019 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\session; + +defined('MOODLE_INTERNAL') || die(); + +/** + * This class contains a list of webservice functions related to session. + * + * @copyright 2019 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 2.9 + */ +class external extends \external_api { + + /** + * Returns description of touch_session() parameters. + * + * @return external_function_parameters + */ + public static function touch_session_parameters() { + return new \external_function_parameters([]); + } + + /** + * Extend the current session. + * + * @return array the mapping + */ + public static function touch_session() { + \core\session\manager::touch_session(session_id()); + return true; + } + + /** + * Returns description of touch_session() result value. + * + * @return external_description + */ + public static function touch_session_returns() { + return new \external_value(PARAM_BOOL, 'result'); + } + + /** + * Returns description of time_remaining() parameters. + * + * @return external_function_parameters + */ + public static function time_remaining_parameters() { + return new \external_function_parameters([]); + } + + /** + * Extend the current session. + * + * @return array the mapping + */ + public static function time_remaining() { + return \core\session\manager::time_remaining(session_id()); + } + + /** + * Returns description of touch_session() result value. + * + * @return external_description + */ + public static function time_remaining_returns() { + return new \external_single_structure(array ( + 'userid' => new \external_value(PARAM_INTEGER, 'The current user id.'), + 'timeremaining' => new \external_value(PARAM_INTEGER, 'The number of seconds remaining in this session.') + )); + } +} diff --git a/lib/classes/session/manager.php b/lib/classes/session/manager.php index 36083dd1dee..47c5df90125 100644 --- a/lib/classes/session/manager.php +++ b/lib/classes/session/manager.php @@ -364,6 +364,9 @@ class manager { } if ($timeout) { + if (defined('NO_SESSION_UPDATE') && NO_SESSION_UPDATE) { + return; + } session_regenerate_id(true); $_SESSION = array(); $DB->delete_records('sessions', array('id'=>$record->id)); @@ -398,7 +401,7 @@ class manager { $updated = true; } - if ($updated) { + if ($updated && (!defined('NO_SESSION_UPDATE') || !NO_SESSION_UPDATE)) { $update->id = $record->id; $DB->update_record('sessions', $update); } @@ -632,6 +635,31 @@ class manager { return self::$handler->session_exists($sid); } + /** + * Return the number of seconds remaining in the current session. + * @param string $sid + */ + public static function time_remaining($sid) { + global $DB, $CFG; + + if (empty($CFG->version)) { + // Not installed yet, do not try to access database. + return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout]; + } + + // Note: add sessions->state checking here if it gets implemented. + if (!$record = $DB->get_record('sessions', array('sid' => $sid), 'id, userid, timemodified')) { + return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout]; + } + + if (empty($record->userid) or isguestuser($record->userid)) { + // Ignore guest and not-logged-in timeouts, there is very little risk here. + return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout]; + } else { + return ['userid' => $record->userid, 'timeremaining' => $CFG->sessiontimeout - (time() - $record->timemodified)]; + } + } + /** * Fake last access for given session, this prevents session timeout. * @param string $sid @@ -955,9 +983,10 @@ class manager { * @param string $identifier The string identifier for the message to show on failure. * @param string $component The string component for the message to show on failure. * @param int $frequency The update frequency in seconds. + * @param int $timeout The timeout of each request in seconds. * @throws coding_exception IF the frequency is longer than the session lifetime. */ - public static function keepalive($identifier = 'sessionerroruser', $component = 'error', $frequency = null) { + public static function keepalive($identifier = 'sessionerroruser', $component = 'error', $frequency = null, $timeout = 0) { global $CFG, $PAGE; if ($frequency) { @@ -966,19 +995,15 @@ class manager { throw new \coding_exception('Keepalive frequency is longer than the session lifespan.'); } } else { - // A frequency of sessiontimeout / 3 allows for one missed request whilst still preserving the session. - $frequency = $CFG->sessiontimeout / 3; + // A frequency of sessiontimeout / 10 matches the timeouts in core/network amd module. + $frequency = $CFG->sessiontimeout / 10; } - // Add the session keepalive script to the list of page output requirements. - $sessionkeepaliveurl = new \moodle_url('/lib/sessionkeepalive_ajax.php'); - $PAGE->requires->string_for_js($identifier, $component); - $PAGE->requires->yui_module('moodle-core-checknet', 'M.core.checknet.init', array(array( - // The JS config takes this is milliseconds rather than seconds. - 'frequency' => $frequency * 1000, - 'message' => array($identifier, $component), - 'uri' => $sessionkeepaliveurl->out(), - ))); + $PAGE->requires->js_call_amd('core/network', 'keepalive', array( + $frequency, + $timeout, + get_string($identifier, $component) + )); } /** diff --git a/lib/db/services.php b/lib/db/services.php index 708f2ccdb80..ce63f3d888b 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -677,6 +677,22 @@ $functions = array( 'loginrequired' => false, 'ajax' => true, ), + 'core_session_touch' => array( + 'classname' => 'core\session\external', + 'methodname' => 'touch_session', + 'description' => 'Keep the users session alive', + 'type' => 'read', + 'loginrequired' => true, + 'ajax' => true, + ), + 'core_session_time_remaining' => array( + 'classname' => 'core\session\external', + 'methodname' => 'time_remaining', + 'description' => 'Count the seconds remaining in this session', + 'type' => 'read', + 'loginrequired' => true, + 'ajax' => true, + ), 'core_files_get_files' => array( 'classname' => 'core_files_external', 'methodname' => 'get_files', diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index c7c0aa14070..f29905037e6 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -321,6 +321,7 @@ class page_requirements_manager { $this->M_cfg = array( 'wwwroot' => $CFG->wwwroot, 'sesskey' => sesskey(), + 'sessiontimeout' => $CFG->sessiontimeout, 'themerev' => theme_get_revision(), 'slasharguments' => (int)(!empty($CFG->slasharguments)), 'theme' => $page->theme->name, diff --git a/lib/sessionkeepalive_ajax.php b/lib/sessionkeepalive_ajax.php deleted file mode 100644 index 65d1acfc1e9..00000000000 --- a/lib/sessionkeepalive_ajax.php +++ /dev/null @@ -1,36 +0,0 @@ -. - -/** - * Ensure that session is kept alive. - * - * @copyright 2014 Andrew Nicols - * @package core - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -define('AJAX_SCRIPT', true); -require_once(__DIR__ . '/../config.php'); - -// Require the session key - want to make sure that this isn't called -// maliciously to keep a session alive longer than intended. -if (!confirm_sesskey()) { - header('HTTP/1.1 403 Forbidden'); - print_error('invalidsesskey'); -} - -// Update the session. -\core\session\manager::touch_session(session_id()); diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 30dbd057adb..631f3e6b893 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -1,6 +1,9 @@ This files describes API changes in core libraries and APIs, information provided here is intended especially for developers. +=== 3.8 === +* The yui checknet module is removed. Call \core\session\manager::keepalive instead. + === 3.7 === * Nodes in the navigation api can have labels for each group. See set/get_collectionlabel(). * The method core_user::is_real_user() now returns false for userid = 0 parameter diff --git a/lib/yui/build/moodle-core-checknet/assets/checknet.txt b/lib/yui/build/moodle-core-checknet/assets/checknet.txt deleted file mode 100644 index d00491fd7e5..00000000000 --- a/lib/yui/build/moodle-core-checknet/assets/checknet.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/lib/yui/build/moodle-core-checknet/moodle-core-checknet-debug.js b/lib/yui/build/moodle-core-checknet/moodle-core-checknet-debug.js deleted file mode 100644 index 3ee6b81c94c..00000000000 --- a/lib/yui/build/moodle-core-checknet/moodle-core-checknet-debug.js +++ /dev/null @@ -1,220 +0,0 @@ -YUI.add('moodle-core-checknet', function (Y, NAME) { - -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * A utility to check whether the connection to the Moodle server is still - * active. - * - * @module moodle-core-checknet - * @package core - * @copyright 2014 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @main moodle-core-checknet - */ - -/** - * @namespace M.core - * @class checknet - */ - -function CheckNet() { - CheckNet.superclass.constructor.apply(this, arguments); -} - -Y.extend(CheckNet, Y.Base, { - /** - * Zero-based count of alerts displayed. - * - * @property _alertCount - * @type Number - * @private - * @default 0 - */ - _alertCount: 0, - - /** - * A link to the warning dialogue. - * - * @property _alertDialogue - * @type M.core.dialogue - * @private - * @default null - */ - _alertDialogue: null, - - /** - * Setup the checking mechanism. - * - * @method initializer - */ - initializer: function() { - // Perform our first check. - this._scheduleCheck(); - }, - - /** - * Schedule a check of the checknet file. - * - * @method _scheduleCheck - * @chainable - * @private - */ - _scheduleCheck: function() { - // Schedule the next check after five seconds. - Y.later(this.get('frequency'), this, this._performCheck); - return this; - }, - - /** - * Perform an immediate check of the checknet file. - * - * @method _performCheck - * @private - */ - _performCheck: function() { - Y.io(this.get('uri'), { - data: { - // Add the session key. - sesskey: M.cfg.sesskey, - // Add a query string to prevent older versions of IE from using the cache. - time: new Date().getTime() - }, - timeout: this.get('timeout'), - headers: { - 'Cache-Control': 'no-cache', - 'Expires': '-1' - }, - context: this, - on: { - complete: function(tid, response) { - // Check for failure conditions. - // We check for a valid status here because if the user is moving away from the page at the time we - // run this callback we do not want to display the error. - if (response && typeof response.status !== "undefined") { - var code = parseInt(response.status, 10); - - if (code === 200) { - // This is a valid attempt - clear any existing warning dialogue and destroy it. - if (this._alertDialogue) { - this._alertDialogue.destroy(); - this._alertDialogue = null; - } - } else if (code >= 300 && code <= 399) { - // This is a cached status - warn developers, but otherwise ignore. - Y.log("A cached copy of the checknet status file was returned so it's reliablity cannot be guaranteed", - 'warn', - 'moodle-mod_scorm-checknet'); - } else { - if (this._alertDialogue === null || this._alertDialogue.get('destroyed')) { - // Only create a new dialogue if it isn't already displayed. - this._alertDialogue = new M.core.alert({ - message: M.util.get_string.apply(this, this.get('message')) - }); - } else { - this._alertDialogue.show(); - } - this._alertCount++; - } - } - // If max alert not modified in args, check indefinitely. - // Once max alert count iteration is reached, stop checking. - if (this.get('maxalerts') === -1 || (this.get('maxalerts') - 1) >= this._alertCount) { - // Start the next check. - this._scheduleCheck(); - } - } - } - }); - } -}, { - NAME: 'checkNet', - ATTRS: { - /** - * The file to check access against. - * - * @attribute uri - * @type String - * @default M.cfg.wwwroot + '/lib/yui/build/moodle-core-checknet/assets/checknet.txt' - */ - uri: { - value: M.cfg.wwwroot + '/lib/yui/build/moodle-core-checknet/assets/checknet.txt' - }, - - /** - * The timeout (in milliseconds) before the checker should give up and display a warning. - * - * @attribute timeout - * @type Number - * @value 4000 - */ - timeout: { - value: 4000 - }, - - /** - * The frequency (in milliseconds) that checks should be run. - * A new check is not begun until the previous check has completed. - * - * @attribute frequency - * @writeOnce - * @type Number - * @value 10000 - */ - frequency: { - value: 10000 - }, - - /** - * The message which should be displayed upon a test failure. - * - * The array values are passed directly to M.util.get_string() and arguments should match accordingly. - * - * @attribute message - * @type Array - * @value [ - * 'networkdropped', - * 'moodle' - * ] - */ - message: { - value: [ - 'networkdropped', - 'moodle' - ] - }, - - /** - * Maxiumum count (not zero-based) of alerts to display for a single page load. - * - * @attribute maxalerts - * @type Number - * @value -1 - */ - maxalerts: { - value: -1 - } - } -}); - -M.core = M.core || {}; -M.core.checknet = M.core.checknet || {}; -M.core.checknet.init = function(config) { - return new CheckNet(config); -}; - - -}, '@VERSION@', {"requires": ["base-base", "moodle-core-notification-alert", "io-base"]}); diff --git a/lib/yui/build/moodle-core-checknet/moodle-core-checknet-min.js b/lib/yui/build/moodle-core-checknet/moodle-core-checknet-min.js deleted file mode 100644 index 0fe879b1162..00000000000 --- a/lib/yui/build/moodle-core-checknet/moodle-core-checknet-min.js +++ /dev/null @@ -1 +0,0 @@ -YUI.add("moodle-core-checknet",function(e,t){function n(){n.superclass.constructor.apply(this,arguments)}e.extend(n,e.Base,{_alertCount:0,_alertDialogue:null,initializer:function(){this._scheduleCheck()},_scheduleCheck:function(){return e.later(this.get("frequency"),this,this._performCheck),this},_performCheck:function(){e.io(this.get("uri"),{data:{sesskey:M.cfg.sesskey,time:(new Date).getTime()},timeout:this.get("timeout"),headers:{"Cache-Control":"no-cache",Expires:"-1"},context:this,on:{complete:function(e,t){if(t&&typeof t.status!="undefined"){var n=parseInt(t.status,10);n===200?this._alertDialogue&&(this._alertDialogue.destroy(),this._alertDialogue=null):n>=300&&n<=399||(this._alertDialogue===null||this._alertDialogue.get("destroyed")?this._alertDialogue=new M.core.alert({message:M.util.get_string.apply(this,this.get("message"))}):this._alertDialogue.show(),this._alertCount++)}(this.get("maxalerts")===-1||this.get("maxalerts")-1>=this._alertCount)&&this._scheduleCheck()}}})}},{NAME:"checkNet",ATTRS:{uri:{value:M.cfg.wwwroot+"/lib/yui/build/moodle-core-checknet/assets/checknet.txt"},timeout:{value:4e3},frequency:{value:1e4},message:{value:["networkdropped","moodle"]},maxalerts:{value:-1}}}),M.core=M.core||{},M.core.checknet=M.core.checknet||{},M.core.checknet.init=function(e){return new n(e)}},"@VERSION@",{requires:["base-base","moodle-core-notification-alert","io-base"]}); diff --git a/lib/yui/build/moodle-core-checknet/moodle-core-checknet.js b/lib/yui/build/moodle-core-checknet/moodle-core-checknet.js deleted file mode 100644 index 52d5b54d00a..00000000000 --- a/lib/yui/build/moodle-core-checknet/moodle-core-checknet.js +++ /dev/null @@ -1,217 +0,0 @@ -YUI.add('moodle-core-checknet', function (Y, NAME) { - -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * A utility to check whether the connection to the Moodle server is still - * active. - * - * @module moodle-core-checknet - * @package core - * @copyright 2014 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @main moodle-core-checknet - */ - -/** - * @namespace M.core - * @class checknet - */ - -function CheckNet() { - CheckNet.superclass.constructor.apply(this, arguments); -} - -Y.extend(CheckNet, Y.Base, { - /** - * Zero-based count of alerts displayed. - * - * @property _alertCount - * @type Number - * @private - * @default 0 - */ - _alertCount: 0, - - /** - * A link to the warning dialogue. - * - * @property _alertDialogue - * @type M.core.dialogue - * @private - * @default null - */ - _alertDialogue: null, - - /** - * Setup the checking mechanism. - * - * @method initializer - */ - initializer: function() { - // Perform our first check. - this._scheduleCheck(); - }, - - /** - * Schedule a check of the checknet file. - * - * @method _scheduleCheck - * @chainable - * @private - */ - _scheduleCheck: function() { - // Schedule the next check after five seconds. - Y.later(this.get('frequency'), this, this._performCheck); - return this; - }, - - /** - * Perform an immediate check of the checknet file. - * - * @method _performCheck - * @private - */ - _performCheck: function() { - Y.io(this.get('uri'), { - data: { - // Add the session key. - sesskey: M.cfg.sesskey, - // Add a query string to prevent older versions of IE from using the cache. - time: new Date().getTime() - }, - timeout: this.get('timeout'), - headers: { - 'Cache-Control': 'no-cache', - 'Expires': '-1' - }, - context: this, - on: { - complete: function(tid, response) { - // Check for failure conditions. - // We check for a valid status here because if the user is moving away from the page at the time we - // run this callback we do not want to display the error. - if (response && typeof response.status !== "undefined") { - var code = parseInt(response.status, 10); - - if (code === 200) { - // This is a valid attempt - clear any existing warning dialogue and destroy it. - if (this._alertDialogue) { - this._alertDialogue.destroy(); - this._alertDialogue = null; - } - } else if (code >= 300 && code <= 399) { - // This is a cached status - warn developers, but otherwise ignore. - } else { - if (this._alertDialogue === null || this._alertDialogue.get('destroyed')) { - // Only create a new dialogue if it isn't already displayed. - this._alertDialogue = new M.core.alert({ - message: M.util.get_string.apply(this, this.get('message')) - }); - } else { - this._alertDialogue.show(); - } - this._alertCount++; - } - } - // If max alert not modified in args, check indefinitely. - // Once max alert count iteration is reached, stop checking. - if (this.get('maxalerts') === -1 || (this.get('maxalerts') - 1) >= this._alertCount) { - // Start the next check. - this._scheduleCheck(); - } - } - } - }); - } -}, { - NAME: 'checkNet', - ATTRS: { - /** - * The file to check access against. - * - * @attribute uri - * @type String - * @default M.cfg.wwwroot + '/lib/yui/build/moodle-core-checknet/assets/checknet.txt' - */ - uri: { - value: M.cfg.wwwroot + '/lib/yui/build/moodle-core-checknet/assets/checknet.txt' - }, - - /** - * The timeout (in milliseconds) before the checker should give up and display a warning. - * - * @attribute timeout - * @type Number - * @value 4000 - */ - timeout: { - value: 4000 - }, - - /** - * The frequency (in milliseconds) that checks should be run. - * A new check is not begun until the previous check has completed. - * - * @attribute frequency - * @writeOnce - * @type Number - * @value 10000 - */ - frequency: { - value: 10000 - }, - - /** - * The message which should be displayed upon a test failure. - * - * The array values are passed directly to M.util.get_string() and arguments should match accordingly. - * - * @attribute message - * @type Array - * @value [ - * 'networkdropped', - * 'moodle' - * ] - */ - message: { - value: [ - 'networkdropped', - 'moodle' - ] - }, - - /** - * Maxiumum count (not zero-based) of alerts to display for a single page load. - * - * @attribute maxalerts - * @type Number - * @value -1 - */ - maxalerts: { - value: -1 - } - } -}); - -M.core = M.core || {}; -M.core.checknet = M.core.checknet || {}; -M.core.checknet.init = function(config) { - return new CheckNet(config); -}; - - -}, '@VERSION@', {"requires": ["base-base", "moodle-core-notification-alert", "io-base"]}); diff --git a/lib/yui/src/checknet/assets/checknet.txt b/lib/yui/src/checknet/assets/checknet.txt deleted file mode 100644 index d00491fd7e5..00000000000 --- a/lib/yui/src/checknet/assets/checknet.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/lib/yui/src/checknet/build.json b/lib/yui/src/checknet/build.json deleted file mode 100644 index 406832d6ce1..00000000000 --- a/lib/yui/src/checknet/build.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "moodle-core-checknet", - "builds": { - "moodle-core-checknet": { - "jsfiles": [ - "checknet.js" - ] - } - } -} diff --git a/lib/yui/src/checknet/js/checknet.js b/lib/yui/src/checknet/js/checknet.js deleted file mode 100644 index 595620e3719..00000000000 --- a/lib/yui/src/checknet/js/checknet.js +++ /dev/null @@ -1,215 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * A utility to check whether the connection to the Moodle server is still - * active. - * - * @module moodle-core-checknet - * @package core - * @copyright 2014 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @main moodle-core-checknet - */ - -/** - * @namespace M.core - * @class checknet - */ - -function CheckNet() { - CheckNet.superclass.constructor.apply(this, arguments); -} - -Y.extend(CheckNet, Y.Base, { - /** - * Zero-based count of alerts displayed. - * - * @property _alertCount - * @type Number - * @private - * @default 0 - */ - _alertCount: 0, - - /** - * A link to the warning dialogue. - * - * @property _alertDialogue - * @type M.core.dialogue - * @private - * @default null - */ - _alertDialogue: null, - - /** - * Setup the checking mechanism. - * - * @method initializer - */ - initializer: function() { - // Perform our first check. - this._scheduleCheck(); - }, - - /** - * Schedule a check of the checknet file. - * - * @method _scheduleCheck - * @chainable - * @private - */ - _scheduleCheck: function() { - // Schedule the next check after five seconds. - Y.later(this.get('frequency'), this, this._performCheck); - return this; - }, - - /** - * Perform an immediate check of the checknet file. - * - * @method _performCheck - * @private - */ - _performCheck: function() { - Y.io(this.get('uri'), { - data: { - // Add the session key. - sesskey: M.cfg.sesskey, - // Add a query string to prevent older versions of IE from using the cache. - time: new Date().getTime() - }, - timeout: this.get('timeout'), - headers: { - 'Cache-Control': 'no-cache', - 'Expires': '-1' - }, - context: this, - on: { - complete: function(tid, response) { - // Check for failure conditions. - // We check for a valid status here because if the user is moving away from the page at the time we - // run this callback we do not want to display the error. - if (response && typeof response.status !== "undefined") { - var code = parseInt(response.status, 10); - - if (code === 200) { - // This is a valid attempt - clear any existing warning dialogue and destroy it. - if (this._alertDialogue) { - this._alertDialogue.destroy(); - this._alertDialogue = null; - } - } else if (code >= 300 && code <= 399) { - // This is a cached status - warn developers, but otherwise ignore. - Y.log("A cached copy of the checknet status file was returned so it's reliablity cannot be guaranteed", - 'warn', - 'moodle-mod_scorm-checknet'); - } else { - if (this._alertDialogue === null || this._alertDialogue.get('destroyed')) { - // Only create a new dialogue if it isn't already displayed. - this._alertDialogue = new M.core.alert({ - message: M.util.get_string.apply(this, this.get('message')) - }); - } else { - this._alertDialogue.show(); - } - this._alertCount++; - } - } - // If max alert not modified in args, check indefinitely. - // Once max alert count iteration is reached, stop checking. - if (this.get('maxalerts') === -1 || (this.get('maxalerts') - 1) >= this._alertCount) { - // Start the next check. - this._scheduleCheck(); - } - } - } - }); - } -}, { - NAME: 'checkNet', - ATTRS: { - /** - * The file to check access against. - * - * @attribute uri - * @type String - * @default M.cfg.wwwroot + '/lib/yui/build/moodle-core-checknet/assets/checknet.txt' - */ - uri: { - value: M.cfg.wwwroot + '/lib/yui/build/moodle-core-checknet/assets/checknet.txt' - }, - - /** - * The timeout (in milliseconds) before the checker should give up and display a warning. - * - * @attribute timeout - * @type Number - * @value 4000 - */ - timeout: { - value: 4000 - }, - - /** - * The frequency (in milliseconds) that checks should be run. - * A new check is not begun until the previous check has completed. - * - * @attribute frequency - * @writeOnce - * @type Number - * @value 10000 - */ - frequency: { - value: 10000 - }, - - /** - * The message which should be displayed upon a test failure. - * - * The array values are passed directly to M.util.get_string() and arguments should match accordingly. - * - * @attribute message - * @type Array - * @value [ - * 'networkdropped', - * 'moodle' - * ] - */ - message: { - value: [ - 'networkdropped', - 'moodle' - ] - }, - - /** - * Maxiumum count (not zero-based) of alerts to display for a single page load. - * - * @attribute maxalerts - * @type Number - * @value -1 - */ - maxalerts: { - value: -1 - } - } -}); - -M.core = M.core || {}; -M.core.checknet = M.core.checknet || {}; -M.core.checknet.init = function(config) { - return new CheckNet(config); -}; diff --git a/lib/yui/src/checknet/meta/checknet.json b/lib/yui/src/checknet/meta/checknet.json deleted file mode 100644 index a63efb02920..00000000000 --- a/lib/yui/src/checknet/meta/checknet.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moodle-core-checknet": { - "requires": [ - "base-base", - "moodle-core-notification-alert", - "io-base" - ] - } -} diff --git a/mod/scorm/player.php b/mod/scorm/player.php index 07bf5b5756e..f0cc78e674f 100644 --- a/mod/scorm/player.php +++ b/mod/scorm/player.php @@ -282,16 +282,9 @@ if (file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'.php')) include_once($CFG->dirroot.'/mod/scorm/datamodels/scorm_12.php'); } -// Add the checknet system to keep checking for a connection. -$PAGE->requires->string_for_js('networkdropped', 'mod_scorm'); -// Build arguments to send to checknet JS. -$args = array( - 'message' => array('networkdropped', 'mod_scorm'), - 'frequency' => 30000, // Frequency of network check. - 'timeout' => 10000, // Timeout of network check. - 'maxalerts' => 1 // Max number of alerts to be thrown. -); -$PAGE->requires->yui_module('moodle-core-checknet', 'M.core.checknet.init', array($args)); +// Add the keepalive system to keep checking for a connection. +\core\session\manager::keepalive('networkdropped', 'mod_scorm', 30, 10); + echo $OUTPUT->footer(); // Set the start time of this SCO. diff --git a/version.php b/version.php index d62f6f0b3c3..acea8f1861c 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2019053000.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2019060600.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.