keeweb/app/scripts/models/app-model.js

719 lines
27 KiB
JavaScript
Raw Normal View History

2015-10-17 23:49:24 +02:00
'use strict';
var Backbone = require('backbone'),
AppSettingsModel = require('./app-settings-model'),
MenuModel = require('./menu/menu-model'),
EntryModel = require('./entry-model'),
2015-10-31 20:09:32 +01:00
GroupModel = require('./group-model'),
2015-10-17 23:49:24 +02:00
FileCollection = require('../collections/file-collection'),
2015-12-06 21:32:41 +01:00
EntryCollection = require('../collections/entry-collection'),
FileInfoCollection = require('../collections/file-info-collection'),
FileModel = require('./file-model'),
FileInfoModel = require('./file-info-model'),
2015-12-06 23:13:29 +01:00
Storage = require('../storage'),
2016-02-06 11:59:57 +01:00
Timeouts = require('../const/timeouts'),
2015-12-12 09:53:50 +01:00
IdGenerator = require('../util/id-generator'),
2016-02-14 08:38:57 +01:00
Logger = require('../util/logger'),
FeatureDetector = require('../util/feature-detector');
2016-01-16 15:19:33 +01:00
require('../mixins/protected-value-ex');
2015-10-17 23:49:24 +02:00
var AppModel = Backbone.Model.extend({
defaults: {},
initialize: function() {
this.tags = [];
this.files = new FileCollection();
2015-12-06 21:32:41 +01:00
this.fileInfos = FileInfoCollection.load();
2015-10-17 23:49:24 +02:00
this.menu = new MenuModel();
this.filter = {};
this.sort = 'title';
this.settings = AppSettingsModel.instance;
this.activeEntryId = null;
2016-02-14 08:38:57 +01:00
this.isBeta = FeatureDetector.isBeta();
2015-10-17 23:49:24 +02:00
this.listenTo(Backbone, 'refresh', this.refresh);
this.listenTo(Backbone, 'set-filter', this.setFilter);
this.listenTo(Backbone, 'add-filter', this.addFilter);
this.listenTo(Backbone, 'set-sort', this.setSort);
2015-11-08 22:25:00 +01:00
this.listenTo(Backbone, 'empty-trash', this.emptyTrash);
2015-12-12 09:53:50 +01:00
this.appLogger = new Logger('app');
2015-10-17 23:49:24 +02:00
},
addFile: function(file) {
2015-12-06 21:32:41 +01:00
if (this.files.getById(file.id)) {
2015-12-02 21:50:31 +01:00
return false;
}
2015-10-17 23:49:24 +02:00
this.files.add(file);
2015-12-02 21:50:31 +01:00
file.get('groups').forEach(function (group) {
this.menu.groupsSection.addItem(group);
}, this);
2015-12-07 20:07:56 +01:00
this._addTags(file);
2015-10-17 23:49:24 +02:00
this._tagsChanged();
this.menu.filesSection.addItem({
icon: 'lock',
title: file.get('name'),
page: 'file',
file: file
});
this.refresh();
2015-12-05 14:04:09 +01:00
this.listenTo(file, 'reload', this.reloadFile);
2015-12-02 21:50:31 +01:00
return true;
2015-10-17 23:49:24 +02:00
},
2015-12-05 14:04:09 +01:00
reloadFile: function(file) {
this.menu.groupsSection.removeByFile(file, true);
file.get('groups').forEach(function (group) {
this.menu.groupsSection.addItem(group);
}, this);
this.updateTags();
},
2015-12-07 20:07:56 +01:00
_addTags: function(file) {
2015-10-17 23:49:24 +02:00
var tagsHash = {};
this.tags.forEach(function(tag) {
tagsHash[tag.toLowerCase()] = true;
});
2015-12-07 20:07:56 +01:00
var that = this;
file.forEachEntry({}, function(entry) {
2015-10-17 23:49:24 +02:00
_.forEach(entry.tags, function(tag) {
if (!tagsHash[tag.toLowerCase()]) {
tagsHash[tag.toLowerCase()] = true;
2015-12-07 20:07:56 +01:00
that.tags.push(tag);
2015-10-17 23:49:24 +02:00
}
2015-12-07 20:07:56 +01:00
});
});
2015-10-17 23:49:24 +02:00
this.tags.sort();
},
_tagsChanged: function() {
if (this.tags.length) {
this.menu.tagsSection.set('scrollable', true);
this.menu.tagsSection.setItems(this.tags.map(function (tag) {
return {title: tag, icon: 'tag', filterKey: 'tag', filterValue: tag};
}));
} else {
this.menu.tagsSection.set('scrollable', false);
this.menu.tagsSection.removeAllItems();
}
},
updateTags: function() {
var oldTags = this.tags.slice();
this.tags.splice(0, this.tags.length);
this.files.forEach(function(file) {
2015-12-07 20:07:56 +01:00
this._addTags(file);
2015-10-17 23:49:24 +02:00
}, this);
if (!_.isEqual(oldTags, this.tags)) {
this._tagsChanged();
}
},
closeAllFiles: function() {
2016-02-06 11:59:57 +01:00
var that = this;
this.files.each(function(file) {
file.close();
that.fileClosed(file);
});
2015-10-17 23:49:24 +02:00
this.files.reset();
this.menu.groupsSection.removeAllItems();
this.menu.tagsSection.set('scrollable', false);
this.menu.tagsSection.removeAllItems();
this.menu.filesSection.removeAllItems();
this.tags.splice(0, this.tags.length);
this.filter = {};
2016-02-23 06:20:01 +01:00
this.refresh();
2015-10-17 23:49:24 +02:00
},
2015-11-07 21:37:54 +01:00
closeFile: function(file) {
2016-02-06 11:59:57 +01:00
file.close();
this.fileClosed(file);
2015-11-07 21:37:54 +01:00
this.files.remove(file);
2015-12-13 12:19:55 +01:00
this.updateTags();
2015-11-07 21:37:54 +01:00
this.menu.groupsSection.removeByFile(file);
this.menu.filesSection.removeByFile(file);
this.refresh();
},
2015-11-08 22:25:00 +01:00
emptyTrash: function() {
this.files.forEach(function(file) {
file.emptyTrash();
}, this);
this.refresh();
},
2015-10-17 23:49:24 +02:00
setFilter: function(filter) {
this.filter = filter;
this.filter.subGroups = this.settings.get('expandGroups');
2015-10-17 23:49:24 +02:00
var entries = this.getEntries();
if (!this.activeEntryId || !entries.get(this.activeEntryId)) {
var firstEntry = entries.first();
this.activeEntryId = firstEntry ? firstEntry.id : null;
}
2015-10-17 23:49:24 +02:00
Backbone.trigger('filter', { filter: this.filter, sort: this.sort, entries: entries });
Backbone.trigger('select-entry', entries.get(this.activeEntryId));
2015-10-17 23:49:24 +02:00
},
refresh: function() {
this.setFilter(this.filter);
},
addFilter: function(filter) {
this.setFilter(_.extend(this.filter, filter));
},
setSort: function(sort) {
this.sort = sort;
this.setFilter(this.filter);
},
getEntries: function() {
var entries = new EntryCollection();
var filter = this.prepareFilter();
this.files.forEach(function(file) {
file.forEachEntry(filter, function(entry) {
entries.push(entry);
});
});
entries.sortEntries(this.sort);
2015-11-08 22:25:00 +01:00
if (this.filter.trash) {
this.addTrashGroups(entries);
}
2015-10-17 23:49:24 +02:00
return entries;
},
2015-11-08 22:25:00 +01:00
addTrashGroups: function(collection) {
this.files.forEach(function(file) {
var trashGroup = file.getTrashGroup();
if (trashGroup) {
trashGroup.getOwnSubGroups().forEach(function(group) {
collection.unshift(GroupModel.fromGroup(group, file, trashGroup));
});
}
});
},
2015-10-17 23:49:24 +02:00
prepareFilter: function() {
var filter = _.clone(this.filter);
if (filter.text) {
filter.textLower = filter.text.toLowerCase();
}
if (filter.tag) {
filter.tagLower = filter.tag.toLowerCase();
}
return filter;
},
2015-10-31 20:09:32 +01:00
getFirstSelectedGroup: function() {
2015-10-17 23:49:24 +02:00
var selGroupId = this.filter.group;
var file, group;
if (selGroupId) {
this.files.forEach(function(f) {
group = f.getGroup(selGroupId);
if (group) {
file = f;
return false;
}
}, this);
}
if (!group) {
file = this.files.first();
group = file.get('groups').first();
}
2015-10-31 20:09:32 +01:00
return { group: group, file: file };
},
2016-02-28 12:16:05 +01:00
completeUserNames: function(part) {
var userNames = {};
this.files.forEach(function(file) {
file.forEachEntry({ text: part, textLower: part.toLowerCase(), advanced: { user: true } }, function(entry) {
var userName = entry.user;
if (userName) {
userNames[userName] = (userNames[userName] || 0) + 1;
}
});
});
var matches = _.pairs(userNames);
matches.sort(function(x, y) { return y[1] - x[1]; });
var maxResults = 5;
if (matches.length > maxResults) {
matches.length = maxResults;
}
return matches.map(function(m) { return m[0]; });
},
2015-10-31 20:09:32 +01:00
createNewEntry: function() {
var sel = this.getFirstSelectedGroup();
return EntryModel.newEntry(sel.group, sel.file);
},
createNewGroup: function() {
var sel = this.getFirstSelectedGroup();
return GroupModel.newGroup(sel.group, sel.file);
2015-12-06 21:32:41 +01:00
},
createDemoFile: function() {
var that = this;
if (!this.files.getByName('Demo')) {
var demoFile = new FileModel();
demoFile.openDemo(function() {
that.addFile(demoFile);
});
return true;
} else {
return false;
}
},
createNewFile: function() {
var name;
for (var i = 0; ; i++) {
name = 'New' + (i || '');
2015-12-10 22:31:47 +01:00
if (!this.files.getByName(name) && !this.fileInfos.getByName(name)) {
2015-12-06 21:32:41 +01:00
break;
}
}
var newFile = new FileModel();
newFile.create(name);
this.addFile(newFile);
},
openFile: function(params, callback) {
2015-12-12 09:53:50 +01:00
var logger = new Logger('open', params.name);
logger.info('File open request');
2015-12-06 21:32:41 +01:00
var that = this;
2015-12-07 20:07:56 +01:00
var fileInfo = params.id ? this.fileInfos.get(params.id) : this.fileInfos.getMatch(params.storage, params.name, params.path);
2016-03-12 21:08:49 +01:00
if (!params.opts && fileInfo && fileInfo.get('opts')) {
params.opts = fileInfo.get('opts');
}
2015-12-08 06:05:57 +01:00
if (fileInfo && fileInfo.get('modified')) {
2015-12-12 09:53:50 +01:00
logger.info('Open file from cache because it is modified');
this.openFileFromCache(params, function(err, file) {
if (!err && file) {
logger.info('Sync just opened modified file');
_.defer(that.syncFile.bind(that, file));
}
callback(err);
}, fileInfo);
2015-12-07 20:07:56 +01:00
} else if (params.fileData) {
2015-12-12 09:53:50 +01:00
logger.info('Open file from supplied content');
2015-12-07 20:07:56 +01:00
this.openFileWithData(params, callback, fileInfo, params.fileData, true);
2015-12-12 16:43:43 +01:00
} else if (!params.storage) {
logger.info('Open file from cache as main storage');
this.openFileFromCache(params, callback, fileInfo);
2015-12-13 15:59:41 +01:00
} else if (fileInfo && fileInfo.get('rev') === params.rev && fileInfo.get('storage') !== 'file') {
2015-12-12 09:53:50 +01:00
logger.info('Open file from cache because it is latest');
2015-12-07 20:07:56 +01:00
this.openFileFromCache(params, callback, fileInfo);
} else if (!fileInfo || params.storage === 'file') {
2015-12-12 09:53:50 +01:00
logger.info('Open file from storage', params.storage);
2015-12-08 20:18:35 +01:00
var storage = Storage[params.storage];
var storageLoad = function() {
2015-12-12 09:53:50 +01:00
logger.info('Load from storage');
2016-03-12 21:08:49 +01:00
storage.load(params.path, params.opts, function(err, data, stat) {
2015-12-08 20:18:35 +01:00
if (err) {
if (fileInfo) {
2015-12-12 09:53:50 +01:00
logger.info('Open file from cache because of storage load error', err);
2015-12-08 20:18:35 +01:00
that.openFileFromCache(params, callback, fileInfo);
} else {
2015-12-12 09:53:50 +01:00
logger.info('Storage load error', err);
2015-12-08 20:18:35 +01:00
callback(err);
}
} else {
2015-12-12 09:53:50 +01:00
logger.info('Open file from content loaded from storage');
2015-12-08 20:18:35 +01:00
params.fileData = data;
params.rev = stat && stat.rev || null;
that.openFileWithData(params, callback, fileInfo, data, true);
}
});
};
var cacheRev = fileInfo && fileInfo.get('rev') || null;
if (cacheRev && storage.stat) {
2015-12-12 09:53:50 +01:00
logger.info('Stat file');
2016-03-12 21:08:49 +01:00
storage.stat(params.path, params.opts, function(err, stat) {
2015-12-08 20:18:35 +01:00
if (fileInfo && (err || stat && stat.rev === cacheRev)) {
2015-12-13 22:18:02 +01:00
logger.info('Open file from cache because ' + (err ? 'stat error' : 'it is latest'), err);
2015-12-07 20:07:56 +01:00
that.openFileFromCache(params, callback, fileInfo);
2015-12-08 20:18:35 +01:00
} else if (stat) {
2016-03-13 09:34:36 +01:00
logger.info('Open file from storage (' + stat.rev + ', local ' + cacheRev + ')');
2015-12-08 20:18:35 +01:00
storageLoad();
2015-12-07 20:07:56 +01:00
} else {
2015-12-12 09:53:50 +01:00
logger.info('Stat error', err);
2015-12-07 20:07:56 +01:00
callback(err);
}
2015-12-08 20:18:35 +01:00
});
} else {
storageLoad();
}
} else {
2016-03-19 12:37:52 +01:00
logger.info('Open file from cache, after load will sync', params.storage);
this.openFileFromCache(params, function(err, file) {
if (!err && file) {
logger.info('Sync just opened file');
_.defer(that.syncFile.bind(that, file));
}
callback(err);
}, fileInfo);
2015-12-07 20:07:56 +01:00
}
},
openFileFromCache: function(params, callback, fileInfo) {
var that = this;
2016-03-12 17:49:52 +01:00
Storage.cache.load(fileInfo.id, null, function(err, data) {
2015-12-12 16:43:43 +01:00
new Logger('open', params.name).info('Loaded file from cache', err);
2015-12-07 20:07:56 +01:00
if (err) {
callback(err);
} else {
that.openFileWithData(params, callback, fileInfo, data);
}
});
},
openFileWithData: function(params, callback, fileInfo, data, updateCacheOnSuccess) {
2015-12-12 09:53:50 +01:00
var logger = new Logger('open', params.name);
2016-04-06 23:13:44 +02:00
if (!params.keyFileData && fileInfo && fileInfo.get('keyFileName') && this.settings.get('rememberKeyFiles')) {
2016-02-14 12:20:21 +01:00
params.keyFileName = fileInfo.get('keyFileName');
params.keyFileData = FileModel.createKeyFileWithHash(fileInfo.get('keyFileHash'));
}
2015-12-06 21:32:41 +01:00
var file = new FileModel({
name: params.name,
storage: params.storage,
path: params.path,
keyFileName: params.keyFileName
});
2015-12-07 20:07:56 +01:00
var that = this;
file.open(params.password, data, params.keyFileData, function(err) {
if (err) {
2015-12-06 21:32:41 +01:00
return callback(err);
}
2015-12-07 20:07:56 +01:00
if (that.files.get(file.id)) {
return callback('Duplicate file id');
}
2015-12-10 22:31:47 +01:00
if (fileInfo && fileInfo.get('modified')) {
if (fileInfo.get('editState')) {
2015-12-12 09:53:50 +01:00
logger.info('Loaded local edit state');
2015-12-10 22:31:47 +01:00
file.setLocalEditState(fileInfo.get('editState'));
}
logger.info('Mark file as modified');
file.set('modified', true);
2015-12-11 21:51:16 +01:00
}
if (fileInfo) {
file.set('syncDate', fileInfo.get('syncDate'));
2015-12-10 22:31:47 +01:00
}
2015-12-07 22:20:18 +01:00
var cacheId = fileInfo && fileInfo.id || IdGenerator.uuid();
2015-12-13 15:59:41 +01:00
file.set('cacheId', cacheId);
2016-02-06 11:59:57 +01:00
if (updateCacheOnSuccess) {
2015-12-12 09:53:50 +01:00
logger.info('Save loaded file to cache');
2016-03-12 17:49:52 +01:00
Storage.cache.save(cacheId, null, params.fileData);
2015-12-07 20:07:56 +01:00
}
2015-12-13 21:03:29 +01:00
var rev = params.rev || fileInfo && fileInfo.get('rev');
2016-03-19 13:43:50 +01:00
that.setFileOpts(file, params.opts);
2015-12-13 21:03:29 +01:00
that.addToLastOpenFiles(file, rev);
2015-12-06 21:32:41 +01:00
that.addFile(file);
2016-02-06 11:59:57 +01:00
that.fileOpened(file);
callback(null, file);
2015-12-06 21:32:41 +01:00
});
},
importFileWithXml: function(params, callback) {
var logger = new Logger('import', params.name);
logger.info('File import request with supplied xml');
2016-03-01 04:29:20 +01:00
var file = new FileModel({
name: params.name,
storage: params.storage,
path: params.path
});
var that = this;
file.importWithXml(params.fileXml, function(err) {
logger.info('Import xml complete ' + (err ? 'with error' : ''), err);
2016-03-01 04:29:20 +01:00
if (err) {
return callback(err);
}
that.addFile(file);
2016-02-06 11:59:57 +01:00
that.fileOpened(file);
2015-12-06 21:32:41 +01:00
});
},
2015-12-13 15:59:41 +01:00
addToLastOpenFiles: function(file, rev) {
2015-12-13 21:03:29 +01:00
this.appLogger.debug('Add last open file', file.get('cacheId'), file.get('name'), file.get('storage'), file.get('path'), rev);
2015-12-07 20:07:56 +01:00
var dt = new Date();
2015-12-06 21:32:41 +01:00
var fileInfo = new FileInfoModel({
2015-12-13 15:59:41 +01:00
id: file.get('cacheId'),
2015-12-06 21:32:41 +01:00
name: file.get('name'),
storage: file.get('storage'),
path: file.get('path'),
2016-03-19 13:43:50 +01:00
opts: this.getStoreOpts(file),
2015-12-06 21:32:41 +01:00
modified: file.get('modified'),
2015-12-10 22:31:47 +01:00
editState: file.getLocalEditState(),
2015-12-07 22:00:44 +01:00
rev: rev,
2015-12-11 21:51:16 +01:00
syncDate: file.get('syncDate') || dt,
2015-12-07 20:07:56 +01:00
openDate: dt
2015-12-06 21:32:41 +01:00
});
2016-02-14 12:20:21 +01:00
if (this.settings.get('rememberKeyFiles')) {
fileInfo.set({
keyFileName: file.get('keyFileName') || null,
keyFileHash: file.getKeyFileHash()
});
}
2015-12-13 15:59:41 +01:00
this.fileInfos.remove(file.get('cacheId'));
2015-12-06 23:13:29 +01:00
this.fileInfos.unshift(fileInfo);
2015-12-06 21:32:41 +01:00
this.fileInfos.save();
},
2016-03-19 13:43:50 +01:00
getStoreOpts: function(file) {
var opts = file.get('opts'), storage = file.get('storage');
if (Storage[storage]&& Storage[storage].fileOptsToStoreOpts && opts) {
return Storage[storage].fileOptsToStoreOpts(opts, file);
}
return null;
},
setFileOpts: function(file, opts) {
var storage = file.get('storage');
if (Storage[storage]&& Storage[storage].storeOptsToFileOpts && opts) {
2016-03-22 18:22:21 +01:00
file.set('opts', Storage[storage].storeOptsToFileOpts(opts, file));
2016-03-19 13:43:50 +01:00
}
},
2016-02-06 11:59:57 +01:00
fileOpened: function(file) {
var that = this;
if (file.get('storage') === 'file') {
Storage.file.watch(file.get('path'), _.debounce(function() {
that.syncFile(file);
}, Timeouts.FileChangeSync));
}
},
fileClosed: function(file) {
if (file.get('storage') === 'file') {
Storage.file.unwatch(file.get('path'));
}
},
2015-12-06 21:32:41 +01:00
removeFileInfo: function(id) {
Storage.cache.remove(id);
this.fileInfos.remove(id);
this.fileInfos.save();
2015-12-08 22:00:31 +01:00
},
2016-02-14 12:20:21 +01:00
getFileInfo: function(file) {
return file.get('cacheId') ? this.fileInfos.get(file.get('cacheId')) :
this.fileInfos.getMatch(file.get('storage'), file.get('name'), file.get('path'));
},
2015-12-08 22:00:31 +01:00
syncFile: function(file, options, callback) {
2015-12-11 21:51:16 +01:00
var that = this;
if (file.get('demo')) {
return callback && callback();
}
2015-12-08 22:00:31 +01:00
if (file.get('syncing')) {
return callback && callback('Sync in progress');
}
if (!options) {
options = {};
2015-12-08 22:00:31 +01:00
}
2015-12-12 09:53:50 +01:00
var logger = new Logger('sync', file.get('name'));
var storage = options.storage || file.get('storage');
var path = options.path || file.get('path');
2016-03-12 21:08:49 +01:00
var opts = options.opts || file.get('opts');
if (storage && Storage[storage].getPathForName && (!path || storage !== file.get('storage'))) {
2015-12-12 16:43:43 +01:00
path = Storage[storage].getPathForName(file.get('name'));
}
2015-12-12 09:53:50 +01:00
logger.info('Sync started', storage, path, options);
2016-02-14 12:20:21 +01:00
var fileInfo = this.getFileInfo(file);
2015-12-10 20:44:02 +01:00
if (!fileInfo) {
2015-12-12 09:53:50 +01:00
logger.info('Create new file info');
2015-12-10 20:44:02 +01:00
var dt = new Date();
fileInfo = new FileInfoModel({
id: IdGenerator.uuid(),
name: file.get('name'),
storage: file.get('storage'),
path: file.get('path'),
2016-03-19 13:43:50 +01:00
opts: this.getStoreOpts(file),
2015-12-10 20:44:02 +01:00
modified: file.get('modified'),
editState: null,
rev: null,
2015-12-11 21:51:16 +01:00
syncDate: dt,
2015-12-10 20:44:02 +01:00
openDate: dt
});
}
2015-12-11 21:51:16 +01:00
file.setSyncProgress();
var complete = function(err, savedToCache) {
if (!err) { savedToCache = true; }
2016-02-06 11:59:57 +01:00
logger.info('Sync finished', err || 'no error');
file.setSyncComplete(path, storage, err ? err.toString() : null, savedToCache);
2015-12-12 16:43:43 +01:00
file.set('cacheId', fileInfo.id);
fileInfo.set({
2015-12-12 16:43:43 +01:00
name: file.get('name'),
storage: storage,
path: path,
2016-03-19 13:59:28 +01:00
opts: that.getStoreOpts(file),
modified: file.get('modified'),
2015-12-11 21:51:16 +01:00
editState: file.getLocalEditState(),
2015-12-12 16:43:43 +01:00
syncDate: file.get('syncDate'),
cacheId: fileInfo.id
});
2016-02-14 12:20:21 +01:00
if (that.settings.get('rememberKeyFiles')) {
fileInfo.set({
keyFileName: file.get('keyFileName') || null,
keyFileHash: file.getKeyFileHash()
});
}
2015-12-11 21:51:16 +01:00
if (!that.fileInfos.get(fileInfo.id)) {
that.fileInfos.unshift(fileInfo);
}
2015-12-11 21:51:16 +01:00
that.fileInfos.save();
if (callback) { callback(err); }
};
2015-12-10 20:44:02 +01:00
if (!storage) {
2015-12-12 16:43:43 +01:00
if (!file.get('modified') && fileInfo.id === file.get('cacheId')) {
2015-12-12 09:53:50 +01:00
logger.info('Local, not modified');
2015-12-10 22:31:47 +01:00
return complete();
2015-12-10 20:44:02 +01:00
}
2015-12-12 09:53:50 +01:00
logger.info('Local, save to cache');
2015-12-10 20:44:02 +01:00
file.getData(function(data, err) {
2015-12-10 22:31:47 +01:00
if (err) { return complete(err); }
2016-03-12 17:49:52 +01:00
Storage.cache.save(fileInfo.id, null, data, function(err) {
2016-02-06 11:59:57 +01:00
logger.info('Saved to cache', err || 'no error');
2015-12-10 22:31:47 +01:00
complete(err);
2015-12-10 20:44:02 +01:00
});
});
} else {
var maxLoadLoops = 3, loadLoops = 0;
var loadFromStorageAndMerge = function() {
if (++loadLoops === maxLoadLoops) {
2015-12-12 16:43:43 +01:00
return complete('Too many load attempts');
2015-12-10 20:44:02 +01:00
}
2015-12-12 16:43:43 +01:00
logger.info('Load from storage, attempt ' + loadLoops);
2016-03-12 21:08:49 +01:00
Storage[storage].load(path, opts, function(err, data, stat) {
2016-02-06 11:59:57 +01:00
logger.info('Load from storage', stat, err || 'no error');
2015-12-10 22:31:47 +01:00
if (err) { return complete(err); }
file.mergeOrUpdate(data, options.remoteKey, function(err) {
2016-02-06 11:59:57 +01:00
logger.info('Merge complete', err || 'no error');
2015-12-12 16:43:43 +01:00
that.refresh();
if (err) {
if (err.code === 'InvalidKey') {
logger.info('Remote key changed, request to enter new key');
Backbone.trigger('remote-key-changed', { file: file });
}
return complete(err);
}
2015-12-10 20:44:02 +01:00
if (stat && stat.rev) {
2015-12-12 16:43:43 +01:00
logger.info('Update rev in file info');
2015-12-10 20:44:02 +01:00
fileInfo.set('rev', stat.rev);
}
2015-12-11 21:51:16 +01:00
file.set('syncDate', new Date());
2015-12-10 20:44:02 +01:00
if (file.get('modified')) {
2015-12-12 09:53:50 +01:00
logger.info('Updated sync date, saving modified file to cache and storage');
2015-12-10 22:31:47 +01:00
saveToCacheAndStorage();
2016-02-06 11:59:57 +01:00
} else if (file.get('dirty')) {
2015-12-12 09:53:50 +01:00
logger.info('Saving not modified dirty file to cache');
2016-03-12 17:49:52 +01:00
Storage.cache.save(fileInfo.id, null, data, function (err) {
2015-12-11 21:51:16 +01:00
if (err) { return complete(err); }
file.set('dirty', false);
2015-12-12 09:53:50 +01:00
logger.info('Complete, remove dirty flag');
2015-12-11 21:51:16 +01:00
complete();
});
2015-12-10 20:44:02 +01:00
} else {
2015-12-12 09:53:50 +01:00
logger.info('Complete, no changes');
2015-12-10 22:31:47 +01:00
complete();
2015-12-10 20:44:02 +01:00
}
});
});
};
2015-12-10 22:31:47 +01:00
var saveToCacheAndStorage = function() {
2015-12-12 09:53:50 +01:00
logger.info('Save to cache and storage');
2015-12-10 20:44:02 +01:00
file.getData(function(data, err) {
2015-12-10 22:31:47 +01:00
if (err) { return complete(err); }
2016-02-06 11:59:57 +01:00
if (!file.get('dirty')) {
logger.info('Save to storage, skip cache because not dirty');
2015-12-10 22:31:47 +01:00
saveToStorage(data);
} else {
2015-12-12 09:53:50 +01:00
logger.info('Saving to cache');
2016-03-12 17:49:52 +01:00
Storage.cache.save(fileInfo.id, null, data, function (err) {
2015-12-10 22:31:47 +01:00
if (err) { return complete(err); }
file.set('dirty', false);
2015-12-12 09:53:50 +01:00
logger.info('Saved to cache, saving to storage');
2015-12-10 22:31:47 +01:00
saveToStorage(data);
});
}
2015-12-10 20:44:02 +01:00
});
};
2015-12-10 22:31:47 +01:00
var saveToStorage = function(data) {
2015-12-12 09:53:50 +01:00
logger.info('Save data to storage');
2016-03-12 21:08:49 +01:00
Storage[storage].save(path, opts, data, function(err, stat) {
2015-12-10 22:31:47 +01:00
if (err && err.revConflict) {
2015-12-12 09:53:50 +01:00
logger.info('Save rev conflict, reloading from storage');
2015-12-10 22:31:47 +01:00
loadFromStorageAndMerge();
} else if (err) {
2015-12-12 09:53:50 +01:00
logger.info('Error saving data to storage');
2015-12-10 22:31:47 +01:00
complete(err);
} else {
2015-12-12 16:43:43 +01:00
if (stat && stat.rev) {
logger.info('Update rev in file info');
fileInfo.set('rev', stat.rev);
}
2016-03-27 20:14:31 +02:00
if (stat && stat.path) {
logger.info('Update path in file info', stat.path);
file.set('path', stat.path);
fileInfo.set('path', stat.path);
path = stat.path;
}
2015-12-11 21:51:16 +01:00
file.set('syncDate', new Date());
2015-12-12 09:53:50 +01:00
logger.info('Save to storage complete, update sync date');
2015-12-10 22:31:47 +01:00
complete();
}
}, fileInfo.get('rev'));
};
2016-02-06 11:59:57 +01:00
logger.info('Stat file');
2016-03-12 21:08:49 +01:00
Storage[storage].stat(path, opts, function (err, stat) {
2016-02-06 11:59:57 +01:00
if (err) {
if (err.notFound) {
logger.info('File does not exist in storage, creating');
saveToCacheAndStorage();
} else if (file.get('dirty')) {
logger.info('Stat error, dirty, save to cache', err || 'no error');
file.getData(function (data) {
if (data) {
2016-03-12 17:49:52 +01:00
Storage.cache.save(fileInfo.id, null, data, function (e) {
2016-02-06 11:59:57 +01:00
if (!e) {
file.set('dirty', false);
}
logger.info('Saved to cache, exit with error', err || 'no error');
complete(err);
});
}
});
2015-12-10 20:44:02 +01:00
} else {
2016-02-06 11:59:57 +01:00
logger.info('Stat error, not dirty', err || 'no error');
complete(err);
2015-12-10 20:44:02 +01:00
}
2016-02-06 11:59:57 +01:00
} else if (stat.rev === fileInfo.get('rev')) {
if (file.get('modified')) {
logger.info('Stat found same version, modified, saving to cache and storage');
saveToCacheAndStorage();
} else {
logger.info('Stat found same version, not modified');
complete();
}
} else {
logger.info('Found new version, loading from storage');
loadFromStorageAndMerge();
}
});
2015-12-10 20:44:02 +01:00
}
2016-02-14 12:20:21 +01:00
},
clearStoredKeyFiles: function() {
this.fileInfos.each(function(fileInfo) {
fileInfo.set({
keyFileName: null,
keyFileHash: null
});
});
this.fileInfos.save();
2015-10-17 23:49:24 +02:00
}
});
module.exports = AppModel;