keeweb/app/scripts/views/settings/settings-file-view.js

395 lines
14 KiB
JavaScript
Raw Normal View History

2015-10-17 23:49:24 +02:00
'use strict';
2015-10-18 16:02:00 +02:00
var Backbone = require('backbone'),
FeatureDetector = require('../../util/feature-detector'),
PasswordGenerator = require('../../util/password-generator'),
Alerts = require('../../comp/alerts'),
Launcher = require('../../comp/launcher'),
2015-12-02 21:39:40 +01:00
Storage = require('../../storage'),
2015-10-23 23:21:21 +02:00
Links = require('../../const/links'),
2015-12-12 09:53:50 +01:00
Format = require('../../util/format'),
2015-12-17 19:25:25 +01:00
Locale = require('../../util/locale'),
2016-03-27 18:38:33 +02:00
UrlUtil = require('../../util/url-util'),
2015-10-24 18:43:30 +02:00
kdbxweb = require('kdbxweb'),
2015-10-18 16:02:00 +02:00
FileSaver = require('filesaver');
2015-10-17 23:49:24 +02:00
2016-03-14 06:04:55 +01:00
var SettingsFileView = Backbone.View.extend({
2015-12-16 22:50:45 +01:00
template: require('templates/settings/settings-file.hbs'),
2015-10-17 23:49:24 +02:00
2015-10-18 16:02:00 +02:00
events: {
2015-12-08 20:18:35 +01:00
'click .settings__file-button-save-default': 'saveDefault',
2016-03-26 09:57:58 +01:00
'click .settings__file-button-save-choose': 'toggleChooser',
2015-11-07 21:37:54 +01:00
'click .settings__file-button-close': 'closeFile',
2016-03-26 09:57:58 +01:00
'click .settings__file-save-to-file': 'saveToFile',
'click .settings__file-save-to-xml': 'saveToXml',
'click .settings__file-save-to-storage': 'saveToStorage',
2015-10-24 18:43:30 +02:00
'change #settings__file-key-file': 'keyFileChange',
2015-12-15 18:42:44 +01:00
'click #settings__file-file-select-link': 'triggerSelectFile',
2015-10-24 18:43:30 +02:00
'change #settings__file-file-select': 'fileSelected',
2015-10-22 22:51:40 +02:00
'focus #settings__file-master-pass': 'focusMasterPass',
'input #settings__file-master-pass': 'changeMasterPass',
2015-10-24 18:43:30 +02:00
'blur #settings__file-master-pass': 'blurMasterPass',
'input #settings__file-name': 'changeName',
'input #settings__file-def-user': 'changeDefUser',
2015-10-24 18:43:30 +02:00
'change #settings__file-trash': 'changeTrash',
'input #settings__file-hist-len': 'changeHistoryLength',
'input #settings__file-hist-size': 'changeHistorySize',
2016-07-03 18:46:43 +02:00
'input #settings__file-key-rounds': 'changeKeyRounds',
'input #settings__file-key-change-force': 'changeKeyChangeForce'
2015-10-18 16:02:00 +02:00
},
2015-12-08 20:18:35 +01:00
appModel: null,
2015-10-23 22:12:12 +02:00
initialize: function() {
this.listenTo(this.model, 'change:syncing change:syncError change:syncDate', this.deferRender);
2015-10-23 22:12:12 +02:00
},
2015-10-17 23:49:24 +02:00
render: function() {
2016-03-26 09:57:58 +01:00
var storageProviders = [];
var fileStorage = this.model.get('storage');
2016-07-17 13:30:38 +02:00
Object.keys(Storage).forEach(name => {
2016-03-26 09:57:58 +01:00
var prv = Storage[name];
if (!prv.system && prv.enabled && name !== fileStorage) {
storageProviders.push(prv);
}
});
2016-07-17 13:30:38 +02:00
storageProviders.sort((x, y) => (x.uipos || Infinity) - (y.uipos || Infinity));
2015-10-18 16:02:00 +02:00
this.renderTemplate({
2015-10-22 22:39:30 +02:00
cmd: FeatureDetector.actionShortcutSymbol(true),
2015-10-25 17:27:34 +01:00
supportFiles: !!Launcher,
2015-10-23 23:21:21 +02:00
desktopLink: Links.Desktop,
2015-10-22 22:39:30 +02:00
name: this.model.get('name'),
2015-10-23 22:12:12 +02:00
path: this.model.get('path'),
2015-10-25 17:27:34 +01:00
storage: this.model.get('storage'),
2015-10-26 22:07:43 +01:00
syncing: this.model.get('syncing'),
2015-12-12 09:53:50 +01:00
syncError: this.model.get('syncError'),
syncDate: Format.dtStr(this.model.get('syncDate')),
password: PasswordGenerator.present(this.model.get('passwordLength')),
2015-10-22 22:39:30 +02:00
defaultUser: this.model.get('defaultUser'),
recycleBinEnabled: this.model.get('recycleBinEnabled'),
historyMaxItems: this.model.get('historyMaxItems'),
historyMaxSize: Math.round(this.model.get('historyMaxSize') / 1024 / 1024),
2016-03-26 09:57:58 +01:00
keyEncryptionRounds: this.model.get('keyEncryptionRounds'),
2016-07-03 18:46:43 +02:00
keyChangeForce: this.model.get('keyChangeForce') > 0 ? this.model.get('keyChangeForce') : null,
2016-03-26 09:57:58 +01:00
storageProviders: storageProviders
2015-10-18 16:02:00 +02:00
});
2015-10-24 18:43:30 +02:00
if (!this.model.get('created')) {
this.$el.find('.settings__file-master-pass-warning').toggle(this.model.get('passwordChanged'));
}
this.renderKeyFileSelect();
},
renderKeyFileSelect: function() {
var keyFileName = this.model.get('keyFileName'),
oldKeyFileName = this.model.get('oldKeyFileName'),
keyFileChanged = this.model.get('keyFileChanged');
var sel = this.$el.find('#settings__file-key-file');
sel.html('');
if (keyFileName && keyFileChanged) {
2015-12-17 19:25:25 +01:00
var text = keyFileName !== 'Generated' ? Locale.setFileUseKeyFile + ' ' + keyFileName : Locale.setFileUseGenKeyFile;
2015-10-24 18:43:30 +02:00
$('<option/>').val('ex').text(text).appendTo(sel);
}
if (oldKeyFileName) {
2015-12-17 19:25:25 +01:00
var useText = keyFileChanged ? Locale.setFileUseOldKeyFile : Locale.setFileUseKeyFile + ' ' + oldKeyFileName;
$('<option/>').val('old').text(useText).appendTo(sel);
2015-10-24 18:43:30 +02:00
}
2015-12-17 19:25:25 +01:00
$('<option/>').val('gen').text(Locale.setFileGenKeyFile).appendTo(sel);
$('<option/>').val('none').text(Locale.setFileDontUseKeyFile).appendTo(sel);
2015-10-24 18:43:30 +02:00
if (keyFileName && keyFileChanged) {
sel.val('ex');
} else if (!keyFileName) {
sel.val('none');
} else if (oldKeyFileName && keyFileName === oldKeyFileName && !keyFileChanged) {
sel.val('old');
}
},
2015-12-08 20:18:35 +01:00
validatePassword: function(continueCallback) {
2015-10-24 18:43:30 +02:00
if (!this.model.get('passwordLength')) {
2015-12-08 19:02:50 +01:00
Alerts.yesno({
2015-12-17 19:25:25 +01:00
header: Locale.setFileEmptyPass,
body: Locale.setFileEmptyPassBody,
2016-07-17 13:30:38 +02:00
success: () => {
2015-12-08 20:18:35 +01:00
continueCallback();
2015-12-08 19:02:50 +01:00
},
2016-07-17 13:30:38 +02:00
cancel: () => {
this.$el.find('#settings__file-master-pass').focus();
2015-12-08 19:02:50 +01:00
}
2015-10-24 18:43:30 +02:00
});
return false;
}
return true;
2015-10-18 16:02:00 +02:00
},
2015-12-08 22:00:31 +01:00
save: function(arg) {
if (!arg) {
arg = {};
}
arg.startedByUser = true;
if (!arg.skipValidation) {
2016-07-17 13:30:38 +02:00
var isValid = this.validatePassword(() => {
2015-12-08 22:00:31 +01:00
arg.skipValidation = true;
2016-07-17 13:30:38 +02:00
this.save(arg);
2015-12-08 22:00:31 +01:00
});
if (!isValid) {
return;
}
}
2015-12-12 16:43:43 +01:00
this.appModel.syncFile(this.model, arg);
2015-12-08 22:00:31 +01:00
},
2015-12-08 20:18:35 +01:00
saveDefault: function() {
2015-12-08 22:00:31 +01:00
this.save();
2015-12-08 20:18:35 +01:00
},
2016-03-26 09:57:58 +01:00
toggleChooser: function() {
this.$el.find('.settings__file-save-choose').toggleClass('hide');
},
2015-12-08 19:02:50 +01:00
saveToFile: function(skipValidation) {
2015-12-08 20:18:35 +01:00
if (skipValidation !== true && !this.validatePassword(this.saveToFile.bind(this, true))) {
2015-10-24 18:43:30 +02:00
return;
}
2015-12-10 22:31:47 +01:00
var fileName = this.model.get('name') + '.kdbx';
if (Launcher && !this.model.get('storage')) {
2016-07-17 13:30:38 +02:00
Launcher.getSaveFileName(fileName, path => {
2015-12-10 22:31:47 +01:00
if (path) {
2016-07-17 13:30:38 +02:00
this.save({storage: 'file', path: path});
2015-12-10 22:31:47 +01:00
}
});
} else {
2016-07-17 13:30:38 +02:00
this.model.getData(data => {
2015-12-10 22:31:47 +01:00
if (Launcher) {
2016-07-17 13:30:38 +02:00
Launcher.getSaveFileName(fileName, path => {
2015-12-10 22:31:47 +01:00
if (path) {
2016-07-17 13:30:38 +02:00
Storage.file.save(path, data, err => {
2015-12-10 22:31:47 +01:00
if (err) {
Alerts.error({
2015-12-17 19:25:25 +01:00
header: Locale.setFileSaveError,
body: Locale.setFileSaveErrorBody + ' ' + path + ': \n' + err
2015-12-10 22:31:47 +01:00
});
}
});
}
});
} else {
var blob = new Blob([data], {type: 'application/octet-stream'});
FileSaver.saveAs(blob, fileName);
}
});
}
2015-10-18 16:02:00 +02:00
},
2016-03-26 09:57:58 +01:00
saveToXml: function() {
2016-07-17 13:30:38 +02:00
this.model.getXml(xml => {
var blob = new Blob([xml], {type: 'text/xml'});
FileSaver.saveAs(blob, this.model.get('name') + '.xml');
2016-07-17 13:30:38 +02:00
});
2015-10-18 16:02:00 +02:00
},
2016-03-26 09:57:58 +01:00
saveToStorage: function(e) {
2016-03-27 18:38:33 +02:00
if (this.model.get('syncing') || this.model.get('demo')) {
2016-03-26 09:57:58 +01:00
return;
}
var storageName = $(e.target).closest('.settings__file-save-to-storage').data('storage');
var storage = Storage[storageName];
if (!storage) {
return;
}
2016-07-17 13:30:38 +02:00
if (this.model.get('storage') === storageName) {
this.save();
2016-03-27 18:38:33 +02:00
} else {
if (!storage.list) {
2016-03-27 20:39:16 +02:00
if (storage.name === 'webdav') {
Alerts.info({
icon: storage.icon,
header: Locale.setFileNoWebDavUpload,
body: Locale.setFileNoWebDavUploadBody
});
} else {
Alerts.notImplemented();
}
return;
2016-03-27 18:38:33 +02:00
}
2016-07-17 13:30:38 +02:00
this.model.set('syncing', true);
storage.list((err, files) => {
this.model.set('syncing', false);
2016-03-27 18:38:33 +02:00
if (err) {
return;
}
2016-07-17 13:30:38 +02:00
var expName = this.model.get('name').toLowerCase();
var existingFile = _.find(files, file => {
2016-03-27 18:38:33 +02:00
return UrlUtil.getDataFileName(file.name).toLowerCase() === expName;
});
if (existingFile) {
Alerts.yesno({
header: Locale.setFileAlreadyExists,
2016-07-17 13:30:38 +02:00
body: Locale.setFileAlreadyExistsBody.replace('{}', this.model.escape('name')),
success: () => {
this.model.set('syncing', true);
storage.remove(existingFile.path, err => {
this.model.set('syncing', false);
2016-03-27 18:38:33 +02:00
if (!err) {
2016-07-17 13:30:38 +02:00
this.save({storage: storageName});
2016-03-27 18:38:33 +02:00
}
});
}
});
} else {
2016-07-17 13:30:38 +02:00
this.save({storage: storageName});
2016-03-27 18:38:33 +02:00
}
});
}
2016-03-26 09:57:58 +01:00
},
2015-11-07 21:37:54 +01:00
closeFile: function() {
if (this.model.get('modified')) {
Alerts.yesno({
2015-12-17 19:25:25 +01:00
header: Locale.setFileUnsaved,
body: Locale.setFileUnsavedBody,
2015-11-07 21:37:54 +01:00
buttons: [
2015-12-17 19:25:25 +01:00
{result: 'close', title: Locale.setFileCloseNoSave, error: true},
{result: '', title: Locale.setFileDontClose}
2015-11-07 21:37:54 +01:00
],
2016-07-17 13:30:38 +02:00
success: result => {
2015-11-07 21:37:54 +01:00
if (result === 'close') {
2016-07-17 13:30:38 +02:00
this.closeFileNoCheck();
2015-11-07 21:37:54 +01:00
}
}
});
} else {
this.closeFileNoCheck();
}
},
closeFileNoCheck: function() {
2015-12-08 20:18:35 +01:00
this.appModel.closeFile(this.model);
2015-11-07 21:37:54 +01:00
},
2015-10-24 18:43:30 +02:00
keyFileChange: function(e) {
2015-10-22 20:03:44 +02:00
switch (e.target.value) {
2015-10-24 18:43:30 +02:00
case 'old':
this.selectOldKeyFile();
2015-10-22 20:03:44 +02:00
break;
case 'gen':
this.generateKeyFile();
break;
2015-10-24 18:43:30 +02:00
case 'none':
2015-10-22 20:03:44 +02:00
this.clearKeyFile();
break;
}
},
2015-10-24 18:43:30 +02:00
selectOldKeyFile: function() {
this.model.resetKeyFile();
this.renderKeyFileSelect();
2015-10-22 20:03:44 +02:00
},
generateKeyFile: function() {
2015-10-24 18:43:30 +02:00
var keyFile = this.model.generateAndSetKeyFile();
var blob = new Blob([keyFile], {type: 'application/octet-stream'});
FileSaver.saveAs(blob, this.model.get('name') + '.key');
this.renderKeyFileSelect();
2015-10-22 20:03:44 +02:00
},
clearKeyFile: function() {
2015-10-24 18:43:30 +02:00
this.model.removeKeyFile();
this.renderKeyFileSelect();
},
triggerSelectFile: function() {
this.$el.find('#settings__file-file-select').click();
},
fileSelected: function(e) {
var file = e.target.files[0];
var reader = new FileReader();
2016-07-17 13:30:38 +02:00
reader.onload = e => {
2015-10-24 18:43:30 +02:00
var res = e.target.result;
this.model.setKeyFile(res, file.name);
this.renderKeyFileSelect();
2016-07-17 13:30:38 +02:00
};
2015-10-24 18:43:30 +02:00
reader.readAsArrayBuffer(file);
2015-10-22 22:39:30 +02:00
},
focusMasterPass: function(e) {
2015-12-12 16:43:43 +01:00
e.target.value = '';
2015-10-23 22:12:12 +02:00
e.target.setAttribute('type', 'text');
2015-10-22 22:51:40 +02:00
},
changeMasterPass: function(e) {
2015-10-22 22:51:40 +02:00
if (!e.target.value) {
2015-10-24 18:43:30 +02:00
this.model.resetPassword();
this.$el.find('.settings__file-master-pass-warning').hide();
2015-10-23 22:12:12 +02:00
} else {
2015-10-24 18:43:30 +02:00
this.model.setPassword(kdbxweb.ProtectedValue.fromString(e.target.value));
if (!this.model.get('created')) {
this.$el.find('.settings__file-master-pass-warning').show();
}
2015-10-22 22:51:40 +02:00
}
},
blurMasterPass: function(e) {
if (!e.target.value) {
this.model.resetPassword();
e.target.value = PasswordGenerator.present(this.model.get('passwordLength'));
this.$el.find('.settings__file-master-pass-warning').hide();
}
2015-10-23 22:12:12 +02:00
e.target.setAttribute('type', 'password');
2015-10-24 18:43:30 +02:00
},
changeName: function(e) {
2015-10-24 18:43:30 +02:00
var value = $.trim(e.target.value);
if (!value) {
return;
}
this.model.setName(value);
},
changeDefUser: function(e) {
2015-10-24 18:43:30 +02:00
var value = $.trim(e.target.value);
this.model.setDefaultUser(value);
},
changeTrash: function(e) {
this.model.setRecycleBinEnabled(e.target.checked);
},
changeHistoryLength: function(e) {
2015-10-24 18:43:30 +02:00
var value = +e.target.value;
if (isNaN(value)) {
e.target.value = this.model.get('historyMaxItems');
return;
}
this.model.setHistoryMaxItems(value);
},
changeHistorySize: function(e) {
2015-10-24 18:43:30 +02:00
var value = +e.target.value;
if (isNaN(value)) {
e.target.value = this.model.get('historyMaxSize') / 1024 / 1024;
return;
}
this.model.setHistoryMaxSize(value * 1024 * 1024);
},
changeKeyRounds: function(e) {
2015-10-24 18:43:30 +02:00
var value = +e.target.value;
if (isNaN(value)) {
e.target.value = this.model.get('keyEncryptionRounds');
return;
}
this.model.setKeyEncryptionRounds(value);
2016-07-03 18:46:43 +02:00
},
changeKeyChangeForce: function(e) {
var value = Math.round(e.target.value);
if (isNaN(value) || value <= 0) {
value = -1;
}
this.model.setKeyChange(true, value);
2015-10-17 23:49:24 +02:00
}
});
2016-03-14 06:04:55 +01:00
module.exports = SettingsFileView;