From 5beb388516acbce2116f19e6d5e0a783226d2eaa Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Mon, 12 Aug 2019 20:12:07 +0800 Subject: [PATCH 1/5] MDL-66367 core: added new cachetemplates setting --- admin/settings/appearance.php | 5 +++++ config-dist.php | 3 +++ lang/en/admin.php | 3 +++ lib/upgrade.txt | 3 +++ version.php | 2 +- 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php index 4730c3217ad..aaae7100045 100644 --- a/admin/settings/appearance.php +++ b/admin/settings/appearance.php @@ -275,4 +275,9 @@ preferences,moodle|/user/preferences.php|t/preferences', $temp->add(new admin_setting_configtextarea('additionalhtmlfooter', new lang_string('additionalhtmlfooter', 'admin'), new lang_string('additionalhtmlfooter_desc', 'admin'), '', PARAM_RAW)); $ADMIN->add('appearance', $temp); + $setting = new admin_setting_configcheckbox('cachetemplates', new lang_string('cachetemplates', 'admin'), + new lang_string('cachetemplates_help', 'admin'), 1); + $temp = new admin_settingpage('templates', new lang_string('templates', 'admin')); + $temp->add($setting); + $ADMIN->add('appearance', $temp); } // end of speedup diff --git a/config-dist.php b/config-dist.php index 5e4ae974da1..cf3118a44d8 100644 --- a/config-dist.php +++ b/config-dist.php @@ -654,6 +654,9 @@ $CFG->admin = 'admin'; // Prevent JS caching // $CFG->cachejs = false; // NOT FOR PRODUCTION SERVERS! // +// Prevent Template caching +// $CFG->cachetemplates = false; // NOT FOR PRODUCTION SERVERS! +// // Restrict which YUI logging statements are shown in the browser console. // For details see the upstream documentation: // http://yuilibrary.com/yui/docs/api/classes/config.html#property_logInclude diff --git a/lang/en/admin.php b/lang/en/admin.php index fa8f10fa451..5b0b23a83b4 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -104,6 +104,8 @@ $string['bookmarkdeleted'] = 'Bookmark deleted.'; $string['bookmarkthispage'] = 'Bookmark this page'; $string['cachejs'] = 'Cache Javascript'; $string['cachejs_help'] = 'Javascript caching and compression greatly improves page loading performance. it is strongly recommended for production sites. Developers will probably want to disable this feature.'; +$string['cachetemplates'] = 'Cache Templates'; +$string['cachetemplates_help'] = 'Template caching will improve page loading performance and is strongly recommended for production sites. Developers will probably want to disable this feature.'; $string['calendarexportsalt'] = 'Calendar export salt'; $string['calendarsettings'] = 'Calendar'; $string['calendartype'] = 'Calendar type'; @@ -1264,6 +1266,7 @@ $string['taskstatscron'] = 'Background processing for statistics'; $string['tasktagcron'] = 'Background processing for tags'; $string['tasktempfilecleanup'] = 'Delete stale temp files'; $string['tempdatafoldercleanup'] = 'Clean up temporary data files older than'; +$string['templates'] = 'Templates'; $string['testoutgoingmailconf'] = 'Test outgoing mail configuration'; $string['testoutgoingmaildetail'] = 'Note: Before testing, please save your configuration.
{$a}'; $string['testoutgoingmailconf_message'] = 'This is a test message to confirm that you have successfully configured your site\'s outgoing mail.'; diff --git a/lib/upgrade.txt b/lib/upgrade.txt index c02d911edfb..ac0b686aa92 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -53,6 +53,9 @@ validation against and defaults to null (so, no user needed) if not provided. * Following behat steps have been removed from core: - I go to "" in the course gradebook * A new admin setting widget 'core_admin\local\settings\filesize' is added. +* A new setting 'Cache templates' was added (see MDL-66367). This setting determines if templates are cached or not. + This setting can be set via the UI or by defining $CFG->cachetemplates in your config.php file. It is a boolean + and should be set to either false or true. Developers will probably want to set this to false. === 3.7 === diff --git a/version.php b/version.php index 1a5a4ba23f9..5dbe5c3d43c 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2019083000.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2019090100.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. From e63395bbedd70e847c5918c6592fadb43c5c4ce2 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Tue, 13 Aug 2019 13:47:53 +0800 Subject: [PATCH 2/5] MDL-66367 core: added templaterev variable --- admin/classes/form/purge_caches.php | 1 + admin/settings/appearance.php | 1 + lang/en/admin.php | 1 + lib/db/install.php | 3 ++- lib/moodlelib.php | 5 +++- lib/outputrequirementslib.php | 36 +++++++++++++++++++++++++++++ 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/admin/classes/form/purge_caches.php b/admin/classes/form/purge_caches.php index 3151445e16b..ce44d1eb3a9 100644 --- a/admin/classes/form/purge_caches.php +++ b/admin/classes/form/purge_caches.php @@ -50,6 +50,7 @@ class purge_caches extends \moodleform { $mform->createElement('advcheckbox', 'theme', '', get_string('purgethemecache', 'admin')), $mform->createElement('advcheckbox', 'lang', '', get_string('purgelangcache', 'admin')), $mform->createElement('advcheckbox', 'js', '', get_string('purgejscache', 'admin')), + $mform->createElement('advcheckbox', 'template', '', get_string('purgetemplates', 'admin')), $mform->createElement('advcheckbox', 'filter', '', get_string('purgefiltercache', 'admin')), $mform->createElement('advcheckbox', 'muc', '', get_string('purgemuc', 'admin')), $mform->createElement('advcheckbox', 'other', '', get_string('purgeothercaches', 'admin')) diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php index aaae7100045..dc924f76bcc 100644 --- a/admin/settings/appearance.php +++ b/admin/settings/appearance.php @@ -277,6 +277,7 @@ preferences,moodle|/user/preferences.php|t/preferences', $setting = new admin_setting_configcheckbox('cachetemplates', new lang_string('cachetemplates', 'admin'), new lang_string('cachetemplates_help', 'admin'), 1); + $setting->set_updatedcallback('template_reset_all_caches'); $temp = new admin_settingpage('templates', new lang_string('templates', 'admin')); $temp->add($setting); $ADMIN->add('appearance', $temp); diff --git a/lang/en/admin.php b/lang/en/admin.php index 5b0b23a83b4..ffb92918146 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -1043,6 +1043,7 @@ $string['purgemuc'] = 'All MUC caches'; $string['purgeothercaches'] = 'All file and miscellaneous caches'; $string['purgeselectedcaches'] = 'Purge selected caches'; $string['purgeselectedcachesfinished'] = 'The selected caches were purged.'; +$string['purgetemplates'] = 'Templates'; $string['purgethemecache'] = 'Themes'; $string['requestcategoryselection'] = 'Enable category selection'; $string['restorecourse'] = 'Restore course'; diff --git a/lib/db/install.php b/lib/db/install.php index 59cebd4b3ed..71f216ee649 100644 --- a/lib/db/install.php +++ b/lib/db/install.php @@ -284,9 +284,10 @@ function xmldb_main_install() { set_role_contextlevels($guestrole, get_default_contextlevels('guest')); set_role_contextlevels($userrole, get_default_contextlevels('user')); - // Init theme and JS revisions + // Init theme, JS and template revisions. set_config('themerev', time()); set_config('jsrev', time()); + set_config('templaterev', time()); // No admin setting for this any more, GD is now required, remove in Moodle 2.6. set_config('gdversion', 2); diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 88855b9f9a8..2f230399475 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -1648,7 +1648,7 @@ function purge_all_caches() { * 'other' Purge all other caches? */ function purge_caches($options = []) { - $defaults = array_fill_keys(['muc', 'theme', 'lang', 'js', 'filter', 'other'], false); + $defaults = array_fill_keys(['muc', 'theme', 'lang', 'js', 'template', 'filter', 'other'], false); if (empty(array_filter($options))) { $options = array_fill_keys(array_keys($defaults), true); // Set all options to true. } else { @@ -1666,6 +1666,9 @@ function purge_caches($options = []) { if ($options['js']) { js_reset_all_caches(); } + if ($options['template']) { + template_reset_all_caches(); + } if ($options['filter']) { reset_text_filters_cache(); } diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index dd07e3dfb7d..df920a3e139 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -413,6 +413,25 @@ class page_requirements_manager { return $jsrev; } + /** + * Determine the correct Template revision to use for this load. + * + * @return int the templaterev to use. + */ + protected function get_templaterev() { + global $CFG; + + if (empty($CFG->cachetemplates)) { + $templaterev = -1; + } else if (empty($CFG->templaterev)) { + $templaterev = 1; + } else { + $templaterev = $CFG->templaterev; + } + + return $templaterev; + } + /** * Ensure that the specified JavaScript file is linked to from this page. * @@ -2104,6 +2123,23 @@ class YUI_config { } } +/** + * Invalidate all server and client side template caches. + */ +function template_reset_all_caches() { + global $CFG; + + $next = time(); + if (isset($CFG->templaterev) and $next <= $CFG->templaterev and $CFG->templaterev - $next < 60 * 60) { + // This resolves problems when reset is requested repeatedly within 1s, + // the < 1h condition prevents accidental switching to future dates + // because we might not recover from it. + $next = $CFG->templaterev + 1; + } + + set_config('templaterev', $next); +} + /** * Invalidate all server and client side JS caches. */ From 988482f07769670a42fb7ea1578da9cd6fc24dd5 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Mon, 12 Aug 2019 22:12:58 +0800 Subject: [PATCH 3/5] MDL-66367 core: make templaterev available in JS --- lib/outputrequirementslib.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index df920a3e139..3ecdb37679e 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -331,7 +331,8 @@ class page_requirements_manager { 'svgicons' => $page->theme->use_svg_icons(), 'usertimezone' => usertimezone(), 'contextid' => $contextid, - 'langrev' => get_string_manager()->get_revision() + 'langrev' => get_string_manager()->get_revision(), + 'templaterev' => $this->get_templaterev() ); if ($CFG->debugdeveloper) { $this->M_cfg['developerdebug'] = true; From 9e5edc7f68a8a897e6ed8b9293a68ab9cf112e77 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Mon, 12 Aug 2019 22:15:39 +0800 Subject: [PATCH 4/5] MDL-66367 core: use templaterev as the cache key when getting templates --- lib/amd/build/templates.min.js | 2 +- lib/amd/build/templates.min.js.map | 2 +- lib/amd/src/templates.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/amd/build/templates.min.js b/lib/amd/build/templates.min.js index a2dcac93bb1..221d4cfa80a 100644 --- a/lib/amd/build/templates.min.js +++ b/lib/amd/build/templates.min.js @@ -1,2 +1,2 @@ -define ("core/templates",["core/mustache","jquery","core/ajax","core/str","core/notification","core/url","core/config","core/localstorage","core/icon_system","core/event","core/yui","core/log","core/truncate","core/user_date","core/pending"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p=0,q={},r={},s={},t={},u=[],v=!1,w=function(a){if(a in r){return r[a]}if(a in q){r[a]=b.Deferred().resolve(q[a]).promise();return r[a]}var c=h.get("core_template/"+a);if(c){q[a]=c;r[a]=b.Deferred().resolve(c).promise();return r[a]}return null},x=function(){if(!u.length){return}if(v){return}v=!0;var a=u.slice(),e=b.Deferred(),f=[],g=a.map(function(a){var c=a.component,i=a.name,j=a.searchKey,k=a.theme,l=a.deferred,m=null,n=w(j);if(n){m=n}else{f.push({methodname:"core_output_load_template_with_dependencies",args:{component:c,template:i,themename:k,lang:b("html").attr("lang").replace(/-/g,"_")}});var o=f.length-1;m=e.promise().then(function(a){g[j]=a[o].then(function(a){var b=null;a.templates.forEach(function(a){var d=[k,a.component,a.name].join("/");q[d]=a.value;h.set("core_template/"+d,a.value);if(a.component==c&&a.name==i){b=a.value}});if(a.strings.length){d.cache_strings(a.strings.map(function(a){return{component:a.component,key:a.name,value:a.value}}))}return b});return g[j]})}return m.then(function(a){return l.resolve(a)}).catch(function(a){l.reject(a);throw a})});if(f.length){e.resolve(c.call(f,!0,!1,!1,0,M.cfg.themerev))}else{e.resolve()}b.when.apply(null,g).then(function(){u.splice(0,a.length);v=!1;x()}).catch(function(){u.splice(0,a.length);v=!1;x()})},y=function(){this.requiredStrings=[];this.requiredJS=[];this.requiredDates=[];this.currentThemeName=""};y.prototype.requiredStrings=null;y.prototype.requiredDates=[];y.prototype.requiredJS=null;y.prototype.currentThemeName="";y.prototype.getTemplate=function(a){var c=this.currentThemeName,d=c+"/"+a,e=w(d);if(e){return e}var f=u.filter(function(a){return a.searchKey==d});if(f.length){return f[0].deferred.promise()}var g=a.split("/"),h=g.shift(),i=g.join("/"),j=b.Deferred();u.push({component:h,name:i,theme:c,searchKey:d,deferred:j});x();return j.promise()};y.prototype.partialHelper=function(a){var b=this.currentThemeName+"/"+a;if(!(b in q)){e.exception(new Error("Failed to pre-fetch the template: "+a))}return q[b]};y.prototype.renderIcon=function(a,c,d){var f=g.iconsystemmodule,h=b.Deferred();require([f],function(a){var b=new a;if(!(b instanceof i)){h.reject("Invalid icon system specified"+g.iconsystemmodule)}else{t=b;b.init().then(h.resolve).catch(e.exception)}});return h.then(function(a){return this.getTemplate(a.getTemplateName())}.bind(this)).then(function(b){return t.renderIcon(a,c,d,b)})};y.prototype.pixHelper=function(a,b,c){var d=b.split(","),e="",f="",g="";if(0=}}$1<%={{ }}=%>").replace(/(\r\n|\r|\n)/g," ");return"\""+d+"\""};y.prototype.shortenTextHelper=function(a,b,c){var d=/(.*?),(.*)/,e=b.match(d),f=e[1].trim(),g=e[2].trim(),h=c(g,a);return m.truncate(h,{length:f,words:!0,ellipsis:"..."})};y.prototype.userDateHelper=function(a,b,c){var d=/(.*?),(.*)/,e=b.match(d),f=c(e[1].trim(),a),g=c(e[2].trim(),a),h=this.requiredDates.length;this.requiredDates.push({timestamp:f,format:g});return"[[_t_"+h+"]]"};y.prototype.addHelpers=function(a,b){this.currentThemeName=b;this.requiredStrings=[];this.requiredJS=[];a.uniqid=p++;a.str=function(){return this.stringHelper.bind(this,a)}.bind(this);a.pix=function(){return this.pixHelper.bind(this,a)}.bind(this);a.js=function(){return this.jsHelper.bind(this,a)}.bind(this);a.quote=function(){return this.quoteHelper.bind(this,a)}.bind(this);a.shortentext=function(){return this.shortenTextHelper.bind(this,a)}.bind(this);a.userdate=function(){return this.userDateHelper.bind(this,a)}.bind(this);a.globals={config:g};a.currentTheme=b};y.prototype.getJS=function(){var a="";if(0").attr("type","text/javascript").html(a);b("head").append(c)}},A=function(a,c,d,e){var f=b(a);if(f.length){var g=b(c),h=null;if(e){h=new k.NodeList(f.children().get());h.destroy(!0);f.empty();f.append(g)}else{h=new k.NodeList(f.get());h.destroy(!0);f.replaceWith(g)}z(d);j.notifyFilterContentUpdated(g)}};y.prototype.scanForPartials=function(b){var c=a.parse(b),d=[],e=function(a,b){var c,d;for(c=0;c"==d[0]||"<"==d[0]){b.push(d[1])}if(4=}}$1<%={{ }}=%>").replace(/(\r\n|\r|\n)/g," ");return"\""+d+"\""};y.prototype.shortenTextHelper=function(a,b,c){var d=/(.*?),(.*)/,e=b.match(d),f=e[1].trim(),g=e[2].trim(),h=c(g,a);return m.truncate(h,{length:f,words:!0,ellipsis:"..."})};y.prototype.userDateHelper=function(a,b,c){var d=/(.*?),(.*)/,e=b.match(d),f=c(e[1].trim(),a),g=c(e[2].trim(),a),h=this.requiredDates.length;this.requiredDates.push({timestamp:f,format:g});return"[[_t_"+h+"]]"};y.prototype.addHelpers=function(a,b){this.currentThemeName=b;this.requiredStrings=[];this.requiredJS=[];a.uniqid=p++;a.str=function(){return this.stringHelper.bind(this,a)}.bind(this);a.pix=function(){return this.pixHelper.bind(this,a)}.bind(this);a.js=function(){return this.jsHelper.bind(this,a)}.bind(this);a.quote=function(){return this.quoteHelper.bind(this,a)}.bind(this);a.shortentext=function(){return this.shortenTextHelper.bind(this,a)}.bind(this);a.userdate=function(){return this.userDateHelper.bind(this,a)}.bind(this);a.globals={config:g};a.currentTheme=b};y.prototype.getJS=function(){var a="";if(0").attr("type","text/javascript").html(a);b("head").append(c)}},A=function(a,c,d,e){var f=b(a);if(f.length){var g=b(c),h=null;if(e){h=new k.NodeList(f.children().get());h.destroy(!0);f.empty();f.append(g)}else{h=new k.NodeList(f.get());h.destroy(!0);f.replaceWith(g)}z(d);j.notifyFilterContentUpdated(g)}};y.prototype.scanForPartials=function(b){var c=a.parse(b),d=[],e=function(a,b){var c,d;for(c=0;c"==d[0]||"<"==d[0]){b.push(d[1])}if(4.\n\n/**\n * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module core/templates\n * @package core\n * @class templates\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\ndefine([\n 'core/mustache',\n 'jquery',\n 'core/ajax',\n 'core/str',\n 'core/notification',\n 'core/url',\n 'core/config',\n 'core/localstorage',\n 'core/icon_system',\n 'core/event',\n 'core/yui',\n 'core/log',\n 'core/truncate',\n 'core/user_date',\n 'core/pending',\n ],\n function(mustache, $, ajax, str, notification, coreurl, config, storage, IconSystem, event, Y, Log, Truncate, UserDate,\n Pending) {\n\n // Module variables.\n /** @var {Number} uniqInstances Count of times this constructor has been called. */\n var uniqInstances = 0;\n\n /** @var {String[]} templateCache - Cache of already loaded template strings */\n var templateCache = {};\n\n /** @var {Promise[]} templatePromises - Cache of already loaded template promises */\n var templatePromises = {};\n\n /** @var {Promise[]} cachePartialPromises - Cache of already loaded template partial promises */\n var cachePartialPromises = {};\n\n /** @var {Object} iconSystem - Object extending core/iconsystem */\n var iconSystem = {};\n\n /** @var {Object[]} loadTemplateBuffer - List of templates to be loaded */\n var loadTemplateBuffer = [];\n\n /** @var {Bool} isLoadingTemplates - Whether templates are currently being loaded */\n var isLoadingTemplates = false;\n\n /**\n * Search the various caches for a template promise for the given search key.\n * The search key should be in the format //