mirror of https://github.com/keeweb/keeweb.git
Merge branch 'release-1.12'
This commit is contained in:
commit
5d5913233b
|
@ -12,7 +12,7 @@ Timeline: [Release Notes](release-notes.md), [TODO](https://github.com/keeweb/ke
|
||||||
On one page: [Features](https://keeweb.info/#features), [FAQ](https://github.com/keeweb/keeweb/wiki/FAQ)
|
On one page: [Features](https://keeweb.info/#features), [FAQ](https://github.com/keeweb/keeweb/wiki/FAQ)
|
||||||
Website: [keeweb.info](https://keeweb.info)
|
Website: [keeweb.info](https://keeweb.info)
|
||||||
Twitter: [kee_web](https://twitter.com/kee_web)
|
Twitter: [kee_web](https://twitter.com/kee_web)
|
||||||
Donate: [OpenCollective](https://opencollective.com/keeweb#support)
|
Donate: [OpenCollective](https://opencollective.com/keeweb#support), [GitHub](https://github.com/sponsors/antelle)
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
|
|
||||||
|
@ -71,6 +71,8 @@ KeeWeb is not free to develop. It takes time, requires paid code signing certifi
|
||||||
You can help the project or say "thank you" with this button:
|
You can help the project or say "thank you" with this button:
|
||||||
[<img src="https://opencollective.com/keeweb/tiers/backer.svg?avatarHeight=42&width=880" alt="OpenCollective">](https://opencollective.com/keeweb#support)
|
[<img src="https://opencollective.com/keeweb/tiers/backer.svg?avatarHeight=42&width=880" alt="OpenCollective">](https://opencollective.com/keeweb#support)
|
||||||
|
|
||||||
|
You can also sponsor the developer directly [on GitHub](https://github.com/sponsors/antelle).
|
||||||
|
|
||||||
Please note: donation does not imply any type of service contract.
|
Please note: donation does not imply any type of service contract.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { EntryCollection } from 'collections/entry-collection';
|
import { SearchResultCollection } from 'collections/search-result-collection';
|
||||||
import { Ranking } from 'util/data/ranking';
|
import { Ranking } from 'util/data/ranking';
|
||||||
|
|
||||||
const urlPartsRegex = /^(\w+:\/\/)?(?:(?:www|wwws|secure)\.)?([^\/]+)\/?(.*)/;
|
const urlPartsRegex = /^(\w+:\/\/)?(?:(?:www|wwws|secure)\.)?([^\/]+)\/?(.*)/;
|
||||||
|
@ -25,7 +25,7 @@ AutoTypeFilter.prototype.getEntries = function() {
|
||||||
x[1] === y[1] ? x[0].title.localeCompare(y[0].title) : y[1] - x[1]
|
x[1] === y[1] ? x[0].title.localeCompare(y[0].title) : y[1] - x[1]
|
||||||
);
|
);
|
||||||
entries = entries.map(p => p[0]);
|
entries = entries.map(p => p[0]);
|
||||||
return new EntryCollection(entries, { comparator: 'none' });
|
return new SearchResultCollection(entries, { comparator: 'none' });
|
||||||
};
|
};
|
||||||
|
|
||||||
AutoTypeFilter.prototype.hasWindowInfo = function() {
|
AutoTypeFilter.prototype.hasWindowInfo = function() {
|
||||||
|
|
|
@ -1,52 +1,8 @@
|
||||||
import { Collection } from 'framework/collection';
|
import { Collection } from 'framework/collection';
|
||||||
import { EntryModel } from 'models/entry-model';
|
import { EntryModel } from 'models/entry-model';
|
||||||
import { Comparators } from 'util/data/comparators';
|
|
||||||
|
|
||||||
class EntryCollection extends Collection {
|
class EntryCollection extends Collection {
|
||||||
static model = EntryModel;
|
static model = EntryModel;
|
||||||
|
|
||||||
comparators = {
|
|
||||||
'none': null,
|
|
||||||
'title': Comparators.stringComparator('title', true),
|
|
||||||
'-title': Comparators.stringComparator('title', false),
|
|
||||||
'website': Comparators.stringComparator('url', true),
|
|
||||||
'-website': Comparators.stringComparator('url', false),
|
|
||||||
'user': Comparators.stringComparator('user', true),
|
|
||||||
'-user': Comparators.stringComparator('user', false),
|
|
||||||
'created': Comparators.dateComparator('created', true),
|
|
||||||
'-created': Comparators.dateComparator('created', false),
|
|
||||||
'updated': Comparators.dateComparator('updated', true),
|
|
||||||
'-updated': Comparators.dateComparator('updated', false),
|
|
||||||
'-attachments': (x, y) => {
|
|
||||||
return this.attachmentSortVal(x).localeCompare(this.attachmentSortVal(y));
|
|
||||||
},
|
|
||||||
'-rank': Comparators.rankComparator().bind(this)
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultComparator = 'title';
|
|
||||||
|
|
||||||
entryFilter = null;
|
|
||||||
|
|
||||||
constructor(models, options) {
|
|
||||||
super(models);
|
|
||||||
const comparatorName = (options && options.comparator) || this.defaultComparator;
|
|
||||||
this.comparator = this.comparators[comparatorName];
|
|
||||||
}
|
|
||||||
|
|
||||||
sortEntries(comparator, filter) {
|
|
||||||
this.entryFilter = filter;
|
|
||||||
this.comparator = this.comparators[comparator] || this.comparators[this.defaultComparator];
|
|
||||||
this.sort();
|
|
||||||
}
|
|
||||||
|
|
||||||
attachmentSortVal(entry) {
|
|
||||||
const att = entry.attachments;
|
|
||||||
let str = att.length ? String.fromCharCode(64 + att.length) : 'Z';
|
|
||||||
if (att[0]) {
|
|
||||||
str += att[0].title;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { EntryCollection };
|
export { EntryCollection };
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { Collection } from 'framework/collection';
|
||||||
|
import { Model } from 'framework/model';
|
||||||
|
import { Comparators } from 'util/data/comparators';
|
||||||
|
|
||||||
|
class SearchResultCollection extends Collection {
|
||||||
|
static model = Model;
|
||||||
|
|
||||||
|
comparators = {
|
||||||
|
'none': null,
|
||||||
|
'title': Comparators.stringComparator('title', true),
|
||||||
|
'-title': Comparators.stringComparator('title', false),
|
||||||
|
'website': Comparators.stringComparator('url', true),
|
||||||
|
'-website': Comparators.stringComparator('url', false),
|
||||||
|
'user': Comparators.stringComparator('user', true),
|
||||||
|
'-user': Comparators.stringComparator('user', false),
|
||||||
|
'created': Comparators.dateComparator('created', true),
|
||||||
|
'-created': Comparators.dateComparator('created', false),
|
||||||
|
'updated': Comparators.dateComparator('updated', true),
|
||||||
|
'-updated': Comparators.dateComparator('updated', false),
|
||||||
|
'-attachments': (x, y) => {
|
||||||
|
return this.attachmentSortVal(x).localeCompare(this.attachmentSortVal(y));
|
||||||
|
},
|
||||||
|
'-rank': Comparators.rankComparator().bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultComparator = 'title';
|
||||||
|
|
||||||
|
entryFilter = null;
|
||||||
|
|
||||||
|
constructor(models, options) {
|
||||||
|
super(models);
|
||||||
|
const comparatorName = (options && options.comparator) || this.defaultComparator;
|
||||||
|
this.comparator = this.comparators[comparatorName];
|
||||||
|
}
|
||||||
|
|
||||||
|
sortEntries(comparator, filter) {
|
||||||
|
this.entryFilter = filter;
|
||||||
|
this.comparator = this.comparators[comparator] || this.comparators[this.defaultComparator];
|
||||||
|
this.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentSortVal(entry) {
|
||||||
|
const att = entry.attachments;
|
||||||
|
let str = att.length ? String.fromCharCode(64 + att.length) : 'Z';
|
||||||
|
if (att[0]) {
|
||||||
|
str += att[0].title;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { SearchResultCollection };
|
|
@ -196,7 +196,7 @@ const Launcher = {
|
||||||
resolveProxy(url, callback) {
|
resolveProxy(url, callback) {
|
||||||
const window = this.getMainWindow();
|
const window = this.getMainWindow();
|
||||||
const session = window.webContents.session;
|
const session = window.webContents.session;
|
||||||
session.resolveProxy(url, proxy => {
|
session.resolveProxy(url).then(proxy => {
|
||||||
const match = /^proxy\s+([\w\.]+):(\d+)+\s*/i.exec(proxy);
|
const match = /^proxy\s+([\w\.]+):(\d+)+\s*/i.exec(proxy);
|
||||||
proxy = match && match[1] ? { host: match[1], port: +match[2] } : null;
|
proxy = match && match[1] ? { host: match[1], port: +match[2] } : null;
|
||||||
callback(proxy);
|
callback(proxy);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Events } from 'framework/events';
|
import { Events } from 'framework/events';
|
||||||
import { AutoType } from 'auto-type';
|
import { AutoType } from 'auto-type';
|
||||||
import { Storage } from 'storage';
|
import { Storage } from 'storage';
|
||||||
import { EntryCollection } from 'collections/entry-collection';
|
import { SearchResultCollection } from 'collections/search-result-collection';
|
||||||
import { FileCollection } from 'collections/file-collection';
|
import { FileCollection } from 'collections/file-collection';
|
||||||
import { FileInfoCollection } from 'collections/file-info-collection';
|
import { FileInfoCollection } from 'collections/file-info-collection';
|
||||||
import { RuntimeInfo } from 'comp/app/runtime-info';
|
import { RuntimeInfo } from 'comp/app/runtime-info';
|
||||||
|
@ -307,7 +307,7 @@ class AppModel {
|
||||||
|
|
||||||
getEntriesByFilter(filter) {
|
getEntriesByFilter(filter) {
|
||||||
const preparedFilter = this.prepareFilter(filter);
|
const preparedFilter = this.prepareFilter(filter);
|
||||||
const entries = new EntryCollection();
|
const entries = new SearchResultCollection();
|
||||||
this.files.forEach(file => {
|
this.files.forEach(file => {
|
||||||
file.forEachEntry(preparedFilter, entry => entries.push(entry));
|
file.forEachEntry(preparedFilter, entry => entries.push(entry));
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
import { Model } from 'framework/model';
|
import { Model } from 'framework/model';
|
||||||
|
import { pick } from 'util/fn';
|
||||||
|
|
||||||
class FileInfoModel extends Model {
|
const DefaultProperties = {
|
||||||
constructor(data) {
|
|
||||||
data = { ...data };
|
|
||||||
for (const [key, val] of Object.entries(data)) {
|
|
||||||
if (/Date$/.test(key)) {
|
|
||||||
data[key] = val ? new Date(val) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileInfoModel.defineModelProperties({
|
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
storage: null,
|
storage: null,
|
||||||
|
@ -28,6 +17,20 @@ FileInfoModel.defineModelProperties({
|
||||||
opts: null,
|
opts: null,
|
||||||
backup: null,
|
backup: null,
|
||||||
fingerprint: null
|
fingerprint: null
|
||||||
});
|
};
|
||||||
|
|
||||||
|
class FileInfoModel extends Model {
|
||||||
|
constructor(data) {
|
||||||
|
data = pick({ ...data }, Object.keys(DefaultProperties));
|
||||||
|
for (const [key, val] of Object.entries(data)) {
|
||||||
|
if (/Date$/.test(key)) {
|
||||||
|
data[key] = val ? new Date(val) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInfoModel.defineModelProperties(DefaultProperties);
|
||||||
|
|
||||||
export { FileInfoModel };
|
export { FileInfoModel };
|
||||||
|
|
|
@ -193,7 +193,7 @@ class AutoTypeSelectView extends View {
|
||||||
|
|
||||||
itemClicked(e) {
|
itemClicked(e) {
|
||||||
const itemEl = $(e.target).closest('.at-select__item');
|
const itemEl = $(e.target).closest('.at-select__item');
|
||||||
const optionsClicked = $(e.target).closest('.at-select__item-options');
|
const optionsClicked = $(e.target).closest('.at-select__item-options').length;
|
||||||
|
|
||||||
if (optionsClicked) {
|
if (optionsClicked) {
|
||||||
this.showItemOptions(itemEl, e);
|
this.showItemOptions(itemEl, e);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { View } from 'framework/views/view';
|
import { View } from 'framework/views/view';
|
||||||
import { Events } from 'framework/events';
|
import { Events } from 'framework/events';
|
||||||
import { EntryCollection } from 'collections/entry-collection';
|
import { SearchResultCollection } from 'collections/search-result-collection';
|
||||||
import { DragDropInfo } from 'comp/app/drag-drop-info';
|
import { DragDropInfo } from 'comp/app/drag-drop-info';
|
||||||
import { Alerts } from 'comp/ui/alerts';
|
import { Alerts } from 'comp/ui/alerts';
|
||||||
import { AppSettingsModel } from 'models/app-settings-model';
|
import { AppSettingsModel } from 'models/app-settings-model';
|
||||||
|
@ -67,7 +67,7 @@ class ListView extends View {
|
||||||
|
|
||||||
this.readTableColumnsEnabled();
|
this.readTableColumnsEnabled();
|
||||||
|
|
||||||
this.items = new EntryCollection();
|
this.items = new SearchResultCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -339,7 +339,7 @@ class SettingsGeneralView extends View {
|
||||||
const storage = Storage[$(e.target).data('storage')];
|
const storage = Storage[$(e.target).data('storage')];
|
||||||
if (storage) {
|
if (storage) {
|
||||||
storage.setEnabled(e.target.checked);
|
storage.setEnabled(e.target.checked);
|
||||||
AppSettingsModel.storage.name = storage.enabled;
|
AppSettingsModel[storage.name] = storage.enabled;
|
||||||
this.$el
|
this.$el
|
||||||
.find('.settings__general-' + storage.name)
|
.find('.settings__general-' + storage.name)
|
||||||
.toggleClass('hide', !e.target.checked);
|
.toggleClass('hide', !e.target.checked);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "KeeWeb",
|
"name": "KeeWeb",
|
||||||
"version": "1.12.1",
|
"version": "1.12.2",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "KeeWeb",
|
"name": "KeeWeb",
|
||||||
"version": "1.12.1",
|
"version": "1.12.2",
|
||||||
"description": "Free cross-platform password manager compatible with KeePass",
|
"description": "Free cross-platform password manager compatible with KeePass",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"homepage": "https://keeweb.info",
|
"homepage": "https://keeweb.info",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "keeweb",
|
"name": "keeweb",
|
||||||
"version": "1.12.1",
|
"version": "1.12.2",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "keeweb",
|
"name": "keeweb",
|
||||||
"version": "1.12.1",
|
"version": "1.12.2",
|
||||||
"description": "Free cross-platform password manager compatible with KeePass",
|
"description": "Free cross-platform password manager compatible with KeePass",
|
||||||
"main": "Gruntfile.js",
|
"main": "Gruntfile.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
Release notes
|
Release notes
|
||||||
-------------
|
-------------
|
||||||
|
##### v1.12.2 (2019-11-03)
|
||||||
|
`-` fixed non-working updater
|
||||||
|
`-` fix #1336: saving disabled storage option
|
||||||
|
`-` fix #1333: item selection in the auto-type pop-up
|
||||||
|
`-` fix #1337: displaying groups in trash
|
||||||
|
|
||||||
##### v1.12.1 (2019-10-27)
|
##### v1.12.1 (2019-10-27)
|
||||||
`-` fix #1324: duplicated shortcut editor in settings
|
`-` fix #1324: duplicated shortcut editor in settings
|
||||||
`-` fix #1313: disabled code signing for macOS dmg
|
`-` fix #1313: disabled code signing for macOS dmg
|
||||||
|
|
Loading…
Reference in New Issue