From 5e7d561393a9cd42db270129386f6322d504d939 Mon Sep 17 00:00:00 2001 From: AMOS bot Date: Fri, 24 May 2019 00:15:06 +0000 Subject: [PATCH 1/7] Automatically generated installer lang files --- install/lang/el/admin.php | 4 ++-- install/lang/el/error.php | 2 +- install/lang/el/install.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/install/lang/el/admin.php b/install/lang/el/admin.php index a68438e39a6..4421c5ad64b 100644 --- a/install/lang/el/admin.php +++ b/install/lang/el/admin.php @@ -32,13 +32,13 @@ defined('MOODLE_INTERNAL') || die(); $string['clianswerno'] = 'ο'; $string['cliansweryes'] = 'ν'; -$string['cliincorrectvalueerror'] = 'Σφάλμα, λανθασμένη τιμή "{$a->value}" για το "{$a->option}"'; +$string['cliincorrectvalueerror'] = 'Σφάλμα, λανθασμένη τιμή «{$a->value}» για το «{$a->option}»'; $string['cliincorrectvalueretry'] = 'Λανθασμένη τιμή. Παρακαλούμε προσπαθήστε ξανά.'; $string['clitypevalue'] = 'πληκτρολογήστε μια τιμή'; $string['clitypevaluedefault'] = 'πληκτρολογήστε μια τιμή· πατήστε Enter για να χρησιμοποιήσετε τηνπροεπιλεγμένη τιμή ({$a})'; $string['cliunknowoption'] = 'Μη αναγνωρίσιμες επιλογές: {$a}
Παρακαλούμε χρησιμοποιήστε την επιλογή --βοήθεια'; -$string['cliyesnoprompt'] = 'πατώντας y (σημαίνει yes=ναι) αλλιώς πατώντας n (σημαίνει no=όχι)'; +$string['cliyesnoprompt'] = 'πατήστε y (σημαίνει yes=ναι) ή πατήστε n (σημαίνει no=όχι)'; $string['environmentrequireinstall'] = 'απαιτείται να εγκατασταθεί/ ενεργοποιηθεί'; $string['environmentrequireversion'] = 'απαιτείται η έκδοση {$a->needed} ενώ εσείς έχετε την {$a->current}'; $string['upgradekeyset'] = 'Κλειδί αναβάθμισης (αφήστε κενό για να μην το ορίσετε)'; diff --git a/install/lang/el/error.php b/install/lang/el/error.php index b4a6f494865..bf4b25dd8b6 100644 --- a/install/lang/el/error.php +++ b/install/lang/el/error.php @@ -44,7 +44,7 @@ $string['dmlexceptiononinstall'] = '

Παρουσιάστηκε σφάλμα $string['downloadedfilecheckfailed'] = 'Αποτυχία ελέγχου αρχείου που έγινε λήψη'; $string['invalidmd5'] = 'Μη έγκυρο md5'; $string['missingrequiredfield'] = 'Κάποιο απαιτούμενο πεδίο λείπει'; -$string['remotedownloaderror'] = '

Απέτυχε η λήψη του τμήματος στον εξυπηρετητή σας. Παρακαλούμε επιβεβαιώστε τις ρυθμίσεις του διακομιστή μεσολάβησης (proxy)· η επέκταση PHP cURL συνιστάται θερμά.

Πρέπει να κατεβάσετε το {$a->url} χειροκίνητα, να το αντιγράψετε στο "{$a->dest}" στον εξυπηρετητή σας και να το αποσυμπιέσετε εκεί.

'; +$string['remotedownloaderror'] = '

Απέτυχε η λήψη του τμήματος στον εξυπηρετητή σας. Παρακαλούμε επιβεβαιώστε τις ρυθμίσεις του διακομιστή μεσολάβησης (proxy)· η επέκταση PHP cURL συνιστάται θερμά.

Πρέπει να κατεβάσετε το {$a->url} χειροκίνητα, να το αντιγράψετε στο «{$a->dest}» στον εξυπηρετητή σας και να το αποσυμπιέσετε εκεί.

'; $string['wrongdestpath'] = 'Λανθασμένη διαδρομή προορισμού (πλήρες όνομα).'; $string['wrongsourcebase'] = 'Λανθασμένη βάση πηγής URL.'; $string['wrongzipfilename'] = 'Λανθασμένo όνομα αρχείου ZIP.'; diff --git a/install/lang/el/install.php b/install/lang/el/install.php index 3fa57285de5..37efea7bbbe 100644 --- a/install/lang/el/install.php +++ b/install/lang/el/install.php @@ -49,7 +49,7 @@ $string['environmentsub2'] = 'Κάθε έκδοση Moodle έχει κάποια Ο πλήρης έλεγχος του περιβάλλοντος πραγματοποιείται πριν κάθε εγκατάσταση και αναβάθμιση. Παρακαλούμε επικοινωνήστε με τον διαχειριστή του εξυπηρετητή εάν δεν ξέρετε πως να εγκαταστήσετε νέα έκδοση της PHP ή να ενεργοποιήσετε επεκτάσεις της.'; $string['errorsinenvironment'] = 'Ο έλεγχος του περιβάλλοντος απέτυχε!'; $string['installation'] = 'Εγκατάσταση'; -$string['langdownloaderror'] = 'Δυστυχώς η γλώσσα "{$a}" δεν είναι εγκατεστημένη. Η εγκατάσταση θα συνεχιστεί στα αγγλικά.'; +$string['langdownloaderror'] = 'Δυστυχώς η γλώσσα «{$a}» δεν είναι εγκατεστημένη. Η εγκατάσταση θα συνεχιστεί στα αγγλικά.'; $string['memorylimithelp'] = '

Το όριο μνήμης της PHP στον εξυπηρετητή σας είναι ορισμένο αυτή τη στιγμή στα {$a}.

Αυτό μπορεί να προκαλέσει προβλήματα μνήμης στο Moodle στη συνέχεια, ειδικά αν έχετε πολλά ενεργοποιημένα αρθρώματα και/ή πολλούς χρήστες.

Προτείνεται η ρύθμιση της PHP με μεγαλύτερο όριο, αν αυτό είναι δυνατό, π.χ. 40M. Υπάρχουν πολλοί τρόποι να το κάνετε αυτό, τους οποίους μπορείτε να δοκιμάσετε:

  1. Αν έχετε τη δυνατότητα, κάνετε επαναμεταγλώττιση την PHP με την παράμετρο --enable-memory-limit. Αυτό θα επιτρέψει στο Moodle να ορίσει μόνο του το όριο μνήμης.
  2. Αν έχετε πρόσβαση στο αρχείο php.ini, μπορείτε να αλλάξετε τη ρύθμιση memory_limit σε 40M. Αν δεν έχετε πρόσβαση ζητήστε από το διαχειριστή να το κάνει για εσάς.
  3. Σε κάποιους εξυπηρετητές PHP μπορείτε να δημιουργήσετε ένα αρχείο .htaccess στο φάκελο του Moodle που να περιέχει τις παρακάτω γραμμές:

    php_value memory_limit 40M

    Ωστόσο, σε κάποιους εξυπηρετητές αυτό θα εμποδίσει τη λειτουργία όλων των σελιδών PHP (θα βλέπετε σφάλματα όταν ανοίγετε τις σελίδες), οπότε θα πρέπει να διαγράψετε το αρχείο .htaccess.

'; $string['paths'] = 'Διαδρομές'; $string['pathserrcreatedataroot'] = 'Ο Φάκελος δεδομένων ({$a->dataroot}) δεν μπορεί να δημιουργθεί από το πρόγραμμα εγκατάστασης.'; @@ -76,5 +76,5 @@ $string['welcomep30'] = 'Αυτή η έκδοση/διανομή {$a->in $string['welcomep40'] = 'Το πακέτο περιλαμβάνει επίσης το Moodle {$a->moodlerelease} ({$a->moodleversion}).'; $string['welcomep50'] = 'Η χρήση όλων των εφαρμογών σε αυτό το πακέτο υπόκειται στις αντίστοιχες άδειες. Ολόκληρο το πακέτο {$a->installername} είναι λογισμικό ανοικτού κώδικα και διανέμεται με την GPL άδεια.'; $string['welcomep60'] = 'Οι παρακάτω σελίδες θα σας καθοδηγήσουν με εύκολα βήματα στην εγκατάσταση και ρύθμιση του Moodle στον υπολογιστή σας. Μπορείτε να δεχθείτε τις προεπιλεγμένες ρυθμίσεις ή προαιρετικά, να τις τροποποιήσετε ανάλογα με τις ανάγκες σας.'; -$string['welcomep70'] = 'Πατήστε το κουμπί "Συνέχεια" για να συνεχίσετε με την εκγατάσταση του Moodle.'; +$string['welcomep70'] = 'Πατήστε το κουμπί «Συνέχεια» για να συνεχίσετε με την εγκατάσταση του Moodle.'; $string['wwwroot'] = 'Διεύθυνση ιστού'; From b91f66b3c6615b7bf84f88204356685d3972ec50 Mon Sep 17 00:00:00 2001 From: AMOS bot Date: Sat, 25 May 2019 00:08:26 +0000 Subject: [PATCH 2/7] Automatically generated installer lang files --- install/lang/pt/install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/lang/pt/install.php b/install/lang/pt/install.php index 4ff411011fe..4affb829e0b 100644 --- a/install/lang/pt/install.php +++ b/install/lang/pt/install.php @@ -61,7 +61,7 @@ $string['pathssubdataroot'] = '

Uma diretoria em que o Moodle irá armazenar t

Não deve ser diretamente acessível através da web.

Se a diretoria não existir atualmente, o processo de instalação tentará criá-la.

'; $string['pathssubdirroot'] = 'Caminho completo para a diretoria que contém o código Moodle.'; -$string['pathssubwwwroot'] = 'Endereço web completo de acesso ao Moodle. Não é possível aceder ao Moodle usando mais do que um endereço. Se o site tiver mais do que um endereço público, devem ser configurados redirecionamentos permanentes em todos eles, à exceção deste. Se o site pode ser acedido a partir da Internet e de Intranet, então use o endereço público aqui. Se o endereço atual não está correto, altere o endereço indicado na barra de endereço do seu navegador e reinicie a instalação.'; +$string['pathssubwwwroot'] = 'Endereço web completo de acesso ao Moodle. Não é possível aceder ao Moodle usando mais do que um endereço. Se o site tiver mais do que um endereço público, devem ser configurados redirecionamentos permanentes em todos eles, à exceção deste. Se o site pode ser acedido a partir da Internet e de Intranet, use o endereço público aqui. Se o endereço atual não está correto, altere o endereço indicado na barra de endereço do seu navegador e reinicie a instalação.'; $string['pathsunsecuredataroot'] = 'A localização da pasta de dados não é segura'; $string['pathswrongadmindir'] = 'A pasta admin não existe'; $string['phpextension'] = 'Extensão {$a} do PHP'; From 6438d80ce901fa8a94c9d83f226a649a9232867d Mon Sep 17 00:00:00 2001 From: AMOS bot Date: Wed, 29 May 2019 00:09:11 +0000 Subject: [PATCH 3/7] Automatically generated installer lang files --- install/lang/es_ve/langconfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/lang/es_ve/langconfig.php b/install/lang/es_ve/langconfig.php index 65aaeb6d25c..12bed67bc5b 100644 --- a/install/lang/es_ve/langconfig.php +++ b/install/lang/es_ve/langconfig.php @@ -31,4 +31,4 @@ defined('MOODLE_INTERNAL') || die(); $string['parentlanguage'] = 'es'; -$string['thislanguage'] = 'Español Venezuela'; +$string['thislanguage'] = 'Español - Venezuela'; From ba2473e4e427c3bd4444b87fbe5a48119e965def Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Thu, 30 May 2019 12:18:18 +0200 Subject: [PATCH 4/7] weekly release 3.8dev --- version.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.php b/version.php index ea92e829a0c..d62f6f0b3c3 100644 --- a/version.php +++ b/version.php @@ -29,11 +29,11 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2019052000.01; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2019053000.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. -$release = '3.8dev (Build: 20190524)'; // Human-friendly version name +$release = '3.8dev (Build: 20190530)'; // Human-friendly version name $branch = '38'; // This version's branch. $maturity = MATURITY_ALPHA; // This version's maturity level. From 7aeeb44463cf1ddf4f34be0d139427da18d91459 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Thu, 10 Jan 2019 16:18:40 +0800 Subject: [PATCH 5/7] MDL-34498 session: Add a checker to warn before session expiry Replaces old yui module checknet. --- lang/en/error.php | 1 + lang/en/moodle.php | 4 +- lib/ajax/service.php | 5 +- lib/amd/build/ajax.min.js | 2 +- lib/amd/build/network.min.js | 1 + lib/amd/build/page_global.min.js | 2 +- lib/amd/src/ajax.js | 34 ++- lib/amd/src/network.js | 169 ++++++++++++++ lib/amd/src/page_global.js | 5 +- lib/classes/session/external.php | 95 ++++++++ lib/classes/session/manager.php | 51 ++-- lib/db/services.php | 16 ++ lib/outputrequirementslib.php | 1 + lib/sessionkeepalive_ajax.php | 36 --- lib/upgrade.txt | 3 + .../moodle-core-checknet/assets/checknet.txt | 1 - .../moodle-core-checknet-debug.js | 220 ------------------ .../moodle-core-checknet-min.js | 1 - .../moodle-core-checknet.js | 217 ----------------- lib/yui/src/checknet/assets/checknet.txt | 1 - lib/yui/src/checknet/build.json | 10 - lib/yui/src/checknet/js/checknet.js | 215 ----------------- lib/yui/src/checknet/meta/checknet.json | 9 - mod/scorm/player.php | 13 +- version.php | 2 +- 25 files changed, 367 insertions(+), 747 deletions(-) create mode 100644 lib/amd/build/network.min.js create mode 100644 lib/amd/src/network.js create mode 100644 lib/classes/session/external.php delete mode 100644 lib/sessionkeepalive_ajax.php delete mode 100644 lib/yui/build/moodle-core-checknet/assets/checknet.txt delete mode 100644 lib/yui/build/moodle-core-checknet/moodle-core-checknet-debug.js delete mode 100644 lib/yui/build/moodle-core-checknet/moodle-core-checknet-min.js delete mode 100644 lib/yui/build/moodle-core-checknet/moodle-core-checknet.js delete mode 100644 lib/yui/src/checknet/assets/checknet.txt delete mode 100644 lib/yui/src/checknet/build.json delete mode 100644 lib/yui/src/checknet/js/checknet.js delete mode 100644 lib/yui/src/checknet/meta/checknet.json 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.
  • Separate knowers remain as objective as possible without including feelings and emotions. In a discussion with other people, they like to defend their own ideas, using logic to find holes in opponent\'s ideas.
  • Connected knowers are more sensitive to other people. They are skilled at empathy and tend to listen and ask questions until they feel they can connect and "understand things from their point of view". They learn by trying to share the experiences that led to the knowledge they find in other people.
'; $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(m,h),!0}).fail(function(){d.alert("",j)})},n=function(){var a={methodname:"core_session_time_remaining",args:{}};return 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(m,h):setTimeout(n,5*k)},p=function(){f||(f=!0,o())},q=function(a,b,c){f||(f=!0,h=1e3*a,j=c,i=1e3*b,o())};return{keepalive:q,init:p}}); \ 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..6a313420d50 --- /dev/null +++ b/lib/amd/src/network.js @@ -0,0 +1,169 @@ +// 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 checkFrequency = (Config.sessiontimeout / 10) * 1000; + var warningLimit = checkFrequency * 2; // 1/5 of sessiontimeout. + + /** + * Ping the server to keep the session alive. + * + * @return {Promise} + */ + var touchSession = function() { + var request = { + methodname: 'core_session_touch', + args: { } + }; + + 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: { } + }; + + 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) { + 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. From ceacef1bd8594191ea8d86c2b58303a2946604a4 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Wed, 27 Mar 2019 13:20:28 +0800 Subject: [PATCH 6/7] MDL-34498 grade: rubric session timeout Allow a longer session timeout when editing a grading rubric form. --- grade/grading/form/rubric/edit.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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(); From 00fd749041fe6a9bfa5034e2a09bbb7f441c8bd2 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Mon, 6 May 2019 14:14:45 +0800 Subject: [PATCH 7/7] MDL-34498 session: Do not extend expired session After showing a warning, if the "extend session" button is clicked too late, tell them their session is expired so they can collect any unsaved data in the page. --- lib/amd/build/network.min.js | 2 +- lib/amd/src/network.js | 47 ++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/lib/amd/build/network.min.js b/lib/amd/build/network.min.js index 5b2c28230db..435d01789dd 100644 --- a/lib/amd/build/network.min.js +++ b/lib/amd/build/network.min.js @@ -1 +1 @@ -define(["jquery","core/ajax","core/config","core/notification","core/str"],function(a,b,c,d,e){var f=!1,g=!1,h=0,i=0,j=!1,k=c.sessiontimeout/10*1e3,l=2*k,m=function(){var a={methodname:"core_session_touch",args:{}};return b.call([a],!0,!0,!1,i)[0].then(function(){return h>0&&setTimeout(m,h),!0}).fail(function(){d.alert("",j)})},n=function(){var a={methodname:"core_session_time_remaining",args:{}};return 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(m,h):setTimeout(n,5*k)},p=function(){f||(f=!0,o())},q=function(a,b,c){f||(f=!0,h=1e3*a,j=c,i=1e3*b,o())};return{keepalive:q,init:p}}); \ No newline at end of file +define(["jquery","core/ajax","core/config","core/notification","core/str"],function(a,b,c,d,e){var f=!1,g=!1,h=0,i=0,j=!1,k=!1,l=1e3*Math.min(c.sessiontimeout/10,600),m=2*l,n=function(){k=!0},o=function(){var a={methodname:"core_session_touch",args:{}};return k?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):b.call([a],!0,!0,!1,i)[0].then(function(){return h>0&&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/src/network.js b/lib/amd/src/network.js index 6a313420d50..5e21a6a6fba 100644 --- a/lib/amd/src/network.js +++ b/lib/amd/src/network.js @@ -29,8 +29,18 @@ define(['jquery', 'core/ajax', 'core/config', 'core/notification', 'core/str'], var keepAliveFrequency = 0; var requestTimeout = 0; var keepAliveMessage = false; - var checkFrequency = (Config.sessiontimeout / 10) * 1000; - var warningLimit = checkFrequency * 2; // 1/5 of sessiontimeout. + 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. @@ -43,14 +53,28 @@ define(['jquery', 'core/ajax', 'core/config', 'core/notification', 'core/str'], args: { } }; - return Ajax.call([request], true, true, false, requestTimeout)[0].then(function() { - if (keepAliveFrequency > 0) { - setTimeout(touchSession, keepAliveFrequency); - } - return true; - }).fail(function() { - Notification.alert('', keepAliveMessage); - }); + 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); + }); + } }; /** @@ -65,6 +89,7 @@ define(['jquery', 'core/ajax', 'core/config', 'core/notification', 'core/str'], args: { } }; + sessionTimeout = false; return Ajax.call([request], true, true, true)[0].then(function(args) { if (args.userid <= 0) { return false; @@ -82,6 +107,8 @@ define(['jquery', 'core/ajax', 'core/config', 'core/notification', 'core/str'], }).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'},