mirror of https://github.com/keeweb/keeweb.git
fix #57: support custom Dropbox apps and folders
This commit is contained in:
parent
060d4cd3ee
commit
fff01ce091
|
@ -12,8 +12,7 @@ var logger = new Logger('dropbox');
|
|||
|
||||
var DropboxKeys = {
|
||||
AppFolder: 'qp7ctun6qt5n9d6',
|
||||
FullDropbox: 'eor7hvv6u6oslq9',
|
||||
AppFolderKeyParts: ['qp7ctun6', 'qt5n9d6'] // to allow replace key by sed, compare in this way
|
||||
FullDropbox: 'eor7hvv6u6oslq9'
|
||||
};
|
||||
|
||||
var DropboxCustomErrors = {
|
||||
|
@ -128,6 +127,8 @@ var DropboxLink = {
|
|||
ERROR_CONFLICT: Dropbox.ApiError.CONFLICT,
|
||||
ERROR_NOT_FOUND: Dropbox.ApiError.NOT_FOUND,
|
||||
|
||||
Keys: DropboxKeys,
|
||||
|
||||
_getClient: function(complete, overrideAppKey) {
|
||||
if (this._dropboxClient && this._dropboxClient.isAuthenticated()) {
|
||||
complete(null, this._dropboxClient);
|
||||
|
@ -244,11 +245,19 @@ var DropboxLink = {
|
|||
});
|
||||
},
|
||||
|
||||
isValidKey: function() {
|
||||
var isSelfHostedApp = !/^http(s?):\/\/localhost:8085/.test(location.href) &&
|
||||
canUseBuiltInKeys: function() {
|
||||
var isSelfHosted = !/^http(s?):\/\/localhost:8085/.test(location.href) &&
|
||||
!/http(s?):\/\/antelle\.github\.io\/keeweb/.test(location.href) &&
|
||||
!/http(s?):\/\/app\.keeweb\.info/.test(location.href);
|
||||
return Launcher || !isSelfHostedApp || getKey() !== DropboxKeys.AppFolderKeyParts.join('');
|
||||
return !!Launcher || !isSelfHosted;
|
||||
},
|
||||
|
||||
getKey: getKey,
|
||||
|
||||
isValidKey: function() {
|
||||
var key = getKey();
|
||||
var isBuiltIn = key === DropboxKeys.AppFolder || key === DropboxKeys.FullDropbox;
|
||||
return key && key.indexOf(' ') < 0 && (!isBuiltIn || this.canUseBuiltInKeys());
|
||||
},
|
||||
|
||||
authenticate: function(complete, overrideAppKey) {
|
||||
|
|
|
@ -49,6 +49,14 @@ var StorageDropbox = {
|
|||
return path;
|
||||
},
|
||||
|
||||
_fixConfigFolder: function(folder) {
|
||||
folder = folder.replace(/\\/g, '/').trim();
|
||||
if (folder[0] === '/') {
|
||||
folder = folder.substr(1);
|
||||
}
|
||||
return folder;
|
||||
},
|
||||
|
||||
needShowOpenConfig: function() {
|
||||
return !DropboxLink.isValidKey();
|
||||
},
|
||||
|
@ -57,20 +65,46 @@ var StorageDropbox = {
|
|||
return {
|
||||
desc: 'dropboxSetupDesc',
|
||||
fields: [
|
||||
{id: 'key', title: 'dropboxAppKey', desc: 'dropboxAppKeyDesc', type: 'text', required: true, pattern: '\\w{10,}'},
|
||||
{id: 'key', title: 'dropboxAppKey', desc: 'dropboxAppKeyDesc', type: 'text', required: true, pattern: '\\w+'},
|
||||
{id: 'folder', title: 'dropboxFolder', desc: 'dropboxFolderDesc', type: 'text', placeholder: 'dropboxFolderPlaceholder'}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
getSettingsConfig: function() {
|
||||
var fields = [];
|
||||
var appKey = DropboxLink.getKey();
|
||||
var linkField = {id: 'link', title: 'dropboxLink', type: 'select', value: 'custom',
|
||||
options: { app: 'dropboxLinkApp', full: 'dropboxLinkFull', custom: 'dropboxLinkCustom' } };
|
||||
var keyField = {id: 'key', title: 'dropboxAppKey', desc: 'dropboxAppKeyDesc', type: 'text', required: true, pattern: '\\w+',
|
||||
value: appKey};
|
||||
var folderField = {id: 'folder', title: 'dropboxFolder', desc: 'dropboxFolderSettingsDesc', type: 'text',
|
||||
value: AppSettingsModel.instance.get('dropboxFolder') || ''};
|
||||
var canUseBuiltInKeys = DropboxLink.canUseBuiltInKeys();
|
||||
if (canUseBuiltInKeys) {
|
||||
fields.push(linkField);
|
||||
if (appKey === DropboxLink.Keys.AppFolder) {
|
||||
linkField.value = 'app';
|
||||
} else if (appKey === DropboxLink.Keys.FullDropbox) {
|
||||
linkField.value = 'full';
|
||||
fields.push(folderField);
|
||||
} else {
|
||||
fields.push(keyField);
|
||||
fields.push(folderField);
|
||||
}
|
||||
} else {
|
||||
fields.push(keyField);
|
||||
fields.push(folderField);
|
||||
}
|
||||
return { fields: fields };
|
||||
},
|
||||
|
||||
applyConfig: function(config, callback) {
|
||||
var that = this;
|
||||
DropboxLink.authenticate(function(err) {
|
||||
if (!err) {
|
||||
if (config.folder) {
|
||||
config.folder = config.folder.replace(/\\/g, '/').trim();
|
||||
if (config.folder[0] === '/') {
|
||||
config.folder = config.folder.substr(1);
|
||||
}
|
||||
config.folder = that._fixConfigFolder(config.folder);
|
||||
}
|
||||
AppSettingsModel.instance.set({
|
||||
dropboxAppKey: config.key,
|
||||
|
@ -81,6 +115,35 @@ var StorageDropbox = {
|
|||
}, config.key);
|
||||
},
|
||||
|
||||
applySetting: function(key, value) {
|
||||
switch (key) {
|
||||
case 'link':
|
||||
key = 'dropboxAppKey';
|
||||
switch (value) {
|
||||
case 'app':
|
||||
value = DropboxLink.Keys.AppFolder;
|
||||
break;
|
||||
case 'full':
|
||||
value = DropboxLink.Keys.FullDropbox;
|
||||
break;
|
||||
case 'custom':
|
||||
value = '(your app key)';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'key':
|
||||
key = 'dropboxAppKey';
|
||||
break;
|
||||
case 'folder':
|
||||
key = 'dropboxFolder';
|
||||
value = this._fixConfigFolder(value);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
AppSettingsModel.instance.set(key, value);
|
||||
},
|
||||
|
||||
getPathForName: function(fileName) {
|
||||
return '/' + fileName + '.kdbx';
|
||||
},
|
||||
|
|
|
@ -375,7 +375,12 @@ var Locale = {
|
|||
dropboxAppKeyDesc: 'Copy the key from your Dropbox app (Developer settings)',
|
||||
dropboxFolder: 'App folder',
|
||||
dropboxFolderDesc: 'If your app is linked to entire Dropbox (not app folder), set the folder with your Kdbx files here',
|
||||
dropboxFolderSettingsDesc: 'Select any folder in your Dropbox where files will be stored (root folder by default)',
|
||||
dropboxFolderPlaceholder: 'default folder',
|
||||
dropboxLink: 'Link the app to',
|
||||
dropboxLinkApp: 'App folder (Apps/KeeWeb)',
|
||||
dropboxLinkFull: 'Full Dropbox / Select a folder',
|
||||
dropboxLinkCustom: 'Own Dropbox app',
|
||||
|
||||
launcherSave: 'Save Passwords Database',
|
||||
launcherFileFilter: 'KeePass files'
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
SettingsPrvView = require('./settings-prv-view'),
|
||||
Launcher = require('../../comp/launcher'),
|
||||
Updater = require('../../comp/updater'),
|
||||
Format = require('../../util/format'),
|
||||
|
@ -36,6 +37,8 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
'click .settings__general-dev-tools-link': 'openDevTools'
|
||||
},
|
||||
|
||||
views: {},
|
||||
|
||||
allThemes: {
|
||||
fb: 'Flat blue',
|
||||
db: 'Dark brown',
|
||||
|
@ -50,7 +53,8 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
render: function() {
|
||||
var updateReady = UpdateModel.instance.get('updateStatus') === 'ready',
|
||||
updateFound = UpdateModel.instance.get('updateStatus') === 'found',
|
||||
updateManual = UpdateModel.instance.get('updateManual');
|
||||
updateManual = UpdateModel.instance.get('updateManual'),
|
||||
storageProviders = this.getStorageProviders();
|
||||
this.renderTemplate({
|
||||
themes: this.allThemes,
|
||||
activeTheme: AppSettingsModel.instance.get('theme'),
|
||||
|
@ -78,8 +82,23 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
updateManual: updateManual,
|
||||
releaseNotesLink: Links.ReleaseNotes,
|
||||
colorfulIcons: AppSettingsModel.instance.get('colorfulIcons'),
|
||||
storageProviders: this.getStorageProviders()
|
||||
storageProviders: storageProviders
|
||||
});
|
||||
this.renderProviderViews(storageProviders);
|
||||
},
|
||||
|
||||
renderProviderViews: function(storageProviders) {
|
||||
storageProviders.forEach(function(prv) {
|
||||
if (this.views[prv.name]) {
|
||||
this.views[prv.name].remove();
|
||||
}
|
||||
if (prv.hasConfig) {
|
||||
this.views[prv.name] = new SettingsPrvView({
|
||||
el: this.$el.find('.settings__general-' + prv.name),
|
||||
model: prv
|
||||
}).render();
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
getUpdateInfo: function() {
|
||||
|
@ -131,7 +150,8 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
return storageProviders.map(function(sp) {
|
||||
return {
|
||||
name: sp.name,
|
||||
enabled: sp.enabled
|
||||
enabled: sp.enabled,
|
||||
hasConfig: sp.getSettingsConfig
|
||||
};
|
||||
});
|
||||
},
|
||||
|
@ -232,6 +252,7 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
if (storage) {
|
||||
storage.enabled = e.target.checked;
|
||||
AppSettingsModel.instance.set(storage.name, storage.enabled);
|
||||
this.$el.find('.settings__general-' + storage.name).toggleClass('hide', !e.target.checked);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
Storage = require('../../storage');
|
||||
|
||||
var SettingsPrvView = Backbone.View.extend({
|
||||
template: require('templates/settings/settings-prv.hbs'),
|
||||
|
||||
events: {
|
||||
'change .settings__general-prv-field-sel': 'changeField',
|
||||
'input .settings__general-prv-field-txt': 'changeField'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var storage = Storage[this.model.name];
|
||||
if (storage && storage.getSettingsConfig) {
|
||||
this.renderTemplate(storage.getSettingsConfig());
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
changeField: function(e) {
|
||||
var id = e.target.dataset.id,
|
||||
value = e.target.value;
|
||||
if (!e.target.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
var storage = Storage[this.model.name];
|
||||
storage.applySetting(id, value);
|
||||
if ($(e.target).is('select')) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SettingsPrvView;
|
|
@ -88,4 +88,10 @@
|
|||
width: 15em;
|
||||
margin-right: $small-spacing;
|
||||
}
|
||||
&__general-storage-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&__general-prv {
|
||||
margin-bottom: $base-padding-v;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,9 +113,11 @@
|
|||
|
||||
<h2>{{res 'setGenStorage'}}</h2>
|
||||
{{#each storageProviders as |prv|}}
|
||||
<h3><input type="checkbox" id="settings__general-prv-check-{{prv.name}}" class="settings__general-prv-check"
|
||||
data-storage="{{prv.name}}" {{#if prv.enabled}}checked{{/if}}
|
||||
/><label for="settings__general-prv-check-{{prv.name}}">{{res prv.name}}</label></h3>
|
||||
<h4 class="settings__general-storage-header"><input
|
||||
type="checkbox" id="settings__general-prv-check-{{prv.name}}" class="settings__general-prv-check"
|
||||
data-storage="{{prv.name}}" {{#if prv.enabled}}checked{{/if}}
|
||||
/><label for="settings__general-prv-check-{{prv.name}}">{{res prv.name}}</label></h4>
|
||||
<div class="settings__general-prv-wrap settings__general-{{prv.name}} {{#ifeq prv.enabled false}}hide{{/ifeq}}"></div>
|
||||
{{/each}}
|
||||
|
||||
{{#if devTools}}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<div class="settings__general-prv settings__general-prv-{{name}}">
|
||||
{{#if desc}}<div class="settings__general-prv-desc">{{res desc}}</div>{{/if}}
|
||||
<div class="settings__general-prv-fields">
|
||||
{{#each fields as |field ix|}}
|
||||
{{#ifeq type 'select'}}
|
||||
<div>
|
||||
<label for="settings__general-prv-field-sel-{{id}}">{{res title}}:</label>
|
||||
<select
|
||||
class="settings__select input-base settings__general-prv-field settings__general-prv-field-sel"
|
||||
id="settings__general-prv-field-sel-{{id}}"
|
||||
data-id="{{id}}">
|
||||
{{#each options as |title val|}}
|
||||
<option value="{{val}}" {{#ifeq ../value val}}selected{{/ifeq}}>{{res title}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
{{else}}
|
||||
<label for="settings__general-prv-field-txt-{{id}}">{{res title}}:</label>
|
||||
{{#if desc}}<div class="settings__general-prv-field-desc muted-color">{{res desc}}</div>{{/if}}
|
||||
<input type="{{type}}"
|
||||
class="input-base settings__general-prv-field settings__input settings__general-prv-field-txt"
|
||||
id="settings__general-prv-field-txt-{{id}}"
|
||||
autocomplete="off"
|
||||
value="{{value}}"
|
||||
data-id="{{id}}"
|
||||
{{#if placeholder}}placeholder="{{res placeholder}}"{{/if}}
|
||||
{{#if required}}required{{/if}}
|
||||
{{#if pattern}}pattern="{{{pattern}}}"{{/if}}
|
||||
/>
|
||||
{{/ifeq}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
|
@ -22,6 +22,7 @@ Storage providers, usability improvements
|
|||
`+` overall spacing increased
|
||||
`+` hide demo button once opened
|
||||
`+` show error details on open
|
||||
`+` select dropbox folder
|
||||
`-` fix capslock indicator
|
||||
`-` fix file settings input behavior
|
||||
`-` fix favicon download
|
||||
|
|
Loading…
Reference in New Issue