diff --git a/lib/amd/build/user_date.min.js b/lib/amd/build/user_date.min.js index 6f512ef86f7..c221d1ad200 100644 --- a/lib/amd/build/user_date.min.js +++ b/lib/amd/build/user_date.min.js @@ -1,2 +1,2 @@ -define ("core/user_date",["jquery","core/ajax","core/sessionstorage","core/config"],function(a,b,c,d){var e={},f=function(b){var c=a("html").attr("lang").replace(/-/g,"_");return"core_user_date/"+c+"/"+d.usertimezone+"/"+b.timestamp+"/"+b.format},g=function(a){return c.get(a)},h=function(a,b){c.set(a,b)},i=function(a){return"undefined"!=typeof e[a]},j=function(a){return e[a]},k=function(a,b){e[a]=b},l=function(a){var c=a.map(function(a){return{timestamp:a.timestamp,format:a.format}}),e={methodname:"core_get_user_dates",args:{contextid:d.contextid,timestamps:c}};return b.call([e],!0,!0)[0].then(function(b){b.dates.forEach(function(b,c){var d=a[c],e=f(d);h(e,b);d.deferred.resolve(b)})}).catch(function(b){a.forEach(function(a){a.deferred.reject(b)})})};return{get:function get(b){var c=[],d=[];b.forEach(function(b){var e=f(b);if(i(e)){d.push(j(e))}else{var h=a.Deferred(),l=g(e);if(l){h.resolve(l)}else{b.deferred=h;c.push(b)}k(e,h.promise());d.push(h.promise())}});if(c.length){l(c)}return a.when.apply(a,d).then(function(){return 1===arguments.length?[arguments[0]]:Array.apply(null,arguments)})},getUserMidnightForTimestamp:function getUserMidnightForTimestamp(a,b){var c=a>b,d=Math.abs(a-b),e=c?Math.floor(d/86400):Math.ceil(d/86400),f=e*86400,g=c?b+f:b-f;return g}}}); +define ("core/user_date",["jquery","core/ajax","core/sessionstorage","core/config"],function(a,b,c,d){var e={},f=function(b){var c=a("html").attr("lang").replace(/-/g,"_");return"core_user_date/"+c+"/"+d.usertimezone+"/"+b.timestamp+"/"+b.format},g=function(a){return c.get(a)},h=function(a,b){c.set(a,b)},i=function(a){return"undefined"!=typeof e[a]},j=function(a){return e[a]},k=function(a,b){e[a]=b},l=function(a){var c=a.map(function(a){var b=a.hasOwnProperty("fixday")?a.fixday:1,c=a.hasOwnProperty("fixhour")?a.fixhour:1;return{timestamp:a.timestamp,format:a.format,type:a.type||"",fixday:b,fixhour:c}}),e={methodname:"core_get_user_dates",args:{contextid:d.contextid,timestamps:c}};return b.call([e],!0,!0)[0].then(function(b){b.dates.forEach(function(b,c){var d=a[c],e=f(d);h(e,b);d.deferred.resolve(b)})}).catch(function(b){a.forEach(function(a){a.deferred.reject(b)})})};return{get:function get(b){var c=[],d=[];b.forEach(function(b){var e=f(b);if(i(e)){d.push(j(e))}else{var h=a.Deferred(),l=g(e);if(l){h.resolve(l)}else{b.deferred=h;c.push(b)}k(e,h.promise());d.push(h.promise())}});if(c.length){l(c)}return a.when.apply(a,d).then(function(){return 1===arguments.length?[arguments[0]]:Array.apply(null,arguments)})},getUserMidnightForTimestamp:function getUserMidnightForTimestamp(a,b){var c=a>b,d=Math.abs(a-b),e=c?Math.floor(d/86400):Math.ceil(d/86400),f=e*86400,g=c?b+f:b-f;return g}}}); //# sourceMappingURL=user_date.min.js.map diff --git a/lib/amd/build/user_date.min.js.map b/lib/amd/build/user_date.min.js.map index 898b39a2cf1..87973d0b0a3 100644 --- a/lib/amd/build/user_date.min.js.map +++ b/lib/amd/build/user_date.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/user_date.js"],"names":["define","$","Ajax","Storage","Config","promisesCache","getKey","request","language","attr","replace","usertimezone","timestamp","format","getFromLocalStorage","key","get","addToLocalStorage","value","set","inPromisesCache","getFromPromisesCache","addToPromisesCache","promise","loadDatesFromServer","dates","args","map","data","methodname","contextid","timestamps","call","then","results","forEach","index","date","deferred","resolve","catch","ex","reject","requests","ajaxRequests","promises","push","Deferred","cached","length","when","apply","arguments","Array","getUserMidnightForTimestamp","todayMidnight","future","diffSeconds","Math","abs","diffDays","floor","ceil","diffDaysInSeconds","dayTimestamp"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Fetch and render dates from timestamps.\n *\n * @module core/user_date\n * @package core\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'],\n function($, Ajax, Storage, Config) {\n\n var SECONDS_IN_DAY = 86400;\n\n /** @var {object} promisesCache Store all promises we've seen so far. */\n var promisesCache = {};\n\n /**\n * Generate a cache key for the given request. The request should\n * have a timestamp and format key.\n *\n * @param {object} request\n * @return {string}\n */\n var getKey = function(request) {\n var language = $('html').attr('lang').replace(/-/g, '_');\n return 'core_user_date/' +\n language + '/' +\n Config.usertimezone + '/' +\n request.timestamp + '/' +\n request.format;\n };\n\n /**\n * Retrieve a transformed date from the browser's storage.\n *\n * @param {string} key\n * @return {string}\n */\n var getFromLocalStorage = function(key) {\n return Storage.get(key);\n };\n\n /**\n * Save the transformed date in the browser's storage.\n *\n * @param {string} key\n * @param {string} value\n */\n var addToLocalStorage = function(key, value) {\n Storage.set(key, value);\n };\n\n /**\n * Check if a key is in the module's cache.\n *\n * @param {string} key\n * @return {bool}\n */\n var inPromisesCache = function(key) {\n return (typeof promisesCache[key] !== 'undefined');\n };\n\n /**\n * Retrieve a promise from the module's cache.\n *\n * @param {string} key\n * @return {object} jQuery promise\n */\n var getFromPromisesCache = function(key) {\n return promisesCache[key];\n };\n\n /**\n * Save the given promise in the module's cache.\n *\n * @param {string} key\n * @param {object} promise\n */\n var addToPromisesCache = function(key, promise) {\n promisesCache[key] = promise;\n };\n\n /**\n * Send a request to the server for each of the required timestamp\n * and format combinations.\n *\n * Resolves the date's deferred with the values returned from the\n * server and saves the value in local storage.\n *\n * @param {array} dates\n * @return {object} jQuery promise\n */\n var loadDatesFromServer = function(dates) {\n var args = dates.map(function(data) {\n return {\n timestamp: data.timestamp,\n format: data.format\n };\n });\n\n var request = {\n methodname: 'core_get_user_dates',\n args: {\n contextid: Config.contextid,\n timestamps: args\n }\n };\n\n return Ajax.call([request], true, true)[0].then(function(results) {\n results.dates.forEach(function(value, index) {\n var date = dates[index];\n var key = getKey(date);\n\n addToLocalStorage(key, value);\n date.deferred.resolve(value);\n });\n return;\n })\n .catch(function(ex) {\n // If we failed to retrieve the dates then reject the date's\n // deferred objects to make sure they don't hang.\n dates.forEach(function(date) {\n date.deferred.reject(ex);\n });\n });\n };\n\n /**\n * Takes an array of request objects and returns a promise that\n * is resolved with an array of formatted dates.\n *\n * The values in the returned array will be ordered the same as\n * the request array.\n *\n * This function will check both the module's static promises cache\n * and the browser's session storage to see if the user dates have\n * already been loaded in order to avoid sending a network request\n * if possible.\n *\n * Only dates not found in either cache will be sent to the server\n * for transforming.\n *\n * A request object must have a timestamp key and a format key.\n *\n * E.g.\n * var request = [\n * {\n * timestamp: 1293876000,\n * format: '%d %B %Y'\n * },\n * {\n * timestamp: 1293876000,\n * format: '%A, %d %B %Y, %I:%M %p'\n * }\n * ];\n *\n * UserDate.get(request).done(function(dates) {\n * console.log(dates[0]); // prints \"1 January 2011\".\n * console.log(dates[1]); // prints \"Saturday, 1 January 2011, 10:00 AM\".\n * });\n *\n * @param {array} requests\n * @return {object} jQuery promise\n */\n var get = function(requests) {\n var ajaxRequests = [];\n var promises = [];\n\n // Loop over each of the requested timestamp/format combos\n // and add a promise to the promises array for them.\n requests.forEach(function(request) {\n var key = getKey(request);\n\n // If we've already got a promise then use it.\n if (inPromisesCache(key)) {\n promises.push(getFromPromisesCache(key));\n } else {\n var deferred = $.Deferred();\n var cached = getFromLocalStorage(key);\n\n if (cached) {\n // If we were able to get the value from session storage\n // then we can resolve the deferred with that value. No\n // need to ask the server to transform it for us.\n deferred.resolve(cached);\n } else {\n // Add this request to the list of ones we need to load\n // from the server. Include the deferred so that it can\n // be resolved when the server has responded with the\n // transformed values.\n request.deferred = deferred;\n ajaxRequests.push(request);\n }\n\n // Remember this promise for next time so that we can\n // bail out early if it is requested again.\n addToPromisesCache(key, deferred.promise());\n promises.push(deferred.promise());\n }\n });\n\n // If we have any requests that we couldn't resolve from the caches\n // then let's ask the server to get them for us.\n if (ajaxRequests.length) {\n loadDatesFromServer(ajaxRequests);\n }\n\n // Wait for all of the promises to resolve. Some of them may be waiting\n // for a response from the server.\n return $.when.apply($, promises).then(function() {\n // This looks complicated but it's just converting an unknown\n // length of arguments into an array for the promise to resolve\n // with.\n return arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);\n });\n };\n\n\n /**\n * For a given timestamp get the midnight value in the user's timezone.\n *\n * The calculation is performed relative to the user's midnight timestamp\n * for today to ensure that timezones are preserved.\n *\n * E.g.\n * Input:\n * timestamp: 1514836800 (01/01/2018 8pm GMT)(02/01/2018 4am GMT+8)\n * midnight: 1514851200 (02/01/2018 midnight GMT)\n * Output:\n * 1514764800 (01/01/2018 midnight GMT)\n *\n * Input:\n * timestamp: 1514836800 (01/01/2018 8pm GMT)(02/01/2018 4am GMT+8)\n * midnight: 1514822400 (02/01/2018 midnight GMT+8)\n * Output:\n * 1514822400 (02/01/2018 midnight GMT+8)\n *\n * @param {Number} timestamp The timestamp to calculate from\n * @param {Number} todayMidnight The user's midnight timestamp\n * @return {Number} The midnight value of the user's timestamp\n */\n var getUserMidnightForTimestamp = function(timestamp, todayMidnight) {\n var future = timestamp > todayMidnight;\n var diffSeconds = Math.abs(timestamp - todayMidnight);\n var diffDays = future ? Math.floor(diffSeconds / SECONDS_IN_DAY) : Math.ceil(diffSeconds / SECONDS_IN_DAY);\n var diffDaysInSeconds = diffDays * SECONDS_IN_DAY;\n // Is the timestamp in the future or past?\n var dayTimestamp = future ? todayMidnight + diffDaysInSeconds : todayMidnight - diffDaysInSeconds;\n return dayTimestamp;\n };\n\n return {\n get: get,\n getUserMidnightForTimestamp: getUserMidnightForTimestamp\n };\n});\n"],"file":"user_date.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/user_date.js"],"names":["define","$","Ajax","Storage","Config","promisesCache","getKey","request","language","attr","replace","usertimezone","timestamp","format","getFromLocalStorage","key","get","addToLocalStorage","value","set","inPromisesCache","getFromPromisesCache","addToPromisesCache","promise","loadDatesFromServer","dates","args","map","data","fixDay","hasOwnProperty","fixday","fixHour","fixhour","type","methodname","contextid","timestamps","call","then","results","forEach","index","date","deferred","resolve","catch","ex","reject","requests","ajaxRequests","promises","push","Deferred","cached","length","when","apply","arguments","Array","getUserMidnightForTimestamp","todayMidnight","future","diffSeconds","Math","abs","diffDays","floor","ceil","diffDaysInSeconds","dayTimestamp"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Fetch and render dates from timestamps.\n *\n * @module core/user_date\n * @package core\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'],\n function($, Ajax, Storage, Config) {\n\n var SECONDS_IN_DAY = 86400;\n\n /** @var {object} promisesCache Store all promises we've seen so far. */\n var promisesCache = {};\n\n /**\n * Generate a cache key for the given request. The request should\n * have a timestamp and format key.\n *\n * @param {object} request\n * @return {string}\n */\n var getKey = function(request) {\n var language = $('html').attr('lang').replace(/-/g, '_');\n return 'core_user_date/' +\n language + '/' +\n Config.usertimezone + '/' +\n request.timestamp + '/' +\n request.format;\n };\n\n /**\n * Retrieve a transformed date from the browser's storage.\n *\n * @param {string} key\n * @return {string}\n */\n var getFromLocalStorage = function(key) {\n return Storage.get(key);\n };\n\n /**\n * Save the transformed date in the browser's storage.\n *\n * @param {string} key\n * @param {string} value\n */\n var addToLocalStorage = function(key, value) {\n Storage.set(key, value);\n };\n\n /**\n * Check if a key is in the module's cache.\n *\n * @param {string} key\n * @return {bool}\n */\n var inPromisesCache = function(key) {\n return (typeof promisesCache[key] !== 'undefined');\n };\n\n /**\n * Retrieve a promise from the module's cache.\n *\n * @param {string} key\n * @return {object} jQuery promise\n */\n var getFromPromisesCache = function(key) {\n return promisesCache[key];\n };\n\n /**\n * Save the given promise in the module's cache.\n *\n * @param {string} key\n * @param {object} promise\n */\n var addToPromisesCache = function(key, promise) {\n promisesCache[key] = promise;\n };\n\n /**\n * Send a request to the server for each of the required timestamp\n * and format combinations.\n *\n * Resolves the date's deferred with the values returned from the\n * server and saves the value in local storage.\n *\n * @param {array} dates\n * @return {object} jQuery promise\n */\n var loadDatesFromServer = function(dates) {\n var args = dates.map(function(data) {\n var fixDay = data.hasOwnProperty('fixday') ? data.fixday : 1;\n var fixHour = data.hasOwnProperty('fixhour') ? data.fixhour : 1;\n return {\n timestamp: data.timestamp,\n format: data.format,\n type: data.type || '',\n fixday: fixDay,\n fixhour: fixHour\n };\n });\n\n var request = {\n methodname: 'core_get_user_dates',\n args: {\n contextid: Config.contextid,\n timestamps: args\n }\n };\n\n return Ajax.call([request], true, true)[0].then(function(results) {\n results.dates.forEach(function(value, index) {\n var date = dates[index];\n var key = getKey(date);\n\n addToLocalStorage(key, value);\n date.deferred.resolve(value);\n });\n return;\n })\n .catch(function(ex) {\n // If we failed to retrieve the dates then reject the date's\n // deferred objects to make sure they don't hang.\n dates.forEach(function(date) {\n date.deferred.reject(ex);\n });\n });\n };\n\n /**\n * Takes an array of request objects and returns a promise that\n * is resolved with an array of formatted dates.\n *\n * The values in the returned array will be ordered the same as\n * the request array.\n *\n * This function will check both the module's static promises cache\n * and the browser's session storage to see if the user dates have\n * already been loaded in order to avoid sending a network request\n * if possible.\n *\n * Only dates not found in either cache will be sent to the server\n * for transforming.\n *\n * A request object must have a timestamp key and a format key and\n * optionally may have a type key.\n *\n * E.g.\n * var request = [\n * {\n * timestamp: 1293876000,\n * format: '%d %B %Y'\n * },\n * {\n * timestamp: 1293876000,\n * format: '%A, %d %B %Y, %I:%M %p',\n * type: 'gregorian',\n * fixday: false,\n * fixhour: false\n * }\n * ];\n *\n * UserDate.get(request).done(function(dates) {\n * console.log(dates[0]); // prints \"1 January 2011\".\n * console.log(dates[1]); // prints \"Saturday, 1 January 2011, 10:00 AM\".\n * });\n *\n * @param {array} requests\n * @return {object} jQuery promise\n */\n var get = function(requests) {\n var ajaxRequests = [];\n var promises = [];\n\n // Loop over each of the requested timestamp/format combos\n // and add a promise to the promises array for them.\n requests.forEach(function(request) {\n var key = getKey(request);\n\n // If we've already got a promise then use it.\n if (inPromisesCache(key)) {\n promises.push(getFromPromisesCache(key));\n } else {\n var deferred = $.Deferred();\n var cached = getFromLocalStorage(key);\n\n if (cached) {\n // If we were able to get the value from session storage\n // then we can resolve the deferred with that value. No\n // need to ask the server to transform it for us.\n deferred.resolve(cached);\n } else {\n // Add this request to the list of ones we need to load\n // from the server. Include the deferred so that it can\n // be resolved when the server has responded with the\n // transformed values.\n request.deferred = deferred;\n ajaxRequests.push(request);\n }\n\n // Remember this promise for next time so that we can\n // bail out early if it is requested again.\n addToPromisesCache(key, deferred.promise());\n promises.push(deferred.promise());\n }\n });\n\n // If we have any requests that we couldn't resolve from the caches\n // then let's ask the server to get them for us.\n if (ajaxRequests.length) {\n loadDatesFromServer(ajaxRequests);\n }\n\n // Wait for all of the promises to resolve. Some of them may be waiting\n // for a response from the server.\n return $.when.apply($, promises).then(function() {\n // This looks complicated but it's just converting an unknown\n // length of arguments into an array for the promise to resolve\n // with.\n return arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);\n });\n };\n\n\n /**\n * For a given timestamp get the midnight value in the user's timezone.\n *\n * The calculation is performed relative to the user's midnight timestamp\n * for today to ensure that timezones are preserved.\n *\n * E.g.\n * Input:\n * timestamp: 1514836800 (01/01/2018 8pm GMT)(02/01/2018 4am GMT+8)\n * midnight: 1514851200 (02/01/2018 midnight GMT)\n * Output:\n * 1514764800 (01/01/2018 midnight GMT)\n *\n * Input:\n * timestamp: 1514836800 (01/01/2018 8pm GMT)(02/01/2018 4am GMT+8)\n * midnight: 1514822400 (02/01/2018 midnight GMT+8)\n * Output:\n * 1514822400 (02/01/2018 midnight GMT+8)\n *\n * @param {Number} timestamp The timestamp to calculate from\n * @param {Number} todayMidnight The user's midnight timestamp\n * @return {Number} The midnight value of the user's timestamp\n */\n var getUserMidnightForTimestamp = function(timestamp, todayMidnight) {\n var future = timestamp > todayMidnight;\n var diffSeconds = Math.abs(timestamp - todayMidnight);\n var diffDays = future ? Math.floor(diffSeconds / SECONDS_IN_DAY) : Math.ceil(diffSeconds / SECONDS_IN_DAY);\n var diffDaysInSeconds = diffDays * SECONDS_IN_DAY;\n // Is the timestamp in the future or past?\n var dayTimestamp = future ? todayMidnight + diffDaysInSeconds : todayMidnight - diffDaysInSeconds;\n return dayTimestamp;\n };\n\n return {\n get: get,\n getUserMidnightForTimestamp: getUserMidnightForTimestamp\n };\n});\n"],"file":"user_date.min.js"} \ No newline at end of file diff --git a/lib/amd/src/user_date.js b/lib/amd/src/user_date.js index 20ea44cf38f..95efe3d8632 100644 --- a/lib/amd/src/user_date.js +++ b/lib/amd/src/user_date.js @@ -107,9 +107,14 @@ define(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'], */ var loadDatesFromServer = function(dates) { var args = dates.map(function(data) { + var fixDay = data.hasOwnProperty('fixday') ? data.fixday : 1; + var fixHour = data.hasOwnProperty('fixhour') ? data.fixhour : 1; return { timestamp: data.timestamp, - format: data.format + format: data.format, + type: data.type || '', + fixday: fixDay, + fixhour: fixHour }; }); @@ -155,7 +160,8 @@ define(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'], * Only dates not found in either cache will be sent to the server * for transforming. * - * A request object must have a timestamp key and a format key. + * A request object must have a timestamp key and a format key and + * optionally may have a type key. * * E.g. * var request = [ @@ -165,7 +171,10 @@ define(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'], * }, * { * timestamp: 1293876000, - * format: '%A, %d %B %Y, %I:%M %p' + * format: '%A, %d %B %Y, %I:%M %p', + * type: 'gregorian', + * fixday: false, + * fixhour: false * } * ]; * diff --git a/lib/external/externallib.php b/lib/external/externallib.php index 6721d2af140..c8e08abbb20 100644 --- a/lib/external/externallib.php +++ b/lib/external/externallib.php @@ -120,7 +120,7 @@ class core_external extends external_api { /** * Returns description of get_string() result value * - * @return string + * @return external_description * @since Moodle 2.4 */ public static function get_string_returns() { @@ -189,7 +189,7 @@ class core_external extends external_api { /** * Returns description of get_string() result value * - * @return array + * @return external_description * @since Moodle 2.4 */ public static function get_strings_returns() { @@ -233,6 +233,9 @@ class core_external extends external_api { [ 'timestamp' => new external_value(PARAM_INT, 'unix timestamp'), 'format' => new external_value(PARAM_TEXT, 'format string'), + 'type' => new external_value(PARAM_PLUGIN, 'The calendar type', VALUE_DEFAULT), + 'fixday' => new external_value(PARAM_INT, 'Remove leading zero for day', VALUE_DEFAULT, 1), + 'fixhour' => new external_value(PARAM_INT, 'Remove leading zero for hour', VALUE_DEFAULT, 1), ] ) ) @@ -264,7 +267,12 @@ class core_external extends external_api { self::validate_context($context); $formatteddates = array_map(function($timestamp) { - return userdate($timestamp['timestamp'], $timestamp['format']); + + $calendartype = $timestamp['type']; + $fixday = !empty($timestamp['fixday']); + $fixhour = !empty($timestamp['fixhour']); + $calendar = \core_calendar\type_factory::get_calendar_instance($calendartype); + return $calendar->timestamp_to_date_string($timestamp['timestamp'], $timestamp['format'], 99, $fixday, $fixhour); }, $params['timestamps']); return ['dates' => $formatteddates]; @@ -273,7 +281,7 @@ class core_external extends external_api { /** * Returns description of get_user_dates() result value * - * @return array + * @return external_description */ public static function get_user_dates_returns() { return new external_single_structure( @@ -333,7 +341,7 @@ class core_external extends external_api { /** * Returns description of get_component_strings() result value * - * @return array + * @return external_description * @since Moodle 2.4 */ public static function get_component_strings_returns() { @@ -421,7 +429,7 @@ class core_external extends external_api { /** * Returns description of get_fragment() result value * - * @return array + * @return external_description * @since Moodle 3.1 */ public static function get_fragment_returns() { diff --git a/lib/external/tests/external_test.php b/lib/external/tests/external_test.php index 6277bc7d23f..bf43f49b87f 100644 --- a/lib/external/tests/external_test.php +++ b/lib/external/tests/external_test.php @@ -203,7 +203,6 @@ class core_external_testcase extends externallib_advanced_testcase { } public function test_get_user_dates() { - global $USER, $CFG, $DB; $this->resetAfterTest(); $this->setAdminUser(); @@ -222,6 +221,11 @@ class core_external_testcase extends externallib_advanced_testcase { 'timestamp' => 1293876000, 'format' => '%d %m %Y' ], + [ + 'timestamp' => 1293876000, + 'format' => '%d %m %Y', + 'type' => 'gregorian' + ], [ 'timestamp' => 1293876000, 'format' => 'some invalid format' @@ -233,6 +237,7 @@ class core_external_testcase extends externallib_advanced_testcase { $this->assertEquals('Saturday, 1 January 2011, 6:00', $result['dates'][0]); $this->assertEquals('1 01 2011', $result['dates'][1]); - $this->assertEquals('some invalid format', $result['dates'][2]); + $this->assertEquals('1 01 2011', $result['dates'][2]); + $this->assertEquals('some invalid format', $result['dates'][3]); } } diff --git a/lib/templates/time_element.mustache b/lib/templates/time_element.mustache new file mode 100644 index 00000000000..f2254484755 --- /dev/null +++ b/lib/templates/time_element.mustache @@ -0,0 +1,92 @@ +{{! + 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 . +}} +{{! + @template core/time_element + + Template to display an HTML time element. + + Classes required for JS: + * none + + Data attributes required for JS: + * data-timestamp Number - The timestamp for the element. + * data-datetimeformat String - A valid format for the datetime attribute. + + Context variables required for this template: + * timestamp Number - The timestamp for the element. + * userdateformat String - The user-facing date format + * datetimeformat String - A valid format for the datetime attribute. Defaults to the ISO-8601 format of '%Y-%m-%dT%H:%M%z'. + Example context (json): + { + "timestamp": 0, + "userdateformat": "%d %b %Y", + "datetimeformat": "%Y-%m-%dT%H:%M%z" + } +}} + +{{#js}} + /** Fetches the formatted date/time for the time element's datetime attribute. */ + require(['core/user_date'], function(UserDate) { + var root = document.getElementById('time-{{$elementid}}{{uniqid}}{{/elementid}}'); + // Fetch value for the datetime attribute using core/user_date, if it's not available. + if (!root.getAttribute('datetime')) { + var dateTimeFormat = root.getAttribute('data-datetimeformat'); + var timestamp = root.getAttribute('data-timestamp'); + + if (!dateTimeFormat.match(/%(?![YmdHMSzZ])./g)) { + var zeroPad = function(nNum, nPad) { + return ((Math.pow(10, nPad) + nNum) + '').slice(1); + }; + + var date = new Date(timestamp * 1000); + + var datetime = dateTimeFormat.replace(/%./g, function(sMatch) { + return (({ + '%Y': date.getFullYear(), + '%m': zeroPad(date.getMonth() + 1, 2), + '%d': zeroPad(date.getDate(), 2), + '%H': zeroPad(date.getHours(), 2), + '%M': zeroPad(date.getMinutes(), 2), + '%S': zeroPad(date.getSeconds(), 2), + '%z': date.toTimeString().replace(/.+GMT([+-]\d+).+/, '$1'), + '%Z': date.toTimeString().replace(/.+\((.+?)\)$/, '$1') + }[sMatch] || '') + '') || sMatch; + }); + root.setAttribute('datetime', datetime); + } else { + // Otherwise, use core/user_date. + var timestamps = [{ + timestamp: timestamp, + format: dateTimeFormat, + type: 'gregorian', + fixday: 0, + fixhour: 0 + }]; + UserDate.get(timestamps).done(function(dates) { + var datetime = dates.pop(); + root.setAttribute('datetime', datetime); + }); + } + } + }); +{{/js}} diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 9cc65ffe135..db9c6a00605 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -63,6 +63,10 @@ information provided here is intended especially for developers. * The following functions have been updated to support passing in an array of group IDs (but still support passing in a single ID): * groups_get_members_join() * groups_get_members_ids_sql() +* Additional parameters were added to core_get_user_dates: + - type: specifies the calendar type. Optional, defaults to Gregorian. + - fixday: Whether to remove leading zero for day. Optional, defaults to 1. + - fixhour: Whether to remove leading zero for hour. Optional, defaults to 1. === 3.8 === * Add CLI option to notify all cron tasks to stop: admin/cli/cron.php --stop diff --git a/mod/forum/classes/local/exporters/post.php b/mod/forum/classes/local/exporters/post.php index 8faf0f90046..a1919f62939 100644 --- a/mod/forum/classes/local/exporters/post.php +++ b/mod/forum/classes/local/exporters/post.php @@ -638,9 +638,8 @@ class post extends exporter { private function get_author_subheading_html(stdClass $exportedauthor, int $timecreated) : string { $fullname = $exportedauthor->fullname; $profileurl = $exportedauthor->urls['profile'] ?? null; - $formatteddate = userdate($timecreated, get_string('strftimedaydatetime', 'core_langconfig')); $name = $profileurl ? "{$fullname}" : $fullname; - $date = ""; + $date = userdate_htmltime($timecreated, get_string('strftimedaydatetime', 'core_langconfig')); return get_string('bynameondate', 'mod_forum', ['name' => $name, 'date' => $date]); } } diff --git a/mod/forum/templates/discussion_list.mustache b/mod/forum/templates/discussion_list.mustache index 50cc04dc34a..da67f2044a8 100644 --- a/mod/forum/templates/discussion_list.mustache +++ b/mod/forum/templates/discussion_list.mustache @@ -257,7 +257,11 @@
{{fullname}}
- {{#userdate}}{{discussion.times.created}}, {{#str}}strftimedatemonthabbr, langconfig{{/str}}{{/userdate}} + {{< core/time_element }} + {{$elementid}}created-{{discussion.id}}{{/elementid}} + {{$timestampval}}{{discussion.times.created}}{{/timestampval}} + {{$userdateformatval}}{{#str}}strftimedatemonthabbr, langconfig{{/str}}{{/userdateformatval}} + {{/core/time_element}}
@@ -277,7 +281,11 @@ diff --git a/mod/forum/templates/forum_discussion_nested_v2_first_post.mustache b/mod/forum/templates/forum_discussion_nested_v2_first_post.mustache index 32b44d0905a..f138cd2026b 100644 --- a/mod/forum/templates/forum_discussion_nested_v2_first_post.mustache +++ b/mod/forum/templates/forum_discussion_nested_v2_first_post.mustache @@ -77,7 +77,7 @@
{{^isdeleted}}
-
+
{{#author}}

{{#parentauthorname}} @@ -105,10 +105,13 @@ {{/parentauthorname}}

{{/author}} - -
+ {{< core/time_element }} + {{$elementid}}created-{{id}}-{{uniqid}}{{/elementid}} + {{$elementclass}}text-muted{{/elementclass}} + {{$timestampval}}{{timecreated}}{{/timestampval}} + {{$userdateformatval}}{{#str}} strftimerecentfull, core_langconfig {{/str}}{{/userdateformatval}} + {{/core/time_element}} +
{{#author.groups}} diff --git a/mod/forum/templates/forum_discussion_post.mustache b/mod/forum/templates/forum_discussion_post.mustache index dad785c7538..868c1e8c9de 100644 --- a/mod/forum/templates/forum_discussion_post.mustache +++ b/mod/forum/templates/forum_discussion_post.mustache @@ -76,14 +76,16 @@ }}>{{$subject}}{{{subject}}}{{/subject}} {{/subjectheading}} {{^isdeleted}} -
+
{{#html.authorsubheading}}{{{.}}}{{/html.authorsubheading}} {{^html.authorsubheading}} - + {{< core/time_element }} + {{$elementid}}created-{{id}}-{{uniqid}}{{/elementid}} + {{$timestampval}}{{timecreated}}{{/timestampval}} + {{$userdateformatval}}{{#str}} strftimedaydatetime, core_langconfig {{/str}}{{/userdateformatval}} + {{/core/time_element}} {{/html.authorsubheading}} -
+
{{/isdeleted}} {{#isprivatereply}}
diff --git a/mod/forum/templates/forum_discussion_threaded_post.mustache b/mod/forum/templates/forum_discussion_threaded_post.mustache index fd4c0346420..a4819dd8cd5 100644 --- a/mod/forum/templates/forum_discussion_threaded_post.mustache +++ b/mod/forum/templates/forum_discussion_threaded_post.mustache @@ -38,9 +38,7 @@ > {{subject}} {{^isdeleted}} -
- {{{html.authorsubheading}}} -
+ {{{html.authorsubheading}}} {{/isdeleted}}
diff --git a/mod/forum/tests/generator/lib.php b/mod/forum/tests/generator/lib.php index 303c1958a78..bb071a02477 100644 --- a/mod/forum/tests/generator/lib.php +++ b/mod/forum/tests/generator/lib.php @@ -371,9 +371,8 @@ class mod_forum_generator extends testing_module_generator { public function get_author_subheading_html(stdClass $exportedauthor, int $timecreated) : string { $fullname = $exportedauthor->fullname; $profileurl = $exportedauthor->urls['profile'] ?? null; - $formatteddate = userdate($timecreated, get_string('strftimedaydatetime', 'core_langconfig')); $name = $profileurl ? "{$fullname}" : $fullname; - $date = ""; + $date = userdate_htmltime($timecreated, get_string('strftimedaydatetime', 'core_langconfig')); return get_string('bynameondate', 'mod_forum', ['name' => $name, 'date' => $date]); } } diff --git a/mod/forum/view.php b/mod/forum/view.php index 5c9f43edae7..9514e74ba92 100644 --- a/mod/forum/view.php +++ b/mod/forum/view.php @@ -108,7 +108,7 @@ $PAGE->set_heading($course->fullname); $PAGE->set_button(forum_search_form($course, $search)); if ($istypesingle && $displaymode == FORUM_MODE_NESTED_V2) { - $PAGE->add_body_class('reset-style'); + $PAGE->add_body_class('nested-v2-display-mode reset-style'); $settingstrigger = $OUTPUT->render_from_template('mod_forum/settings_drawer_trigger', null); $PAGE->add_header_action($settingstrigger); } diff --git a/theme/boost/scss/moodle/undo.scss b/theme/boost/scss/moodle/undo.scss index 5e6e4b3a45b..0d2408fbf1a 100644 --- a/theme/boost/scss/moodle/undo.scss +++ b/theme/boost/scss/moodle/undo.scss @@ -236,7 +236,7 @@ $allow-reset-style: true !default; vertical-align: top; div[role="main"] { - flex: 1; + flex: 1 0 auto; } .activity-navigation { diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 48c6ea97036..aa6c0dbe509 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -17737,7 +17737,7 @@ body.reset-style #page-content { padding-right: 1.25rem; vertical-align: top; } body.reset-style #page-content #region-main-box #region-main div[role="main"] { - flex: 1; } + flex: 1 0 auto; } body.reset-style #page-content #region-main-box #region-main .activity-navigation { overflow: hidden; } body.reset-style #page-content #region-main-box #region-main.has-blocks {