From a4355c95c297ae83a05f91a39f181dbcd32c2ee4 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Wed, 24 Mar 2021 16:42:12 +0200 Subject: [PATCH] Add credential sorting --- keepassxc-browser/_locales/en/messages.json | 20 +++++ keepassxc-browser/background/page.js | 5 ++ keepassxc-browser/common/global.js | 7 ++ .../content/keepassxc-browser.js | 73 ++++++++++++++----- keepassxc-browser/manifest.json | 4 +- keepassxc-browser/options/options.html | 12 +++ keepassxc-browser/options/options.js | 8 +- package.json | 2 +- 8 files changed, 110 insertions(+), 21 deletions(-) diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index 9c88dc6..a05f447 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -683,6 +683,10 @@ "message": "Color theme", "description": "Theme selection header text." }, + "optionsCredentialSortSelectionHeader": { + "message": "Sort matching credentials by", + "description": "Credential sort option header text." + }, "optionsThemeSelection": { "message": "Select color theme", "description": "Theme selection title text." @@ -903,6 +907,22 @@ "message": "Disable all features", "description": "Site preferences option selection." }, + "optionsSortByTitle": { + "message": "Title", + "desription": "Sort matching credentials by title option selection." + }, + "optionsSortByUsername": { + "message": "Username", + "desription": "Sort matching credentials by username option selection." + }, + "optionsSortByGroupAndTitle": { + "message": "Group and title", + "desription": "Sort matching credentials by group and title option selection." + }, + "optionsSortByGroupAndUsername": { + "message": "Group and username", + "desription": "Sort matching credentials by group and username option selection." + }, "optionsCustomFieldsNotFound": { "message": "No saved custom login fields found.", "description": "Shown when no saved custom credentials are saved." diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index eeafbf9..96338c9 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -10,6 +10,7 @@ const defaultSettings = { autoSubmit: false, checkUpdateKeePassXC: 3, colorTheme: 'system', + credentialSorting: SORT_BY_GROUP_AND_TITLE, defaultGroup: '', defaultGroupAlwaysAsk: false, redirectAllowance: 1, @@ -82,6 +83,10 @@ page.initSettings = async function() { page.settings.colorTheme = defaultSettings.colorTheme; } + if (!('credentialSorting' in page.settings)) { + page.settings.credentialSorting = defaultSettings.credentialSorting; + } + if (!('defaultGroup' in page.settings)) { page.settings.defaultGroup = defaultSettings.defaultGroup; } diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index bb7f7d7..e8bdf29 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -1,10 +1,17 @@ 'use strict'; +// Site Preferences ignore options const IGNORE_NOTHING = 'ignoreNothing'; const IGNORE_NORMAL = 'ignoreNormal'; const IGNORE_AUTOSUBMIT = 'ignoreAutoSubmit'; const IGNORE_FULL = 'ignoreFull'; +// Credential sorting options +const SORT_BY_TITLE = 'sortByTitle'; +const SORT_BY_USERNAME = 'sortByUsername'; +const SORT_BY_GROUP_AND_TITLE = 'sortByGroupAndTitle'; +const SORT_BY_GROUP_AND_USERNAME = 'sortByGroupAndUsername'; + const schemeSegment = '(\\*|http|https|ws|wss|file|ftp)'; const hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?'; const pathSegment = '(.*)'; diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index 611c000..f0eff41 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -1329,25 +1329,46 @@ kpxc.initCredentialFields = async function() { } }; -// Intializes the login popup list for choosing credentials +// Intializes the login lists for popup and Autocomplete Menu kpxc.initLoginPopup = function() { if (kpxc.credentials.length === 0) { return; } - const getLoginText = function(credential, withGroup) { - const name = credential.name.length < MAX_AUTOCOMPLETE_NAME_LEN + // Returns a login item with additional information for sorting + const getLoginItem = function(credential, withGroup, loginId) { + const title = credential.name.length < MAX_AUTOCOMPLETE_NAME_LEN ? credential.name : credential.name.substr(0, MAX_AUTOCOMPLETE_NAME_LEN) + '…'; const group = (withGroup && credential.group) ? `[${credential.group}] ` : ''; const visibleLogin = (credential.login.length > 0) ? credential.login : tr('credentialsNoUsername'); - const text = `${group}${name} (${visibleLogin})`; + let text = `${group}${title} (${visibleLogin})`; if (credential.expired && credential.expired === 'true') { - return `${text} [${tr('credentialExpired')}]`; + text = `${text} [${tr('credentialExpired')}]`; } - return text; + return { + title: title, + group: group, + visibleLogin: visibleLogin, + login: credential.login, + loginId: loginId, + uuid: credential.uuid, + text: text + }; + }; + + // Sorting with or without group name included + const sortLoginItemBy = function(a, b, name, withGroup = false) { + const firstGroup = a.group.toLowerCase(); + const secondGroup = b.group.toLowerCase(); + const first = a[name].toLowerCase(); + const second = b[name].toLowerCase(); + + return withGroup + ? firstGroup.localeCompare(secondGroup) || first.localeCompare(second) + : first.localeCompare(second); }; const getUniqueGroupCount = function(creds) { @@ -1356,25 +1377,43 @@ kpxc.initLoginPopup = function() { return uniqueGroups.size; }; - // Add usernames + descriptions to autocomplete-list and popup-list - const usernames = []; - kpxcUserAutocomplete.clear(); const showGroupNameInAutocomplete = kpxc.settings.showGroupNameInAutocomplete && (getUniqueGroupCount(kpxc.credentials) > 1); + // Initialize login items + const loginItems = []; for (let i = 0; i < kpxc.credentials.length; i++) { - const loginText = getLoginText(kpxc.credentials[i], showGroupNameInAutocomplete); - usernames.push({ text: loginText, uuid: kpxc.credentials[i].uuid }); + const loginItem = getLoginItem(kpxc.credentials[i], showGroupNameInAutocomplete, i); + loginItems.push(loginItem); + } + + // Sort login items + if (kpxc.settings.credentialSorting === SORT_BY_TITLE) { + loginItems.sort((a, b) => sortLoginItemBy(a, b, 'title')); + } else if (kpxc.settings.credentialSorting === SORT_BY_USERNAME) { + loginItems.sort((a, b) => sortLoginItemBy(a, b, 'visibleLogin')); + } else if (kpxc.settings.credentialSorting === SORT_BY_GROUP_AND_TITLE) { + loginItems.sort((a, b) => sortLoginItemBy(a, b, 'title', true)); + } else if (kpxc.settings.credentialSorting === SORT_BY_GROUP_AND_USERNAME) { + loginItems.sort((a, b) => sortLoginItemBy(a, b, 'visibleLogin', true)); + } + + const popupLoginItems = []; + kpxcUserAutocomplete.clear(); + + // Initialize Popup Login and Autocomplete Menu items + for (const l of loginItems) { + popupLoginItems.push({ text: l.text, uuid: l.uuid }); kpxcUserAutocomplete.elements.push({ - label: loginText, - value: kpxc.credentials[i].login, - uuid: kpxc.credentials[i].uuid, - loginId: i + label: l.text, + value: l.login, + uuid: l.uuid, + loginId: l.loginId }); } - // Generate popup-list of usernames + descriptions - sendMessage('popup_login', usernames); + // Activate Popup Login list of usernames + descriptions + sendMessage('popup_login', popupLoginItems); }; kpxc.passwordFilled = async function() { diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json index 2088d57..d444e96 100755 --- a/keepassxc-browser/manifest.json +++ b/keepassxc-browser/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 2, "name": "KeePassXC-Browser", - "version": "1.7.6", - "version_name": "1.7.6", + "version": "1.7.7", + "version_name": "1.7.7", "description": "__MSG_extensionDescription__", "author": "KeePassXC Team", "icons": { diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index e0a7659..37d812e 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -140,6 +140,18 @@ +
+
+ + +
+
+
diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 7d3f5ac..40c1885 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -73,12 +73,17 @@ options.initGeneralSettings = function() { $('#tab-general-settings select#colorTheme').val(options.settings['colorTheme']); } - $('#tab-general-settings select:first').change(async function() { + $('#tab-general-settings select#colorTheme').change(async function() { options.settings['colorTheme'] = $(this).val(); await options.saveSettings(); location.reload(); }); + $('#tab-general-settings select#credentialSorting').change(async function() { + options.settings['credentialSorting'] = $(this).val(); + await options.saveSettings(); + }); + $('#tab-general-settings input[type=checkbox]').each(function() { $(this).attr('checked', options.settings[$(this).attr('name')]); if ($(this).attr('name') === 'defaultGroupAlwaysAsk' && $(this).attr('checked')) { @@ -115,6 +120,7 @@ options.initGeneralSettings = function() { } }); + $('#tab-general-settings select#credentialSorting').val(options.settings['credentialSorting']); $('#tab-general-settings input#defaultGroup').val(options.settings['defaultGroup']); $('#tab-general-settings input[type=radio]').each(function() { diff --git a/package.json b/package.json index e5330ba..5ae3e5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "KeePassXC-Browser", - "version": "1.7.6", + "version": "1.7.7", "description": "KeePassXC-Browser", "main": "build.js", "devDependencies": {