Merge branch 'soon' into staging
This commit is contained in:
commit
d1eff71584
|
@ -325,9 +325,30 @@ button:not(.btn).primary:hover{
|
|||
};
|
||||
|
||||
var hasErrored = false;
|
||||
var isOffline = false;
|
||||
var updateLoadingProgress = function (data) {
|
||||
if (!built || !data) { return; }
|
||||
|
||||
// If we receive a "offline" event, show the warning text
|
||||
if (data.type === "offline") {
|
||||
try {
|
||||
isOffline = true;
|
||||
Messages.offlineError = "OFFLINE MODE NOT AVAILABLE"; // XXX
|
||||
document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;');
|
||||
document.querySelector('#cp-loading-message').innerText = Messages.offlineError;
|
||||
} catch (e) { console.error(e); }
|
||||
return;
|
||||
}
|
||||
|
||||
// If we receive a new event and we were offline, remove
|
||||
// the offline warning text
|
||||
if (isOffline) {
|
||||
try {
|
||||
isOffline = false;
|
||||
document.querySelector('#cp-loading-message').setAttribute('style', 'display:none;');
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
// Make sure progress doesn't go backward
|
||||
var c = types.indexOf(data.type);
|
||||
if (c < current) { return console.debug(data); }
|
||||
|
|
|
@ -14,6 +14,7 @@ const Util = require("./common-util");
|
|||
module.exports.create = function (config) {
|
||||
|
||||
const Env = {
|
||||
OFFLINE_MODE: false,
|
||||
FRESH_KEY: '',
|
||||
FRESH_MODE: true,
|
||||
DEV_MODE: false,
|
||||
|
@ -117,6 +118,9 @@ module.exports.create = function (config) {
|
|||
//console.log("FRESH MODE ENABLED");
|
||||
Env.FRESH_KEY = +new Date();
|
||||
}
|
||||
|
||||
// Offline mode is mostly for development. It lets us test clientside cache and offline support
|
||||
if (process.env.OFFLINE) { Env.OFFLINE_MODE = true; }
|
||||
}());
|
||||
|
||||
Env.checkCache = function (channel) {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"start": "node server.js",
|
||||
"dev": "DEV=1 node server.js",
|
||||
"fresh": "FRESH=1 node server.js",
|
||||
"offline": "FRESH=1 OFFLINE=1 node server.js",
|
||||
"package": "PACKAGE=1 node server.js",
|
||||
"lint": "jshint --config .jshintrc --exclude-path .jshintignore . && ./node_modules/lesshint/bin/lesshint -c ./.lesshintrc ./customize.dist/src/less2/",
|
||||
"lint:js": "jshint --config .jshintrc --exclude-path .jshintignore .",
|
||||
|
|
|
@ -313,6 +313,7 @@ nThen(function (w) {
|
|||
Env.Log = _log;
|
||||
config.log = _log;
|
||||
|
||||
if (Env.OFFLINE_MODE) { return; }
|
||||
if (config.externalWebsocketURL) { return; }
|
||||
|
||||
require("./lib/api").create(Env);
|
||||
|
|
|
@ -223,6 +223,7 @@ define([
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
UIElements.noContactsMessage = function (common) {
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var data = metadataMgr.getUserData();
|
||||
|
|
|
@ -453,10 +453,35 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
common.getFileSize = function (href, password, cb) {
|
||||
postMessage("GET_FILE_SIZE", {href: href, password: password}, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb(undefined, obj.size);
|
||||
common.getFileSize = function (href, password, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb));
|
||||
var channel = Hash.hrefToHexChannelId(href, password);
|
||||
var error;
|
||||
Nthen(function (waitFor) {
|
||||
// Blobs can't change, if it's in the cache, use it
|
||||
Cache.getBlobCache(channel, waitFor(function(err, blob) {
|
||||
if (err) { return; }
|
||||
waitFor.abort();
|
||||
cb(null, blob.length);
|
||||
}));
|
||||
|
||||
}).nThen(function (waitFor) {
|
||||
// If it's not in the cache or it's not a blob, try to get the value from the server
|
||||
postMessage("GET_FILE_SIZE", {channel:channel}, waitFor(function (obj) {
|
||||
if (obj && obj.error) {
|
||||
// If disconnected, try to get the value from the channel cache (next nThen)
|
||||
error = obj.error;
|
||||
return;
|
||||
}
|
||||
waitFor.abort();
|
||||
cb(undefined, obj.size);
|
||||
}));
|
||||
}).nThen(function () {
|
||||
Cache.getChannelCache(channel, function(err, data) {
|
||||
if (err) { return void cb(error); }
|
||||
var size = data && Array.isArray(data.c) && data.c.join('').length;
|
||||
cb(null, size || 0);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -467,11 +492,37 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
common.isNewChannel = function (href, password, cb) {
|
||||
postMessage('IS_NEW_CHANNEL', {href: href, password: password}, function (obj) {
|
||||
if (obj.error) { return void cb(obj.error); }
|
||||
if (!obj) { return void cb('INVALID_RESPONSE'); }
|
||||
cb(undefined, obj.isNew);
|
||||
// This function is used when we want to open a pad. We first need
|
||||
// to check if it exists. With the cached drive, we need to wait for
|
||||
// the network to be available before we can continue.
|
||||
common.isNewChannel = function (href, password, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb));
|
||||
var channel = Hash.hrefToHexChannelId(href, password);
|
||||
var error;
|
||||
Nthen(function (waitFor) {
|
||||
Cache.getChannelCache(channel, waitFor(function(err, data) {
|
||||
if (err || !data) { return; }
|
||||
waitFor.abort();
|
||||
cb(undefined, false);
|
||||
}));
|
||||
}).nThen(function () {
|
||||
// If it's not in the cache try to get the value from the server
|
||||
var isNew = function () {
|
||||
error = undefined;
|
||||
postMessage('IS_NEW_CHANNEL', {channel: channel}, function (obj) {
|
||||
if (obj && obj.error) { error = obj.error; }
|
||||
if (!obj) { error = "INVALID_RESPONSE"; }
|
||||
|
||||
if (error === "ANON_RPC_NOT_READY") {
|
||||
// Try again in 1s
|
||||
return void setTimeout(isNew, 100);
|
||||
} else if (error) {
|
||||
return void cb(error);
|
||||
}
|
||||
cb(undefined, obj.isNew);
|
||||
}, {timeout: -1});
|
||||
};
|
||||
isNew();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -927,9 +978,11 @@ define([
|
|||
// Get data about a given channel: use with hidden hashes
|
||||
common.getPadDataFromChannel = function (obj, cb) {
|
||||
if (!obj || !obj.channel) { return void cb('EINVAL'); }
|
||||
// Note: no timeout for this command, we may only have loaded the cached drive
|
||||
// and need to wait for the fully synced drive
|
||||
postMessage("GET_PAD_DATA_FROM_CHANNEL", obj, function (data) {
|
||||
cb(void 0, data);
|
||||
});
|
||||
}, {timeout: -1});
|
||||
};
|
||||
|
||||
|
||||
|
@ -1911,6 +1964,64 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
var provideFeedback = function () {
|
||||
if (typeof(window.Proxy) === 'undefined') {
|
||||
Feedback.send("NO_PROXIES");
|
||||
}
|
||||
|
||||
if (!common.isWebRTCSupported()) {
|
||||
Feedback.send("NO_WEBRTC");
|
||||
}
|
||||
|
||||
var shimPattern = /CRYPTPAD_SHIM/;
|
||||
if (shimPattern.test(Array.isArray.toString())) {
|
||||
Feedback.send("NO_ISARRAY");
|
||||
}
|
||||
|
||||
if (shimPattern.test(Array.prototype.fill.toString())) {
|
||||
Feedback.send("NO_ARRAYFILL");
|
||||
}
|
||||
|
||||
if (typeof(Symbol) === 'undefined') {
|
||||
Feedback.send('NO_SYMBOL');
|
||||
}
|
||||
|
||||
if (typeof(SharedWorker) === "undefined") {
|
||||
Feedback.send('NO_SHAREDWORKER');
|
||||
} else {
|
||||
Feedback.send('SHAREDWORKER');
|
||||
}
|
||||
if (typeof(Worker) === "undefined") {
|
||||
Feedback.send('NO_WEBWORKER');
|
||||
}
|
||||
if (!('serviceWorker' in navigator)) {
|
||||
Feedback.send('NO_SERVICEWORKER');
|
||||
}
|
||||
if (!common.hasCSSVariables()) {
|
||||
Feedback.send('NO_CSS_VARIABLES');
|
||||
}
|
||||
|
||||
Feedback.reportScreenDimensions();
|
||||
Feedback.reportLanguage();
|
||||
};
|
||||
var initFeedback = function (feedback) {
|
||||
// Initialize feedback
|
||||
Feedback.init(feedback);
|
||||
provideFeedback();
|
||||
};
|
||||
var onStoreReady = function (data) {
|
||||
if (common.userHash) {
|
||||
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
|
||||
if (localToken === null) {
|
||||
// if that number hasn't been set to localStorage, do so.
|
||||
localStorage.setItem(Constants.tokenKey, data[Constants.tokenKey]);
|
||||
}
|
||||
}
|
||||
|
||||
initFeedback(data.feedback);
|
||||
};
|
||||
|
||||
common.startAccountDeletion = function (data, cb) {
|
||||
// Logout other tabs
|
||||
LocalStore.logout(null, true);
|
||||
|
@ -1957,6 +2068,8 @@ define([
|
|||
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
|
||||
if (localToken !== data.token) { requestLogin(); }
|
||||
},
|
||||
// Store
|
||||
STORE_READY: onStoreReady,
|
||||
// Network
|
||||
NETWORK_DISCONNECT: common.onNetworkDisconnect.fire,
|
||||
NETWORK_RECONNECT: function (data) {
|
||||
|
@ -2038,52 +2151,6 @@ define([
|
|||
return void setTimeout(function () { f(void 0, env); });
|
||||
}
|
||||
|
||||
var provideFeedback = function () {
|
||||
if (typeof(window.Proxy) === 'undefined') {
|
||||
Feedback.send("NO_PROXIES");
|
||||
}
|
||||
|
||||
if (!common.isWebRTCSupported()) {
|
||||
Feedback.send("NO_WEBRTC");
|
||||
}
|
||||
|
||||
var shimPattern = /CRYPTPAD_SHIM/;
|
||||
if (shimPattern.test(Array.isArray.toString())) {
|
||||
Feedback.send("NO_ISARRAY");
|
||||
}
|
||||
|
||||
if (shimPattern.test(Array.prototype.fill.toString())) {
|
||||
Feedback.send("NO_ARRAYFILL");
|
||||
}
|
||||
|
||||
if (typeof(Symbol) === 'undefined') {
|
||||
Feedback.send('NO_SYMBOL');
|
||||
}
|
||||
|
||||
if (typeof(SharedWorker) === "undefined") {
|
||||
Feedback.send('NO_SHAREDWORKER');
|
||||
} else {
|
||||
Feedback.send('SHAREDWORKER');
|
||||
}
|
||||
if (typeof(Worker) === "undefined") {
|
||||
Feedback.send('NO_WEBWORKER');
|
||||
}
|
||||
if (!('serviceWorker' in navigator)) {
|
||||
Feedback.send('NO_SERVICEWORKER');
|
||||
}
|
||||
if (!common.hasCSSVariables()) {
|
||||
Feedback.send('NO_CSS_VARIABLES');
|
||||
}
|
||||
|
||||
Feedback.reportScreenDimensions();
|
||||
Feedback.reportLanguage();
|
||||
};
|
||||
var initFeedback = function (feedback) {
|
||||
// Initialize feedback
|
||||
Feedback.init(feedback);
|
||||
provideFeedback();
|
||||
};
|
||||
|
||||
var userHash;
|
||||
|
||||
(function iOSFirefoxFix () {
|
||||
|
@ -2158,8 +2225,10 @@ define([
|
|||
anonHash: LocalStore.getFSHash(),
|
||||
localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), // TODO move this to LocalStore ?
|
||||
language: common.getLanguage(),
|
||||
cache: rdyCfg.cache,
|
||||
driveEvents: true //rdyCfg.driveEvents // Boolean
|
||||
};
|
||||
common.userHash = userHash;
|
||||
|
||||
// FIXME Backward compatibility
|
||||
if (sessionStorage.newPadFileData) {
|
||||
|
@ -2354,15 +2423,6 @@ define([
|
|||
|
||||
if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
|
||||
|
||||
if (cfg.userHash) {
|
||||
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
|
||||
if (localToken === null) {
|
||||
// if that number hasn't been set to localStorage, do so.
|
||||
localStorage.setItem(Constants.tokenKey, data[Constants.tokenKey]);
|
||||
}
|
||||
}
|
||||
|
||||
initFeedback(data.feedback);
|
||||
initialized = true;
|
||||
channelIsReady();
|
||||
});
|
||||
|
|
|
@ -42,7 +42,7 @@ define([
|
|||
|
||||
var APP = window.APP = {
|
||||
editable: false,
|
||||
online: true,
|
||||
online: false,
|
||||
mobile: function () {
|
||||
if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; }
|
||||
else { return $('body').width() <= 600; }
|
||||
|
@ -3936,7 +3936,8 @@ define([
|
|||
var newRoot = Util.find(manager, ['folders', sfId, 'proxy', manager.user.userObject.ROOT]) || {};
|
||||
subfolder = manager.hasSubfolder(newRoot);
|
||||
// Fix name
|
||||
key = manager.getSharedFolderData(sfId).title || Messages.fm_deletedFolder;
|
||||
var sfData = manager.getSharedFolderData(sfId);
|
||||
key = sfData.title || sfData.lastTitle || Messages.fm_deletedFolder;
|
||||
// Fix icon
|
||||
$icon = isCurrentFolder ? $sharedFolderOpenedIcon : $sharedFolderIcon;
|
||||
isSharedFolder = sfId;
|
||||
|
@ -4363,8 +4364,12 @@ define([
|
|||
var anonDrive = manager.isPathIn(currentPath, [FILES_DATA]) && !APP.loggedIn;
|
||||
|
||||
if (manager.isFolder(el) && !manager.isSharedFolder(el) && !anonDrive) { // Folder
|
||||
// disconnected
|
||||
if (!APP.editable) {
|
||||
return void UI.warn(Messages.error); // XXX
|
||||
}
|
||||
// if folder is inside SF
|
||||
if (manager.isInSharedFolder(paths[0].path)) {
|
||||
else if (manager.isInSharedFolder(paths[0].path)) {
|
||||
return void UI.alert(Messages.convertFolderToSF_SFParent);
|
||||
}
|
||||
// if folder already contains SF
|
||||
|
|
|
@ -776,8 +776,14 @@ define([
|
|||
|
||||
var sframeChan = common.getSframeChannel();
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var priv = metadataMgr.getPrivateData();
|
||||
|
||||
var $div = $(h('div.cp-share-columns'));
|
||||
|
||||
if (priv.offline) {
|
||||
$div.append(h('p', Messages.access_offline));
|
||||
return void cb(void 0, $div);
|
||||
}
|
||||
if (!data) { return void cb(void 0, $div); }
|
||||
|
||||
var div1 = h('div.cp-usergrid-user.cp-share-column.cp-access');
|
||||
|
@ -807,130 +813,139 @@ define([
|
|||
]));
|
||||
}
|
||||
|
||||
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue).appendTo($d);
|
||||
var $pwLabel = $('<label>', {'for': 'cp-app-prop-password'})
|
||||
.text(Messages.creation_passwordValue).appendTo($d);
|
||||
var hasPassword = data.password;
|
||||
var password = UI.passwordInput({
|
||||
id: 'cp-app-prop-password',
|
||||
readonly: 'readonly'
|
||||
});
|
||||
$d.append(password);
|
||||
if (!data.noPassword) {
|
||||
var hasPassword = data.password;
|
||||
var $password = $(password);
|
||||
var $pwInput = $password.find('.cp-password-input');
|
||||
$pwInput.val(data.password || '').click(function () {
|
||||
$pwInput[0].select();
|
||||
var $password = $(password).appendTo($d);
|
||||
var $pwInput = $password.find('.cp-password-input');
|
||||
$pwInput.val(data.password || '').click(function () {
|
||||
$pwInput[0].select();
|
||||
});
|
||||
if (!hasPassword) {
|
||||
$password.hide();
|
||||
$pwLabel.hide();
|
||||
}
|
||||
|
||||
// In the properties, we should have the edit href if we know it.
|
||||
// We should know it because the pad is stored, but it's better to check...
|
||||
if (!data.noEditPassword && owned && data.href) { // FIXME SHEET fix password change for sheets
|
||||
var isOO = parsed.type === 'sheet';
|
||||
var isFile = parsed.hashData.type === 'file';
|
||||
var isSharedFolder = parsed.type === 'drive';
|
||||
|
||||
var changePwTitle = Messages.properties_changePassword;
|
||||
var changePwConfirm = isFile ? Messages.properties_confirmChangeFile : Messages.properties_confirmChange;
|
||||
if (!hasPassword) {
|
||||
changePwTitle = Messages.properties_addPassword;
|
||||
changePwConfirm = isFile ? Messages.properties_confirmNewFile : Messages.properties_confirmNew;
|
||||
}
|
||||
$('<label>', {'for': 'cp-app-prop-change-password'})
|
||||
.text(changePwTitle).appendTo($d);
|
||||
var newPassword = UI.passwordInput({
|
||||
id: 'cp-app-prop-change-password',
|
||||
style: 'flex: 1;'
|
||||
});
|
||||
|
||||
// In the properties, we should have the edit href if we know it.
|
||||
// We should know it because the pad is stored, but it's better to check...
|
||||
if (!data.noEditPassword && owned && data.href) { // FIXME SHEET fix password change for sheets
|
||||
var isOO = parsed.type === 'sheet';
|
||||
var isFile = parsed.hashData.type === 'file';
|
||||
var isSharedFolder = parsed.type === 'drive';
|
||||
|
||||
var changePwTitle = Messages.properties_changePassword;
|
||||
var changePwConfirm = isFile ? Messages.properties_confirmChangeFile : Messages.properties_confirmChange;
|
||||
if (!hasPassword) {
|
||||
changePwTitle = Messages.properties_addPassword;
|
||||
changePwConfirm = isFile ? Messages.properties_confirmNewFile : Messages.properties_confirmNew;
|
||||
var passwordOk = h('button.btn', Messages.properties_changePasswordButton);
|
||||
var changePass = h('span.cp-password-change-container', [
|
||||
newPassword,
|
||||
passwordOk
|
||||
]);
|
||||
var pLocked = false;
|
||||
$(passwordOk).click(function () {
|
||||
var newPass = $(newPassword).find('input').val();
|
||||
if (data.password === newPass ||
|
||||
(!data.password && !newPass)) {
|
||||
return void UI.alert(Messages.properties_passwordSame);
|
||||
}
|
||||
$('<label>', {'for': 'cp-app-prop-change-password'})
|
||||
.text(changePwTitle).appendTo($d);
|
||||
var newPassword = UI.passwordInput({
|
||||
id: 'cp-app-prop-change-password',
|
||||
style: 'flex: 1;'
|
||||
});
|
||||
var passwordOk = h('button.btn', Messages.properties_changePasswordButton);
|
||||
var changePass = h('span.cp-password-change-container', [
|
||||
newPassword,
|
||||
passwordOk
|
||||
]);
|
||||
var pLocked = false;
|
||||
$(passwordOk).click(function () {
|
||||
var newPass = $(newPassword).find('input').val();
|
||||
if (data.password === newPass ||
|
||||
(!data.password && !newPass)) {
|
||||
return void UI.alert(Messages.properties_passwordSame);
|
||||
}
|
||||
if (pLocked) { return; }
|
||||
pLocked = true;
|
||||
UI.confirm(changePwConfirm, function (yes) {
|
||||
if (!yes) { pLocked = false; return; }
|
||||
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
|
||||
var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' :
|
||||
(isOO ? 'Q_OO_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE');
|
||||
if (pLocked) { return; }
|
||||
pLocked = true;
|
||||
UI.confirm(changePwConfirm, function (yes) {
|
||||
if (!yes) { pLocked = false; return; }
|
||||
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
|
||||
var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' :
|
||||
(isOO ? 'Q_OO_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE');
|
||||
|
||||
// If this is a file password change, register to the upload events:
|
||||
// * if there is a pending upload, ask if we shoudl interrupt
|
||||
// * display upload progress
|
||||
var onPending;
|
||||
var onProgress;
|
||||
if (isFile) {
|
||||
onPending = sframeChan.on('Q_BLOB_PASSWORD_CHANGE_PENDING', function (data, cb) {
|
||||
onPending.stop();
|
||||
UI.confirm(Messages.upload_uploadPending, function (yes) {
|
||||
cb({cancel: yes});
|
||||
});
|
||||
});
|
||||
onProgress = sframeChan.on('EV_BLOB_PASSWORD_CHANGE_PROGRESS', function (data) {
|
||||
if (typeof (data) !== "number") { return; }
|
||||
var p = Math.round(data);
|
||||
$(passwordOk).text(p + '%');
|
||||
// If this is a file password change, register to the upload events:
|
||||
// * if there is a pending upload, ask if we shoudl interrupt
|
||||
// * display upload progress
|
||||
var onPending;
|
||||
var onProgress;
|
||||
if (isFile) {
|
||||
onPending = sframeChan.on('Q_BLOB_PASSWORD_CHANGE_PENDING', function (data, cb) {
|
||||
onPending.stop();
|
||||
UI.confirm(Messages.upload_uploadPending, function (yes) {
|
||||
cb({cancel: yes});
|
||||
});
|
||||
});
|
||||
onProgress = sframeChan.on('EV_BLOB_PASSWORD_CHANGE_PROGRESS', function (data) {
|
||||
if (typeof (data) !== "number") { return; }
|
||||
var p = Math.round(data);
|
||||
$(passwordOk).text(p + '%');
|
||||
});
|
||||
}
|
||||
|
||||
sframeChan.query(q, {
|
||||
teamId: typeof(owned) !== "boolean" ? owned : undefined,
|
||||
href: data.href,
|
||||
password: newPass
|
||||
}, function (err, data) {
|
||||
$(passwordOk).text(Messages.properties_changePasswordButton);
|
||||
pLocked = false;
|
||||
if (err || data.error) {
|
||||
console.error(err || data.error);
|
||||
return void UI.alert(Messages.properties_passwordError);
|
||||
}
|
||||
UI.findOKButton().click();
|
||||
|
||||
$pwInput.val(newPass);
|
||||
if (newPass) {
|
||||
$password.show();
|
||||
$pwLabel.show();
|
||||
} else {
|
||||
$password.hide();
|
||||
$pwLabel.hide();
|
||||
}
|
||||
|
||||
sframeChan.query(q, {
|
||||
teamId: typeof(owned) !== "boolean" ? owned : undefined,
|
||||
href: data.href,
|
||||
password: newPass
|
||||
}, function (err, data) {
|
||||
// If the current document is a file or if we're changing the password from a drive,
|
||||
// we don't have to reload the page at the end.
|
||||
// Tell the user the password change was successful and abort
|
||||
if (isFile || priv.app !== parsed.type) {
|
||||
if (onProgress && onProgress.stop) { onProgress.stop(); }
|
||||
$(passwordOk).text(Messages.properties_changePasswordButton);
|
||||
pLocked = false;
|
||||
if (err || data.error) {
|
||||
console.error(err || data.error);
|
||||
return void UI.alert(Messages.properties_passwordError);
|
||||
}
|
||||
UI.findOKButton().click();
|
||||
var alertMsg = data.warning ? Messages.properties_passwordWarningFile
|
||||
: Messages.properties_passwordSuccessFile;
|
||||
return void UI.alert(alertMsg, undefined, {force: true});
|
||||
}
|
||||
|
||||
$pwInput.val(newPass);
|
||||
// Pad password changed: update the href
|
||||
// Use hidden hash if needed (we're an owner of this pad so we know it is stored)
|
||||
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
|
||||
var href = (priv.readOnly && data.roHref) ? data.roHref : data.href;
|
||||
if (useUnsafe !== true) {
|
||||
var newParsed = Hash.parsePadUrl(href);
|
||||
var newSecret = Hash.getSecrets(newParsed.type, newParsed.hash, newPass);
|
||||
var newHash = Hash.getHiddenHashFromKeys(parsed.type, newSecret, {});
|
||||
href = Hash.hashToHref(newHash, parsed.type);
|
||||
}
|
||||
|
||||
// If the current document is a file or if we're changing the password from a drive,
|
||||
// we don't have to reload the page at the end.
|
||||
// Tell the user the password change was successful and abort
|
||||
if (isFile || priv.app !== parsed.type) {
|
||||
if (onProgress && onProgress.stop) { onProgress.stop(); }
|
||||
$(passwordOk).text(Messages.properties_changePasswordButton);
|
||||
var alertMsg = data.warning ? Messages.properties_passwordWarningFile
|
||||
: Messages.properties_passwordSuccessFile;
|
||||
return void UI.alert(alertMsg, undefined, {force: true});
|
||||
if (data.warning) {
|
||||
return void UI.alert(Messages.properties_passwordWarning, function () {
|
||||
common.gotoURL(href);
|
||||
}, {force: true});
|
||||
}
|
||||
return void UI.alert(Messages.properties_passwordSuccess, function () {
|
||||
if (!isSharedFolder) {
|
||||
common.gotoURL(href);
|
||||
}
|
||||
|
||||
// Pad password changed: update the href
|
||||
// Use hidden hash if needed (we're an owner of this pad so we know it is stored)
|
||||
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
|
||||
var href = (priv.readOnly && data.roHref) ? data.roHref : data.href;
|
||||
if (useUnsafe !== true) {
|
||||
var newParsed = Hash.parsePadUrl(href);
|
||||
var newSecret = Hash.getSecrets(newParsed.type, newParsed.hash, newPass);
|
||||
var newHash = Hash.getHiddenHashFromKeys(parsed.type, newSecret, {});
|
||||
href = Hash.hashToHref(newHash, parsed.type);
|
||||
}
|
||||
|
||||
if (data.warning) {
|
||||
return void UI.alert(Messages.properties_passwordWarning, function () {
|
||||
common.gotoURL(href);
|
||||
}, {force: true});
|
||||
}
|
||||
return void UI.alert(Messages.properties_passwordSuccess, function () {
|
||||
if (!isSharedFolder) {
|
||||
common.gotoURL(href);
|
||||
}
|
||||
}, {force: true});
|
||||
});
|
||||
}, {force: true});
|
||||
});
|
||||
});
|
||||
$d.append(changePass);
|
||||
}
|
||||
});
|
||||
$d.append(changePass);
|
||||
}
|
||||
if (owned) {
|
||||
var deleteOwned = h('button.btn.btn-danger-alt', [h('i.cptools.cptools-destroy'), Messages.fc_delete_owned]);
|
||||
|
|
|
@ -82,7 +82,7 @@ define([
|
|||
MT.displayAvatar = function (common, $container, href, name, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb || function () {}));
|
||||
var displayDefault = function () {
|
||||
var text = (href && typeof(href) === "string") ? href : Util.getFirstCharacter(name);
|
||||
var text = Util.getFirstCharacter(name || Messages.anonymous);
|
||||
var $avatar = $('<span>', {'class': 'cp-avatar-default'}).text(text);
|
||||
$container.append($avatar);
|
||||
if (cb) { cb(); }
|
||||
|
|
|
@ -54,6 +54,9 @@ define([
|
|||
|
||||
if (!common.isLoggedIn()) { return void cb(void 0, $d); }
|
||||
|
||||
var privateData = common.getMetadataMgr().getPrivateData();
|
||||
if (privateData.offline) { return void cb(void 0, $d); }
|
||||
|
||||
// File and history size...
|
||||
var owned = Modal.isOwned(Env, data);
|
||||
|
||||
|
|
|
@ -273,6 +273,20 @@ define([
|
|||
var hasFriends = opts.hasFriends;
|
||||
var onFriendShare = Util.mkEvent();
|
||||
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var priv = metadataMgr.getPrivateData();
|
||||
if (priv.offline) {
|
||||
return void cb(void 0, {
|
||||
content: h('p', Messages.share_noContactsOffline),
|
||||
buttons: [{
|
||||
className: 'cancel',
|
||||
name: Messages.filePicker_close,
|
||||
onClick: function () {},
|
||||
keys: [27]
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
var friendsObject = hasFriends ? createShareWithFriends(opts, onFriendShare, opts.getLinkValue) : UIElements.noContactsMessage(common);
|
||||
var friendsList = friendsObject.content;
|
||||
|
||||
|
@ -642,6 +656,8 @@ define([
|
|||
opts.teams = teams;
|
||||
var hasFriends = opts.hasFriends = Object.keys(opts.friends || {}).length ||
|
||||
Object.keys(teams).length;
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var priv = metadataMgr.getPrivateData();
|
||||
|
||||
// check if the pad is password protected
|
||||
var pathname = opts.pathname;
|
||||
|
@ -662,23 +678,24 @@ define([
|
|||
$rights.find('input[type="radio"]').trigger('change');
|
||||
};
|
||||
var onShowContacts = function () {
|
||||
if (!hasFriends) {
|
||||
if (!hasFriends || priv.offline) {
|
||||
$rights.hide();
|
||||
}
|
||||
};
|
||||
|
||||
var contactsActive = hasFriends && !priv.offline;
|
||||
var tabs = [{
|
||||
getTab: getContactsTab,
|
||||
title: Messages.share_contactCategory,
|
||||
icon: "fa fa-address-book",
|
||||
active: hasFriends,
|
||||
active: contactsActive,
|
||||
onShow: onShowContacts,
|
||||
onHide: resetTab
|
||||
}, {
|
||||
getTab: getLinkTab,
|
||||
title: Messages.share_linkCategory,
|
||||
icon: "fa fa-link",
|
||||
active: !hasFriends,
|
||||
active: !contactsActive,
|
||||
}, {
|
||||
getTab: getEmbedTab,
|
||||
title: Messages.share_embedCategory,
|
||||
|
|
|
@ -423,7 +423,7 @@ var factory = function () {
|
|||
}
|
||||
catch (e) { return null; }
|
||||
};
|
||||
var fetchDecryptedMetadata = function (src, strKey, cb) {
|
||||
var fetchDecryptedMetadata = function (src, key, cb) {
|
||||
if (typeof(src) !== 'string') {
|
||||
return window.setTimeout(function () {
|
||||
cb('NO_SOURCE');
|
||||
|
@ -431,7 +431,7 @@ var factory = function () {
|
|||
}
|
||||
fetchMetadata(src, function (e, buffer) {
|
||||
if (e) { return cb(e); }
|
||||
var key = Decrypt.getKeyFromStr(strKey);
|
||||
if (typeof(key) === "string") { key = Decrypt.getKeyFromStr(key); }
|
||||
cb(void 0, decryptMetadata(buffer, key));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1037,6 +1037,7 @@ define([
|
|||
//});
|
||||
|
||||
execCommand('GET_MY_INFO', null, function (e, info) {
|
||||
if (e) { return; }
|
||||
contactsData[info.curvePublic] = info;
|
||||
});
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@ define([
|
|||
NetConfig, AppConfig,
|
||||
Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) {
|
||||
|
||||
var onReadyEvt = Util.mkEvent(true);
|
||||
var onCacheReadyEvt = Util.mkEvent(true);
|
||||
|
||||
// Default settings for new users
|
||||
var NEW_USER_SETTINGS = {
|
||||
drive: {
|
||||
|
@ -598,6 +601,7 @@ define([
|
|||
pendingFriends: store.proxy.friends_pending || {},
|
||||
supportPrivateKey: Util.find(store.proxy, ['mailboxes', 'supportadmin', 'keys', 'curvePrivate']),
|
||||
accountName: store.proxy.login_name || '',
|
||||
offline: store.offline,
|
||||
teams: teams,
|
||||
plan: account.plan
|
||||
}
|
||||
|
@ -1035,9 +1039,7 @@ define([
|
|||
});
|
||||
};
|
||||
Store.setPadTitle = function (clientId, data, cb) {
|
||||
if (store.offline) {
|
||||
return void cb({ error: 'OFFLINE' });
|
||||
}
|
||||
onReadyEvt.reg(function () {
|
||||
var title = data.title;
|
||||
var href = data.href;
|
||||
var channel = data.channel;
|
||||
|
@ -1110,6 +1112,11 @@ define([
|
|||
Array.prototype.push.apply(allData, res);
|
||||
});
|
||||
var contains = allData.length !== 0;
|
||||
if (store.offline && !contains) {
|
||||
return void cb({ error: 'OFFLINE' });
|
||||
} else if (store.offline) {
|
||||
return void cb();
|
||||
}
|
||||
allData.forEach(function (obj) {
|
||||
var pad = obj.data;
|
||||
pad.atime = +new Date();
|
||||
|
@ -1188,6 +1195,8 @@ define([
|
|||
onSync(teamId, waitFor());
|
||||
});
|
||||
}).nThen(cb);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
// Filepicker app
|
||||
|
@ -1263,8 +1272,17 @@ define([
|
|||
}
|
||||
});
|
||||
});
|
||||
var result = res || viewRes;
|
||||
|
||||
// If we're not fully synced yet and we don't have a result, wait for the ready event
|
||||
if (!result && store.offline) {
|
||||
onReadyEvt.reg(function () {
|
||||
Store.getPadDataFromChannel(clientId, obj, cb);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Call back with the best value we can get
|
||||
cb(res || viewRes || {});
|
||||
cb(result || {});
|
||||
};
|
||||
|
||||
// Hidden hash: if a pad is deleted, we may have to switch back to full hash
|
||||
|
@ -1410,13 +1428,15 @@ define([
|
|||
// Universal
|
||||
Store.universal = {
|
||||
execCommand: function (clientId, obj, cb) {
|
||||
var type = obj.type;
|
||||
var data = obj.data;
|
||||
if (store.modules[type]) {
|
||||
store.modules[type].execCommand(clientId, data, cb);
|
||||
} else {
|
||||
return void cb({error: type + ' is disabled'});
|
||||
}
|
||||
onReadyEvt.reg(function () {
|
||||
var type = obj.type;
|
||||
var data = obj.data;
|
||||
if (store.modules[type]) {
|
||||
store.modules[type].execCommand(clientId, data, cb);
|
||||
} else {
|
||||
return void cb({error: type + ' is disabled'});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
var loadUniversal = function (Module, type, waitFor, clientId) {
|
||||
|
@ -1456,17 +1476,23 @@ define([
|
|||
// Cursor
|
||||
Store.cursor = {
|
||||
execCommand: function (clientId, data, cb) {
|
||||
if (!store.cursor) { return void cb ({error: 'Cursor channel is disabled'}); }
|
||||
store.cursor.execCommand(clientId, data, cb);
|
||||
// The cursor module can only be used when the store is ready
|
||||
onReadyEvt.reg(function () {
|
||||
if (!store.cursor) { return void cb ({error: 'Cursor channel is disabled'}); }
|
||||
store.cursor.execCommand(clientId, data, cb);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Mailbox
|
||||
Store.mailbox = {
|
||||
execCommand: function (clientId, data, cb) {
|
||||
if (!store.loggedIn) { return void cb(); }
|
||||
if (!store.mailbox) { return void cb ({error: 'Mailbox is disabled'}); }
|
||||
store.mailbox.execCommand(clientId, data, cb);
|
||||
// The mailbox can only be used when the store is ready
|
||||
onReadyEvt.reg(function () {
|
||||
if (!store.loggedIn) { return void cb(); }
|
||||
if (!store.mailbox) { return void cb ({error: 'Mailbox is disabled'}); }
|
||||
store.mailbox.execCommand(clientId, data, cb);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1723,7 +1749,7 @@ define([
|
|||
noChainPad: true,
|
||||
channel: data.channel,
|
||||
metadata: data.metadata,
|
||||
network: store.network,
|
||||
network: store.network || store.networkPromise,
|
||||
//readOnly: data.readOnly,
|
||||
onConnect: function (wc, sendMessage) {
|
||||
channel.sendMessage = function (msg, cId, cb) {
|
||||
|
@ -1956,6 +1982,7 @@ define([
|
|||
Store.getPadMetadata = function (clientId, data, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb));
|
||||
|
||||
if (store.offline || !store.anon_rpc) { return void cb({ error: 'OFFLINE' }); }
|
||||
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); }
|
||||
if (data.channel.length !== 32) { return void cb({ error: 'EINVAL'}); }
|
||||
store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) {
|
||||
|
@ -2207,7 +2234,7 @@ define([
|
|||
if (!s) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
SF.load({
|
||||
isNew: isNew,
|
||||
network: store.network,
|
||||
network: store.network || store.networkPromise,
|
||||
store: s,
|
||||
isNewChannel: Store.isNewChannel
|
||||
}, id, data, cb);
|
||||
|
@ -2223,16 +2250,18 @@ define([
|
|||
});
|
||||
};
|
||||
Store.addSharedFolder = function (clientId, data, cb) {
|
||||
var s = getStore(data.teamId);
|
||||
s.manager.addSharedFolder(data, function (id) {
|
||||
if (id && typeof(id) === "object" && id.error) {
|
||||
return void cb(id);
|
||||
}
|
||||
var send = data.teamId ? s.sendEvent : sendDriveEvent;
|
||||
send('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
}, clientId);
|
||||
cb(id);
|
||||
onReadyEvt.reg(function () {
|
||||
var s = getStore(data.teamId);
|
||||
s.manager.addSharedFolder(data, function (id) {
|
||||
if (id && typeof(id) === "object" && id.error) {
|
||||
return void cb(id);
|
||||
}
|
||||
var send = data.teamId ? s.sendEvent : sendDriveEvent;
|
||||
send('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
}, clientId);
|
||||
cb(id);
|
||||
});
|
||||
});
|
||||
};
|
||||
Store.updateSharedFolderPassword = function (clientId, data, cb) {
|
||||
|
@ -2501,8 +2530,9 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
var onReady = function (clientId, returned, cb) {
|
||||
var onCacheReady = function (clientId, cb) {
|
||||
var proxy = store.proxy;
|
||||
if (store.manager) { return void cb(); }
|
||||
var unpin = function (data, cb) {
|
||||
if (!store.loggedIn) { return void cb(); }
|
||||
Store.unpinPads(null, data, cb);
|
||||
|
@ -2511,8 +2541,6 @@ define([
|
|||
if (!store.loggedIn) { return void cb(); }
|
||||
Store.pinPads(null, data, cb);
|
||||
};
|
||||
if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; }
|
||||
if (!proxy.friends_pending) { proxy.friends_pending = {}; }
|
||||
var manager = store.manager = ProxyManager.create(proxy.drive, {
|
||||
onSync: function (cb) { onSync(null, cb); },
|
||||
edPublic: proxy.edPublic,
|
||||
|
@ -2534,9 +2562,26 @@ define([
|
|||
});
|
||||
var userObject = store.userObject = manager.user.userObject;
|
||||
addSharedFolderHandler();
|
||||
userObject.migrate(cb);
|
||||
};
|
||||
|
||||
// onReady: called when the drive is synced (not using the cache anymore)
|
||||
// "cb" is wrapped in Util.once() and may have already been called
|
||||
// if we have a local cache
|
||||
var onReady = function (clientId, returned, cb) {
|
||||
console.error('READY');
|
||||
store.ready = true;
|
||||
var proxy = store.proxy;
|
||||
var manager = store.manager;
|
||||
var userObject = store.userObject;
|
||||
|
||||
nThen(function (waitFor) {
|
||||
userObject.migrate(waitFor());
|
||||
if (manager) { return; }
|
||||
if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; }
|
||||
if (!proxy.friends_pending) { proxy.friends_pending = {}; }
|
||||
onCacheReady(clientId, waitFor());
|
||||
manager = store.manager;
|
||||
userObject = store.userObject;
|
||||
}).nThen(function (waitFor) {
|
||||
initAnonRpc(null, null, waitFor());
|
||||
initRpc(null, null, waitFor());
|
||||
|
@ -2569,7 +2614,7 @@ define([
|
|||
loadUniversal(Messenger, 'messenger', waitFor);
|
||||
store.messenger = store.modules['messenger'];
|
||||
loadUniversal(Profile, 'profile', waitFor);
|
||||
loadUniversal(Team, 'team', waitFor, clientId);
|
||||
loadUniversal(Team, 'team', waitFor, clientId); // TODO load teams offline
|
||||
loadUniversal(History, 'history', waitFor);
|
||||
}).nThen(function () {
|
||||
var requestLogin = function () {
|
||||
|
@ -2606,7 +2651,14 @@ define([
|
|||
returned.feedback = Util.find(proxy, ['settings', 'general', 'allowUserFeedback']);
|
||||
Feedback.init(returned.feedback);
|
||||
|
||||
if (typeof(cb) === 'function') { cb(returned); }
|
||||
// "cb" may have already been called by onCacheReady
|
||||
store.returned = returned;
|
||||
if (typeof(cb) === 'function') { cb(); }
|
||||
|
||||
store.offline = false;
|
||||
sendDriveEvent('NETWORK_RECONNECT'); // Tell inner that we're now online
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
broadcast([], "STORE_READY", returned);
|
||||
|
||||
if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) {
|
||||
// even anonymous users should have a persistent, unique-ish id
|
||||
|
@ -2662,6 +2714,8 @@ define([
|
|||
});
|
||||
|
||||
loadMailbox();
|
||||
|
||||
onReadyEvt.fire();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -2705,8 +2759,39 @@ define([
|
|||
if (!data.userHash) {
|
||||
returned.anonHash = Hash.getEditHashFromKeys(secret);
|
||||
}
|
||||
}).on('cacheready', function (info) {
|
||||
store.offline = true;
|
||||
store.realtime = info.realtime;
|
||||
store.networkPromise = info.networkPromise;
|
||||
store.cacheReturned = returned;
|
||||
|
||||
// Check if we can connect
|
||||
var to = setTimeout(function () {
|
||||
store.networkTimeout = true;
|
||||
broadcast([], "LOADING_DRIVE", {
|
||||
type: "offline"
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
store.networkPromise.then(function () {
|
||||
clearTimeout(to);
|
||||
}, function (err) {
|
||||
console.error(err);
|
||||
clearTimeout(to);
|
||||
});
|
||||
|
||||
if (!data.cache) { return; }
|
||||
|
||||
// Make sure we have a valid user object before emitting cacheready
|
||||
if (rt.proxy && !rt.proxy.drive) { return; }
|
||||
|
||||
onCacheReady(clientId, function () {
|
||||
if (typeof(cb) === "function") { cb(returned); }
|
||||
onCacheReadyEvt.fire();
|
||||
});
|
||||
}).on('ready', function (info) {
|
||||
if (store.userObject) { return; } // the store is already ready, it is a reconnection
|
||||
delete store.networkTimeout;
|
||||
if (store.ready) { return; } // the store is already ready, it is a reconnection
|
||||
store.driveMetadata = info.metadata;
|
||||
if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; }
|
||||
var drive = rt.proxy.drive;
|
||||
|
@ -2732,10 +2817,12 @@ define([
|
|||
rt.proxy.on('disconnect', function () {
|
||||
store.offline = true;
|
||||
sendDriveEvent('NETWORK_DISCONNECT');
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
});
|
||||
rt.proxy.on('reconnect', function () {
|
||||
store.offline = false;
|
||||
sendDriveEvent('NETWORK_RECONNECT');
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
});
|
||||
|
||||
// Ping clients regularly to make sure one tab was not closed without sending a removeClient()
|
||||
|
@ -2783,17 +2870,29 @@ define([
|
|||
*/
|
||||
var initialized = false;
|
||||
|
||||
var whenReady = function (cb) {
|
||||
if (store.returned) { return void cb(); }
|
||||
setTimeout(function() {
|
||||
whenReady(cb);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
Store.init = function (clientId, data, _callback) {
|
||||
var callback = Util.once(_callback);
|
||||
|
||||
// If this is not the first tab and we're offline, callback only if the app
|
||||
// supports offline mode
|
||||
if (initialized && !store.returned && data.cache) {
|
||||
return void onCacheReadyEvt.reg(function () {
|
||||
callback({
|
||||
state: 'ALREADY_INIT',
|
||||
returned: store.cacheReturned
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// If this is not the first tab (initialized is true), it means either we don't
|
||||
// support offline or we're already online
|
||||
if (initialized) {
|
||||
return void whenReady(function () {
|
||||
if (store.networkTimeout) {
|
||||
postMessage(clientId, "LOADING_DRIVE", {
|
||||
type: "offline"
|
||||
});
|
||||
}
|
||||
return void onReadyEvt.reg(function () {
|
||||
callback({
|
||||
state: 'ALREADY_INIT',
|
||||
returned: store.returned
|
||||
|
@ -2815,8 +2914,6 @@ define([
|
|||
}
|
||||
if (ret && ret.error) {
|
||||
initialized = false;
|
||||
} else {
|
||||
store.returned = ret;
|
||||
}
|
||||
|
||||
callback(ret);
|
||||
|
|
|
@ -21,6 +21,7 @@ define([
|
|||
if (!client || !client.cursor) { return; }
|
||||
var chan = ctx.channels[client.channel];
|
||||
if (!chan) { return; }
|
||||
if (!chan.sendMsg) { return; } // Store not synced yet, we're running with the cache
|
||||
var data = {
|
||||
id: client.id,
|
||||
cursor: client.cursor
|
||||
|
|
|
@ -128,11 +128,11 @@ define([
|
|||
var uo = store.manager.addProxy(id, sf.rt, leave, secondaryKey);
|
||||
// NOTE: Shared folder migration, disable for now
|
||||
SF.checkMigration(secondaryKey, sf.rt.proxy, uo, function () {
|
||||
cb(sf.rt, sf.metadata);
|
||||
cb(sf.rt);
|
||||
});
|
||||
*/
|
||||
store.manager.addProxy(id, sf.rt, leave, secondaryKey);
|
||||
cb(sf.rt, sf.metadata);
|
||||
cb(sf.rt);
|
||||
});
|
||||
sf.teams.push({
|
||||
cb: cb,
|
||||
|
@ -182,7 +182,28 @@ define([
|
|||
}
|
||||
};
|
||||
var rt = sf.rt = Listmap.create(listmapConfig);
|
||||
rt.proxy.on('ready', function (info) {
|
||||
rt.proxy.on('cacheready', function () {
|
||||
if (!sf.teams) {
|
||||
return;
|
||||
}
|
||||
sf.teams.forEach(function (obj) {
|
||||
var leave = function () { SF.leave(secret.channel, obj.store.id); };
|
||||
|
||||
// We can safely call addProxy and obj.cb here because
|
||||
// 1. addProxy won't re-add the same folder twice on 'ready'
|
||||
// 2. obj.cb is using Util.once
|
||||
rt.cache = true;
|
||||
|
||||
// If we're updating the password of an existing folder, force the creation
|
||||
// of a new userobject in proxy-manager. Once it's done, remove this flag
|
||||
// to make sure we won't create a second new userobject on 'ready'
|
||||
obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey, config.updatePassword);
|
||||
config.updatePassword = false;
|
||||
obj.cb(sf.rt);
|
||||
});
|
||||
sf.ready = true;
|
||||
});
|
||||
rt.proxy.on('ready', function () {
|
||||
if (isNew && !Object.keys(rt.proxy).length) {
|
||||
// New Shared folder: no migration required
|
||||
rt.proxy.version = 2;
|
||||
|
@ -196,13 +217,13 @@ define([
|
|||
var uo = obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey);
|
||||
// NOTE: Shared folder migration, disable for now
|
||||
SF.checkMigration(secondaryKey, rt.proxy, uo, function () {
|
||||
obj.cb(sf.rt, info.metadata);
|
||||
obj.cb(sf.rt);
|
||||
});
|
||||
*/
|
||||
obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey);
|
||||
obj.cb(sf.rt, info.metadata);
|
||||
rt.cache = false;
|
||||
obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey, config.updatePassword);
|
||||
obj.cb(sf.rt);
|
||||
});
|
||||
sf.metadata = info.metadata;
|
||||
sf.ready = true;
|
||||
});
|
||||
rt.proxy.on('error', function (info) {
|
||||
|
@ -304,6 +325,7 @@ define([
|
|||
SF.load({
|
||||
network: network,
|
||||
store: s,
|
||||
updatePassword: true,
|
||||
isNewChannel: Store.isNewChannel
|
||||
}, sfId, sf, waitFor());
|
||||
if (!s.rpc) { return; }
|
||||
|
|
|
@ -73,6 +73,7 @@ var init = function (client, cb) {
|
|||
});
|
||||
chan.on('CONNECT', function (cfg, cb) {
|
||||
debug('SharedW connecting to store...');
|
||||
/*
|
||||
if (self.store) {
|
||||
debug('Store already exists!');
|
||||
if (cfg.driveEvents) {
|
||||
|
@ -80,31 +81,35 @@ var init = function (client, cb) {
|
|||
}
|
||||
return void cb(self.store);
|
||||
}
|
||||
*/
|
||||
|
||||
debug('Loading new async store');
|
||||
// One-time initialization (init async-store)
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
Object.keys(self.tabs).forEach(function (cId) {
|
||||
if (excludes.indexOf(cId) !== -1) { return; }
|
||||
if (!self.store) {
|
||||
debug('Loading new async store');
|
||||
// One-time initialization (init async-store)
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
Object.keys(self.tabs).forEach(function (cId) {
|
||||
if (excludes.indexOf(cId) !== -1) { return; }
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (data && data.state === "ALREADY_INIT") {
|
||||
debug('Store already exists!');
|
||||
self.store = data.returned;
|
||||
return void cb(data.returned);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,16 @@ define([
|
|||
};
|
||||
|
||||
// Add a shared folder to the list
|
||||
var addProxy = function (Env, id, lm, leave, editKey) {
|
||||
var addProxy = function (Env, id, lm, leave, editKey, force) {
|
||||
if (Env.folders[id] && !force) {
|
||||
// Shared folder already added to the proxy-manager, probably
|
||||
// a cached version
|
||||
if (Env.folders[id].offline && !lm.cache) {
|
||||
Env.folders[id].offline = false;
|
||||
Env.Store.refreshDriveUI();
|
||||
}
|
||||
return;
|
||||
}
|
||||
var cfg = getConfig(Env);
|
||||
cfg.sharedFolder = true;
|
||||
cfg.id = id;
|
||||
|
@ -38,7 +47,8 @@ define([
|
|||
Env.folders[id] = {
|
||||
proxy: lm.proxy,
|
||||
userObject: userObject,
|
||||
leave: leave
|
||||
leave: leave,
|
||||
offline: Boolean(lm.cache)
|
||||
};
|
||||
if (proxy.on) {
|
||||
proxy.on('disconnect', function () {
|
||||
|
@ -537,7 +547,7 @@ define([
|
|||
Env.user.userObject.add(id, resolved.path);
|
||||
|
||||
// 2b. load the proxy
|
||||
Env.loadSharedFolder(id, folderData, waitFor(function (rt, metadata) {
|
||||
Env.loadSharedFolder(id, folderData, waitFor(function (rt) {
|
||||
if (!rt) {
|
||||
waitFor.abort();
|
||||
return void cb({ error: 'EDELETED' });
|
||||
|
@ -546,11 +556,13 @@ define([
|
|||
if (!rt.proxy.metadata) { // Creating a new shared folder
|
||||
rt.proxy.metadata = { title: data.name || Messages.fm_newFolder };
|
||||
}
|
||||
// If we're importing a folder, check its serverside metadata
|
||||
if (data.folderData && metadata) {
|
||||
var fData = Env.user.proxy[UserObject.SHARED_FOLDERS][id];
|
||||
if (metadata.owners) { fData.owners = metadata.owners; }
|
||||
if (metadata.expire) { fData.expire = +metadata.expire; }
|
||||
if (data.folderData) {
|
||||
// If we're importing a folder, check its serverside metadata
|
||||
Env.Store.getPadMetadata(null, { channel: folderData.channel }, function (md) {
|
||||
var fData = Env.user.proxy[UserObject.SHARED_FOLDERS][id];
|
||||
if (md.owners) { fData.owners = md.owners; }
|
||||
if (md.expire) { fData.expire = +md.expire; }
|
||||
});
|
||||
}
|
||||
}), !Boolean(data.folderData));
|
||||
}).nThen(function () {
|
||||
|
@ -961,8 +973,8 @@ define([
|
|||
if (!resolved.id) {
|
||||
var el = Env.user.userObject.find(resolved.path);
|
||||
if (Env.user.userObject.isSharedFolder(el) && Env.folders[el]) {
|
||||
Env.folders[el].proxy.metadata.title = data.newName;
|
||||
Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = data.newName;
|
||||
Env.folders[el].proxy.metadata.title = data.newName || Messages.fm_folder;
|
||||
Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = data.newName || Messages.fm_folder;
|
||||
return void cb();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ define([
|
|||
hash = obj.hash;
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
SFCommonO.start({
|
||||
cache: true,
|
||||
hash: hash,
|
||||
href: href,
|
||||
useCreationScreen: true,
|
||||
|
|
|
@ -218,6 +218,7 @@ define([
|
|||
|
||||
Cryptpad.ready(waitFor(), {
|
||||
driveEvents: cfg.driveEvents,
|
||||
cache: Boolean(cfg.cache),
|
||||
currentPad: currentPad
|
||||
});
|
||||
|
||||
|
@ -497,7 +498,6 @@ define([
|
|||
// We've received a link without /p/ and it doesn't work without a password: abort
|
||||
return void todo();
|
||||
}
|
||||
|
||||
// Wrong password or deleted file?
|
||||
askPassword(true, passwordCfg);
|
||||
}));
|
||||
|
|
|
@ -186,14 +186,22 @@ define([
|
|||
};
|
||||
|
||||
funcs.getFileSize = function (channelId, cb) {
|
||||
funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) {
|
||||
if (!data) { return void cb("No response"); }
|
||||
if (data.error) { return void cb(data.error); }
|
||||
if (data.response && data.response.length && typeof(data.response[0]) === 'number') {
|
||||
return void cb(void 0, data.response[0]);
|
||||
} else {
|
||||
cb('INVALID_RESPONSE');
|
||||
}
|
||||
nThen(function (waitFor) {
|
||||
ctx.cache.getBlobCache(channelId, waitFor(function(err, blob) {
|
||||
if (err) { return; }
|
||||
waitFor.abort();
|
||||
cb(null, blob.length);
|
||||
}));
|
||||
}).nThen(function () {
|
||||
funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) {
|
||||
if (!data) { return void cb("No response"); }
|
||||
if (data.error) { return void cb(data.error); }
|
||||
if (data.response && data.response.length && typeof(data.response[0]) === 'number') {
|
||||
return void cb(void 0, data.response[0]);
|
||||
} else {
|
||||
cb('INVALID_RESPONSE');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -367,6 +367,9 @@ MessengerUI, Messages) {
|
|||
if (!toolbar.connected) { return; }
|
||||
updateUserList(toolbar, config);
|
||||
});
|
||||
setTimeout(function () {
|
||||
updateUserList(toolbar, config, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1288,8 +1291,8 @@ MessengerUI, Messages) {
|
|||
if (typeof el !== "string" || !el.trim()) { return; }
|
||||
if (typeof tb[el] === "function") {
|
||||
if (!init && config.displayed.indexOf(el) !== -1) { return; } // Already done
|
||||
toolbar[el] = tb[el](toolbar, config);
|
||||
if (!init) { config.displayed.push(el); }
|
||||
toolbar[el] = tb[el](toolbar, config);
|
||||
}
|
||||
});
|
||||
checkSize();
|
||||
|
@ -1417,7 +1420,7 @@ MessengerUI, Messages) {
|
|||
toolbar.isErrorState = bool; // Stop kickSpinner
|
||||
toolbar.title.toggleClass('cp-toolbar-unsync', bool); // "read only" next to the title
|
||||
if (bool && toolbar.spinner) {
|
||||
toolbar.spinner.text(Messages.offline);
|
||||
toolbar.spinner.text(Messages.Offline);
|
||||
} else {
|
||||
kickSpinner(toolbar, config);
|
||||
}
|
||||
|
|
|
@ -1496,5 +1496,13 @@
|
|||
"admin_unarchiveTitle": "Dokumente wiederherstellen",
|
||||
"admin_archiveTitle": "Dokumente archivieren",
|
||||
"history_trimPrompt": "Dieses Dokument hat einen Verlauf von {0} angesammelt, was das Laden verlangsamen kann. Ziehe in Betracht, den Verlauf zu löschen, sofern er nicht benötigt wird.",
|
||||
"contacts_confirmCancel": "Bist du sicher, dass du die Kontaktanfrage an <b>{0}</b> zurücknehmen möchtest?"
|
||||
"contacts_confirmCancel": "Bist du sicher, dass du die Kontaktanfrage an <b>{0}</b> zurücknehmen möchtest?",
|
||||
"admin_support_collapse": "Einklappen",
|
||||
"admin_support_open": "Anzeigen",
|
||||
"admin_support_first": "Erstellt: ",
|
||||
"admin_support_last": "Aktualisiert: ",
|
||||
"admin_support_closed": "Geschlossene Tickets:",
|
||||
"admin_support_answered": "Beantwortete Tickets:",
|
||||
"admin_support_normal": "Unbeantwortete Tickets:",
|
||||
"admin_support_premium": "Premium-Tickets:"
|
||||
}
|
||||
|
|
|
@ -1496,5 +1496,8 @@
|
|||
"admin_archiveTitle": "Archiver les documents",
|
||||
"mediatag_loadButton": "Charger la pièce jointe",
|
||||
"history_trimPrompt": "Ce document a accumulé {0} d'historique qui peut ralentir le temps de chargement. Envisagez de supprimer l'historique s'il n'est pas nécessaire.",
|
||||
"contacts_confirmCancel": "Êtes-vous sûr de vouloir annuler votre demande de contact avec <b>{0}</b> ?"
|
||||
"contacts_confirmCancel": "Êtes-vous sûr de vouloir annuler votre demande de contact avec <b>{0}</b> ?",
|
||||
"share_noContactsOffline": "Vous êtes déconnecté. Les contacts ne sont pas disponibles.",
|
||||
"access_offline": "Vous êtes déconnecté. La gestion de l'accès n'est pas disponible.",
|
||||
"offlineNoCacheError": "Vous êtes déconnecté et ce document n'est pas disponible."
|
||||
}
|
||||
|
|
|
@ -1504,5 +1504,8 @@
|
|||
"admin_support_open": "Show",
|
||||
"admin_support_collapse": "Collapse",
|
||||
"admin_support_first": "Created on: ",
|
||||
"admin_support_last": "Updated on: "
|
||||
"admin_support_last": "Updated on: ",
|
||||
"offlineNoCacheError": "You are offline and this document is not available.",
|
||||
"access_offline": "You are currently offline. Access management is not available.",
|
||||
"share_noContactsOffline": "You are currently offline. Contacts are not available."
|
||||
}
|
||||
|
|
|
@ -278,6 +278,7 @@ define([
|
|||
if (!proxy.drive || typeof(proxy.drive) !== 'object') {
|
||||
throw new Error("Corrupted drive");
|
||||
}
|
||||
APP.online = !privateData.offline;
|
||||
var drive = DriveUI.create(common, {
|
||||
$limit: usageBar && usageBar.$container,
|
||||
proxy: proxy,
|
||||
|
|
|
@ -105,6 +105,7 @@ define([
|
|||
hash: hash,
|
||||
href: href,
|
||||
afterSecrets: afterSecrets,
|
||||
cache: true,
|
||||
noHash: true,
|
||||
noRealtime: true,
|
||||
driveEvents: true,
|
||||
|
|
|
@ -729,6 +729,11 @@ define([
|
|||
cursor.offsetUpdate();
|
||||
var oldText = inner.outerHTML;
|
||||
|
||||
// Get scroll position
|
||||
var sTop = $iframe.scrollTop();
|
||||
var sTopMax = $iframe.innerHeight() - $('iframe').innerHeight();
|
||||
var scrollMax = sTop === sTopMax;
|
||||
|
||||
// Apply the changes
|
||||
var patch = (DD).diff(inner, userDocStateDom);
|
||||
(DD).apply(inner, patch);
|
||||
|
@ -765,6 +770,10 @@ define([
|
|||
comments.onContentUpdate();
|
||||
|
||||
updateTOC();
|
||||
|
||||
if (scrollMax) {
|
||||
$iframe.scrollTop($iframe.innerHeight());
|
||||
}
|
||||
});
|
||||
|
||||
framework.setTextContentGetter(function() {
|
||||
|
|
|
@ -328,6 +328,7 @@ define([
|
|||
}, true);
|
||||
}
|
||||
|
||||
driveAPP.online = !teamData.offline;
|
||||
var drive = DriveUI.create(common, {
|
||||
proxy: proxy,
|
||||
folders: folders,
|
||||
|
|
Loading…
Reference in New Issue