diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 9e73d814d..6a70c2b31 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1780,11 +1780,15 @@ define([ var sframeChan = common.getSframeChannel(); var focus; - var pickerCfg = { + var pickerCfgInit = { types: [type], where: ['template'], hidden: true }; + var pickerCfg = { + types: [type], + where: ['template'], + }; var onConfirm = function (yes) { if (!yes) { if (focus) { focus.focus(); } @@ -1812,7 +1816,7 @@ define([ sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) { if (data) { - common.openFilePicker(pickerCfg); + common.openFilePicker(pickerCfgInit); focus = document.activeElement; if (force) { return void onConfirm(true); } UI.confirm(Messages.useTemplate, onConfirm, { @@ -1968,10 +1972,13 @@ define([ if (!res.data || !Array.isArray(res.data)) { return void console.error("Error: get the templates list"); } - // TODO Sort the templates by number of use? var data = res.data.slice().sort(function (a, b) { - if (a.name === b.name) { return 0; } - return a.name < b.name ? -1 : 1; + if (a.used === b.used) { + // Sort by name + if (a.name === b.name) { return 0; } + return a.name < b.name ? -1 : 1; + } + return b.used - a.used; }).slice(0, 2); data.unshift({ name: Messages.creation_newTemplate, diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 0a9a27f0d..d4adb1ee0 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -424,6 +424,7 @@ define([ // it allows us to add owners and expiration time if it is a new file var parsed = Hash.parsePadUrl(href); if(!parsed) { throw new Error("Cannot get template hash"); } + postMessage("INCREMENT_TEMPLATE_USE", href); Crypt.get(parsed.hash, function (err, val) { if (err) { throw new Error(err); } var p = Hash.parsePadUrl(window.location.href); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index f3f1e1450..0125e6018 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -660,6 +660,15 @@ define([ }); cb(res); }; + Store.incrementTemplateUse = function (href) { + store.userObject.getPadAttribute(href, 'used', function (err, data) { + // This is a not critical function, abort in case of error to make sure we won't + // create any issue with the user object or the async store + if (err) { return; } + var used = typeof data === "number" ? ++data : 1; + store.userObject.setPadAttribute(href, 'used', used); + }); + }; // Pads Store.moveToTrash = function (data, cb) { diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 1043f9dcf..1713d7a2a 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -126,6 +126,9 @@ define([ case 'GET_STRONGER_HASH': { Store.getStrongerHash(data, cb); break; } + case 'INCREMENT_TEMPLATE_USE': { + Store.incrementTemplateUse(data); break; + } // Messaging case 'INVITE_FROM_USERLIST': { Store.inviteFromUserlist(data, cb); break; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index fab9dfcfd..f40614c52 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -431,6 +431,8 @@ define([ // File picker var FP = {}; var initFilePicker = function (cfg) { + // cfg.hidden means pre-loading the filepicker while keeping it hidden. + // if cfg.hidden is true and the iframe already exists, do nothing if (!FP.$iframe) { var config = {}; config.onFilePicked = function (data) { @@ -449,7 +451,7 @@ define([ }; FP.$iframe = $('