Merge branch 'c' into staging

pull/1/head
yflory 4 years ago
commit 61753aff4f

@ -39,6 +39,7 @@
"dev": "DEV=1 node server.js",
"fresh": "FRESH=1 node server.js",
"offline": "FRESH=1 OFFLINE=1 node server.js",
"offlinedev": "DEV=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 .",

@ -299,7 +299,7 @@ nThen(function (w) {
oscar.edKeys.edPublic
],
keys: rosterKeys,
anon_rpc: oscar.anonRpc,
store: oscar,
lastKnownHash: void 0,
}, w(function (err, roster) {
if (err) {
@ -514,7 +514,7 @@ nThen(function (w) {
channel: rosterKeys.channel,
//owners: [], // Alice doesn't know who the owners might be...
keys: rosterKeys,
anon_rpc: alice.anonRpc,
store: alice,
lastKnownHash: void 0, // alice should fetch everything from the beginning of time...
}, w(function (err, roster) {
if (err) {
@ -742,7 +742,7 @@ nThen(function (w) {
network: bob.network,
channel: rosterKeys.channel,
keys: rosterKeys,
anon_rpc: bob.anonRpc,
store: bob,
//lastKnownHash: oscar.lastRosterCheckpointHash
//lastKnownHash: oscar.lastKnownHash, // FIXME this doesn't work. off-by-one?
}, w(function (err, roster) {

@ -73,6 +73,11 @@ define([
return JSONSortify(obj);
};
var supportsXLSX = function () {
return !(typeof(Atomics) === "undefined" || typeof (SharedArrayBuffer) === "undefined");
};
var toolbar;
@ -208,6 +213,19 @@ define([
}
};
// Make sure a former tab on the same worker doesn't have remaining locks
var checkClients = function (clients) {
Object.keys(content.ids).forEach(function (id) {
var tabId = Number(id.slice(33)); // remove the netflux ID and the "-"
if (clients.indexOf(tabId) === -1) {
removeClient({
id: tabId
});
}
});
};
var getFileType = function () {
var type = common.getMetadataMgr().getPrivateData().ooType;
var title = common.getMetadataMgr().getMetadataLazy().title;
@ -723,6 +741,7 @@ define([
sframeChan.on('EV_OO_EVENT', function (obj) {
switch (obj.ev) {
case 'READY':
checkClients(obj.data);
cb();
break;
case 'LEAVE':
@ -984,6 +1003,12 @@ define([
deleteOfflineLocks();
// Prepare callback
if (cpNfInner) {
var waitLock = APP.waitLock = Util.mkEvent(true);
setTimeout(function () {
// Make sure the waitLock is never stuck
waitLock.fire();
if (waitLock === APP.waitLock) { delete APP.waitLock; }
}, 5000);
var onPatchSent = function (again) {
if (!again) { cpNfInner.offPatchSent(onPatchSent); }
// Answer to our onlyoffice
@ -999,6 +1024,8 @@ define([
type: "getLock",
locks: getLock()
});
waitLock.fire();
if (waitLock === APP.waitLock) { delete APP.waitLock; }
} else {
if (!isLockedModal.modal) {
isLockedModal.modal = UI.openCustomModal(isLockedModal.content);
@ -1107,7 +1134,8 @@ define([
Channel.create(msgEv, postMsg, function (chan) {
APP.chan = chan;
var send = ooChannel.send = function (obj) {
var send = ooChannel.send = function (obj, force) {
if (APP.onStrictSaveChanges && !force) { return; } // can't push to OO before reloading cp
debug(obj, 'toOO');
chan.event('CMD', obj);
};
@ -1121,10 +1149,19 @@ define([
case "isSaveLock":
// TODO ping the server to check if we're online first?
if (!offline) {
send({
type: "saveLock",
saveLock: false
});
if (APP.waitLock) {
APP.waitLock.reg(function () {
send({
type: "saveLock",
saveLock: false
}, true);
});
} else {
send({
type: "saveLock",
saveLock: false
}, true);
}
}
break;
case "getLock":
@ -1135,6 +1172,18 @@ define([
send({ type: "message" });
break;
case "saveChanges":
// If we have unsaved data before reloading for a checkpoint...
if (APP.onStrictSaveChanges) {
APP.unsavedChanges = {
type: "saveChanges",
changes: parseChanges(obj.changes),
changesIndex: ooChannel.cpIndex || 0,
locks: getUserLock(getId()),
excelAdditionalInfo: null
};
APP.onStrictSaveChanges();
return;
}
// We're sending our changes to netflux
handleChanges(obj, send);
// If we're alone, clean up the medias
@ -1223,27 +1272,29 @@ define([
$iframe.prop('tabindex', '-1');
var $tb = $iframe.find('head');
var css = // Old OO
'#id-toolbar-full .toolbar-group:nth-child(2), #id-toolbar-full .separator:nth-child(3) { display: none; }' +
'#fm-btn-save { display: none !important; }' +
//'#id-toolbar-full .toolbar-group:nth-child(2), #id-toolbar-full .separator:nth-child(3) { display: none; }' +
//'#fm-btn-save { display: none !important; }' +
'#panel-settings-general tr.autosave { display: none !important; }' +
'#panel-settings-general tr.coauth { display: none !important; }' +
'#header { display: none !important; }' +
//'#header { display: none !important; }' +
'#title-doc-name { display: none !important; }' +
'#title-user-name { display: none !important; }' +
(supportsXLSX() ? '' : '#slot-btn-dt-print { display: none !important; }') +
// New OO:
'#asc-gen566 { display: none !important; }' + // Insert image from url
'section[data-tab="ins"] .separator:nth-last-child(2) { display: none !important; }' + // separator
'#slot-btn-insequation { display: none !important; }' + // Insert equation
'.toolbar .tabs .ribtab:not(.canedit) { display: none !important; }' + // Switch collaborative mode
'#app-title { display: none !important; }' + // OnlyOffice logo + doc title
//'.toolbar .tabs .ribtab:not(.canedit) { display: none !important; }' + // Switch collaborative mode
'#fm-btn-info { display: none !important; }' + // Author name, doc title, etc. in "File" (menu entry)
'#panel-info { display: none !important; }' + // Same but content
'#image-button-from-url { display: none !important; }' + // Inline image settings: replace with url
'#file-menu-panel .devider { display: none !important; }' + // separator in the "File" menu
'#file-menu-panel { top: 28px !important; }' + // Position of the "File" menu
'#left-btn-spellcheck, #left-btn-about { display: none !important; }'+
'div.btn-users.dropdown-toggle { display: none; !important }';
if (readOnly) {
css += '#toolbar { display: none !important; }';
//css += '#app-title { display: none !important; }'; // OnlyOffice logo + doc title
//css += '#file-menu-panel { top: 28px !important; }'; // Position of the "File" menu
}
$('<style>').text(css).appendTo($tb);
setTimeout(function () {
@ -1320,8 +1371,23 @@ define([
} catch (e) {}
} else {
setEditable(true);
handleNewLocks({}, content.locks);
if (APP.unsavedChanges) {
var unsaved = APP.unsavedChanges;
delete APP.unsavedChanges;
rtChannel.sendMsg(unsaved, null, function (err, hash) {
if (err) { return void UI.warn(Messages.error); } // XXX Better error message?
// This is supposed to be a "send" function to tell our OO
// to unlock the cell. We use this to know that the patch was
// correctly sent so that we can apply it to our OO too.
ooChannel.send(unsaved);
ooChannel.cpIndex++;
ooChannel.lastHash = hash;
});
}
}
if (isLockedModal.modal && force) {
isLockedModal.modal.closeModal();
delete isLockedModal.modal;
@ -1361,10 +1427,13 @@ define([
}
}
};
/*
// NOTE: Make sure it won't break anaything new (Firefox setTimeout bug)
window.onbeforeunload = function () {
var ifr = document.getElementsByTagName('iframe')[0];
if (ifr) { ifr.remove(); }
};
*/
APP.UploadImageFiles = function (files, type, id, jwt, cb) {
cb('NO');
@ -1705,10 +1774,6 @@ define([
});
};
var supportsXLSX = function () {
return !(typeof(Atomics) === "undefined" || typeof (SharedArrayBuffer) === "undefined");
};
var exportXLSXFile = function() {
var text = getContent();
var suggestion = Title.suggestTitle(Title.defaultTitle);
@ -1997,6 +2062,28 @@ define([
}
};
var setStrictEditing = function () {
if (APP.isFast) { return; }
var editor = getEditor();
var editing = editor.asc_isDocumentModified();
if (editing) {
evOnPatch.fire();
} else {
evOnSync.fire();
}
};
APP.onFastChange = function (isFast) {
APP.isFast = isFast;
if (isFast) {
if (APP.hasChangedInterval) {
window.clearInterval(APP.hasChangedInterval);
}
return;
}
setStrictEditing();
APP.hasChangedInterval = window.setInterval(setStrictEditing, 500);
};
APP.getContent = function () { return content; };
APP.onLocal = config.onLocal = function () {
@ -2471,19 +2558,32 @@ define([
checkCheckpoint();
}
var editor = getEditor();
if (content.hashes) {
var latest = getLastCp(true);
var newLatest = getLastCp();
if (newLatest.index > latest.index || (newLatest.index && !latest.index)) {
ooChannel.queue = [];
ooChannel.ready = false;
// New checkpoint
sframeChan.query('Q_OO_SAVE', {
hash: newLatest.hash,
url: newLatest.file
}, function () {
checkNewCheckpoint();
});
var reload = function () {
// New checkpoint
sframeChan.query('Q_OO_SAVE', {
hash: newLatest.hash,
url: newLatest.file
}, function () {
checkNewCheckpoint();
});
};
if (editor.asc_isDocumentModified()) {
setEditable(false);
APP.onStrictSaveChanges = function () {
reload();
delete APP.onStrictSaveChanges;
};
editor.asc_Save();
} else {
reload();
}
}
oldHashes = JSON.parse(JSON.stringify(content.hashes));
}

@ -1689,17 +1689,17 @@ function(){var t=this;if(this.openingEnd.xlsxStart)return;this.openingEnd.xlsxSt
function(actionType,options,oAdditionalData,dataContainer){var fileType=options.fileType;if(c_oAscFileType.PDF===fileType||c_oAscFileType.PDFA===fileType){var printPagesData=this.wb.calcPagesPrint(options.advancedOptions);var pdfPrinterMemory=this.wb.printSheets(printPagesData).DocumentRenderer.Memory;dataContainer.data=oAdditionalData["nobase64"]?pdfPrinterMemory.GetData():pdfPrinterMemory.GetBase64Memory()}else{var oBinaryFileWriter=new AscCommonExcel.BinaryFileWriter(this.wbModel);if(c_oAscFileType.CSV===
fileType)if(options.advancedOptions instanceof asc.asc_CTextOptions){oAdditionalData["codepage"]=options.advancedOptions.asc_getCodePage();oAdditionalData["delimiter"]=options.advancedOptions.asc_getDelimiter();oAdditionalData["delimiterChar"]=options.advancedOptions.asc_getDelimiterChar()}dataContainer.data=oBinaryFileWriter.Write(oAdditionalData["nobase64"])}if(window.isCloudCryptoDownloadAs){var sParamXml="<m_nCsvTxtEncoding>"+oAdditionalData["codepage"]+"</m_nCsvTxtEncoding>";sParamXml+="<m_nCsvDelimiter>"+
oAdditionalData["delimiter"]+"</m_nCsvDelimiter>";window["AscDesktopEditor"]["CryptoDownloadAs"](dataContainer.data,fileType,sParamXml);return true}};spreadsheet_api.prototype.asc_isDocumentModified=function(){if(!this.canSave||this.asc_getCellEditMode())return true;else if(History&&History.Have_Changes)return History.Have_Changes();return false};spreadsheet_api.prototype.asc_registerCallback=function(name,callback,replaceOldCallback){this.handlers.add(name,callback,replaceOldCallback)};spreadsheet_api.prototype.asc_unregisterCallback=
function(name,callback){this.handlers.remove(name,callback)};spreadsheet_api.prototype.asc_SetDocumentPlaceChangedEnabled=function(val){this.wb.setDocumentPlaceChangedEnabled(val)};spreadsheet_api.prototype.asc_SetFastCollaborative=function(bFast){if(this.collaborativeEditing){AscCommon.CollaborativeEditing.Set_Fast(bFast);this.collaborativeEditing.setFast(bFast)}};spreadsheet_api.prototype.asc_setThumbnailStylesSizes=function(width,height){this.styleThumbnailWidth=width;this.styleThumbnailHeight=
height};spreadsheet_api.prototype.sheetsChanged=function(){this.handlers.trigger("asc_onSheetsChanged")};spreadsheet_api.prototype.asyncFontsDocumentStartLoaded=function(){this.OpenDocumentProgress.Type=c_oAscAsyncAction.LoadDocumentFonts;this.OpenDocumentProgress.FontsCount=this.FontLoader.fonts_loading.length;this.OpenDocumentProgress.CurrentFont=0;this.sync_StartAction(c_oAscAsyncActionType.BlockInteraction,c_oAscAsyncAction.LoadDocumentFonts)};spreadsheet_api.prototype.asyncFontsDocumentEndLoaded=
function(){this.sync_EndAction(c_oAscAsyncActionType.BlockInteraction,c_oAscAsyncAction.LoadDocumentFonts);if(this.asyncMethodCallback!==undefined){this.asyncMethodCallback();this.asyncMethodCallback=undefined}else{this.FontLoadWaitComplete=true;this._openDocumentEndCallback()}};spreadsheet_api.prototype.asyncFontEndLoaded=function(font){this.sync_EndAction(c_oAscAsyncActionType.Information,c_oAscAsyncAction.LoadFont)};spreadsheet_api.prototype._loadFonts=function(fonts,callback){if(window["NATIVE_EDITOR_ENJINE"])return callback();
this.asyncMethodCallback=callback;var arrLoadFonts=[];for(var i in fonts)arrLoadFonts.push(new AscFonts.CFont(i,0,"",0));AscFonts.FontPickerByCharacter.extendFonts(arrLoadFonts);this.FontLoader.LoadDocumentFonts2(arrLoadFonts)};spreadsheet_api.prototype.openDocument=function(file){this._openDocument(file.data);this._openOnClient()};spreadsheet_api.prototype.openDocumentFromZip=function(wb,data){var t=this;return new Promise(function(resolve,reject){var openXml=AscCommon.openXml;if(t.isChartEditor){resolve();
return}var nextPromise;if(data){var doc=new openXml.OpenXmlPackage;var wbPart=null;var wbXml=null;var pivotCaches={};var jsZipWrapper=new AscCommon.JSZipWrapper;nextPromise=jsZipWrapper.loadAsync(data).then(function(zip){return doc.openFromZip(zip)}).then(function(){wbPart=doc.getPartByRelationshipType(openXml.relationshipTypes.workbook);return wbPart.getDocumentContent()}).then(function(contentWorkbook){wbXml=new AscCommonExcel.CT_Workbook;(new openXml.SaxParserBase).parse(contentWorkbook,wbXml);
if(wbXml.pivotCaches)return wbXml.pivotCaches.reduce(function(prevVal,wbPivotCacheXml){var pivotTableCacheDefinitionPart;var pivotTableCacheDefinition;return prevVal.then(function(){if(null!==wbPivotCacheXml.cacheId&&null!==wbPivotCacheXml.id){pivotTableCacheDefinitionPart=wbPart.getPartById(wbPivotCacheXml.id);return pivotTableCacheDefinitionPart.getDocumentContent()}}).then(function(content){if(content){pivotTableCacheDefinition=new Asc.CT_PivotCacheDefinition;(new openXml.SaxParserBase).parse(content,
pivotTableCacheDefinition);if(pivotTableCacheDefinition.isValidCacheSource()){pivotCaches[wbPivotCacheXml.cacheId]=pivotTableCacheDefinition;if(pivotTableCacheDefinition.id){var partPivotTableCacheRecords=pivotTableCacheDefinitionPart.getPartById(pivotTableCacheDefinition.id);return partPivotTableCacheRecords.getDocumentContent()}}}}).then(function(content){if(content){var pivotTableCacheRecords=new Asc.CT_PivotCacheRecords;(new openXml.SaxParserBase).parse(content,pivotTableCacheRecords);pivotTableCacheDefinition.cacheRecords=
pivotTableCacheRecords}})},Promise.resolve())}).then(function(){if(wbXml.sheets){var wsIndex=0;return wbXml.sheets.reduce(function(prevVal,wbSheetXml){var wsPart;return prevVal.then(function(){if(null!==wbSheetXml.id){var actions=[];wsPart=wbPart.getPartById(wbSheetXml.id);var pivotParts=wsPart.getPartsByRelationshipType(openXml.relationshipTypes.pivotTable);for(var i=0;i<pivotParts.length;++i)actions.push(pivotParts[i].getDocumentContent());return Promise.all(actions)}}).then(function(res){if(res){var ws=
t.wbModel.getWorksheet(wsIndex);for(var i=0;i<res.length;++i){var pivotTable=new Asc.CT_pivotTableDefinition;(new openXml.SaxParserBase).parse(res[i],pivotTable);var cacheDefinition=pivotCaches[pivotTable.cacheId];if(cacheDefinition){pivotTable.cacheDefinition=cacheDefinition;ws.insertPivotTable(pivotTable)}}}wsIndex++})},Promise.resolve())}}).catch(function(err){if(window.console&&window.console.log)window.console.log(err)}).then(function(){jsZipWrapper.close()})}else nextPromise=Promise.resolve();
nextPromise.then(function(err){openXml.SaxParserDataTransfer={};return Asc.ReadDefTableStyles(wb)}).then(resolve,reject)})};spreadsheet_api.prototype.syncCollaborativeChanges=function(){if(!this.collaborativeEditing.getFast())this.handlers.trigger("asc_onCollaborativeChanges")};spreadsheet_api.prototype._applyFirstLoadChanges=function(){if(this.isDocumentLoadComplete)return;if(this.collaborativeEditing.applyChanges())this.onDocumentContentReady();this.collaborativeEditing.sendChanges()};spreadsheet_api.prototype.goTo=
function(action){var comment=this.wbModel.getComment(action&&action["data"]);if(comment){this.asc_showWorksheet(this.wbModel.getWorksheetById(comment.wsId).getIndex());this.asc_selectComment(comment.nId);this.asc_showComment(comment.nId)}};spreadsheet_api.prototype._coAuthoringInitEnd=function(){var t=this;this.collaborativeEditing=new AscCommonExcel.CCollaborativeEditing({"askLock":function(){t.CoAuthoringApi.askLock.apply(t.CoAuthoringApi,arguments)},"releaseLocks":function(){t.CoAuthoringApi.releaseLocks.apply(t.CoAuthoringApi,
function(name,callback){this.handlers.remove(name,callback)};spreadsheet_api.prototype.asc_SetDocumentPlaceChangedEnabled=function(val){this.wb.setDocumentPlaceChangedEnabled(val)};spreadsheet_api.prototype.asc_SetFastCollaborative=function(bFast){if(this.collaborativeEditing){if(window.parent&&window.parent.APP&&window.parent.APP.onFastChange)window.parent.APP.onFastChange(bFast);AscCommon.CollaborativeEditing.Set_Fast(bFast);this.collaborativeEditing.setFast(bFast)}};spreadsheet_api.prototype.asc_setThumbnailStylesSizes=
function(width,height){this.styleThumbnailWidth=width;this.styleThumbnailHeight=height};spreadsheet_api.prototype.sheetsChanged=function(){this.handlers.trigger("asc_onSheetsChanged")};spreadsheet_api.prototype.asyncFontsDocumentStartLoaded=function(){this.OpenDocumentProgress.Type=c_oAscAsyncAction.LoadDocumentFonts;this.OpenDocumentProgress.FontsCount=this.FontLoader.fonts_loading.length;this.OpenDocumentProgress.CurrentFont=0;this.sync_StartAction(c_oAscAsyncActionType.BlockInteraction,c_oAscAsyncAction.LoadDocumentFonts)};
spreadsheet_api.prototype.asyncFontsDocumentEndLoaded=function(){this.sync_EndAction(c_oAscAsyncActionType.BlockInteraction,c_oAscAsyncAction.LoadDocumentFonts);if(this.asyncMethodCallback!==undefined){this.asyncMethodCallback();this.asyncMethodCallback=undefined}else{this.FontLoadWaitComplete=true;this._openDocumentEndCallback()}};spreadsheet_api.prototype.asyncFontEndLoaded=function(font){this.sync_EndAction(c_oAscAsyncActionType.Information,c_oAscAsyncAction.LoadFont)};spreadsheet_api.prototype._loadFonts=
function(fonts,callback){if(window["NATIVE_EDITOR_ENJINE"])return callback();this.asyncMethodCallback=callback;var arrLoadFonts=[];for(var i in fonts)arrLoadFonts.push(new AscFonts.CFont(i,0,"",0));AscFonts.FontPickerByCharacter.extendFonts(arrLoadFonts);this.FontLoader.LoadDocumentFonts2(arrLoadFonts)};spreadsheet_api.prototype.openDocument=function(file){this._openDocument(file.data);this._openOnClient()};spreadsheet_api.prototype.openDocumentFromZip=function(wb,data){var t=this;return new Promise(function(resolve,
reject){var openXml=AscCommon.openXml;if(t.isChartEditor){resolve();return}var nextPromise;if(data){var doc=new openXml.OpenXmlPackage;var wbPart=null;var wbXml=null;var pivotCaches={};var jsZipWrapper=new AscCommon.JSZipWrapper;nextPromise=jsZipWrapper.loadAsync(data).then(function(zip){return doc.openFromZip(zip)}).then(function(){wbPart=doc.getPartByRelationshipType(openXml.relationshipTypes.workbook);return wbPart.getDocumentContent()}).then(function(contentWorkbook){wbXml=new AscCommonExcel.CT_Workbook;
(new openXml.SaxParserBase).parse(contentWorkbook,wbXml);if(wbXml.pivotCaches)return wbXml.pivotCaches.reduce(function(prevVal,wbPivotCacheXml){var pivotTableCacheDefinitionPart;var pivotTableCacheDefinition;return prevVal.then(function(){if(null!==wbPivotCacheXml.cacheId&&null!==wbPivotCacheXml.id){pivotTableCacheDefinitionPart=wbPart.getPartById(wbPivotCacheXml.id);return pivotTableCacheDefinitionPart.getDocumentContent()}}).then(function(content){if(content){pivotTableCacheDefinition=new Asc.CT_PivotCacheDefinition;
(new openXml.SaxParserBase).parse(content,pivotTableCacheDefinition);if(pivotTableCacheDefinition.isValidCacheSource()){pivotCaches[wbPivotCacheXml.cacheId]=pivotTableCacheDefinition;if(pivotTableCacheDefinition.id){var partPivotTableCacheRecords=pivotTableCacheDefinitionPart.getPartById(pivotTableCacheDefinition.id);return partPivotTableCacheRecords.getDocumentContent()}}}}).then(function(content){if(content){var pivotTableCacheRecords=new Asc.CT_PivotCacheRecords;(new openXml.SaxParserBase).parse(content,
pivotTableCacheRecords);pivotTableCacheDefinition.cacheRecords=pivotTableCacheRecords}})},Promise.resolve())}).then(function(){if(wbXml.sheets){var wsIndex=0;return wbXml.sheets.reduce(function(prevVal,wbSheetXml){var wsPart;return prevVal.then(function(){if(null!==wbSheetXml.id){var actions=[];wsPart=wbPart.getPartById(wbSheetXml.id);var pivotParts=wsPart.getPartsByRelationshipType(openXml.relationshipTypes.pivotTable);for(var i=0;i<pivotParts.length;++i)actions.push(pivotParts[i].getDocumentContent());
return Promise.all(actions)}}).then(function(res){if(res){var ws=t.wbModel.getWorksheet(wsIndex);for(var i=0;i<res.length;++i){var pivotTable=new Asc.CT_pivotTableDefinition;(new openXml.SaxParserBase).parse(res[i],pivotTable);var cacheDefinition=pivotCaches[pivotTable.cacheId];if(cacheDefinition){pivotTable.cacheDefinition=cacheDefinition;ws.insertPivotTable(pivotTable)}}}wsIndex++})},Promise.resolve())}}).catch(function(err){if(window.console&&window.console.log)window.console.log(err)}).then(function(){jsZipWrapper.close()})}else nextPromise=
Promise.resolve();nextPromise.then(function(err){openXml.SaxParserDataTransfer={};return Asc.ReadDefTableStyles(wb)}).then(resolve,reject)})};spreadsheet_api.prototype.syncCollaborativeChanges=function(){if(!this.collaborativeEditing.getFast())this.handlers.trigger("asc_onCollaborativeChanges")};spreadsheet_api.prototype._applyFirstLoadChanges=function(){if(this.isDocumentLoadComplete)return;if(this.collaborativeEditing.applyChanges())this.onDocumentContentReady();this.collaborativeEditing.sendChanges()};
spreadsheet_api.prototype.goTo=function(action){var comment=this.wbModel.getComment(action&&action["data"]);if(comment){this.asc_showWorksheet(this.wbModel.getWorksheetById(comment.wsId).getIndex());this.asc_selectComment(comment.nId);this.asc_showComment(comment.nId)}};spreadsheet_api.prototype._coAuthoringInitEnd=function(){var t=this;this.collaborativeEditing=new AscCommonExcel.CCollaborativeEditing({"askLock":function(){t.CoAuthoringApi.askLock.apply(t.CoAuthoringApi,arguments)},"releaseLocks":function(){t.CoAuthoringApi.releaseLocks.apply(t.CoAuthoringApi,
arguments)},"sendChanges":function(){t._onSaveChanges.apply(t,arguments)},"applyChanges":function(){t._onApplyChanges.apply(t,arguments)},"updateAfterApplyChanges":function(){t._onUpdateAfterApplyChanges.apply(t,arguments)},"drawSelection":function(){t._onDrawSelection.apply(t,arguments)},"drawFrozenPaneLines":function(){t._onDrawFrozenPaneLines.apply(t,arguments)},"updateAllSheetsLock":function(){t._onUpdateAllSheetsLock.apply(t,arguments)},"showDrawingObjects":function(){t._onShowDrawingObjects.apply(t,
arguments)},"showComments":function(){t._onShowComments.apply(t,arguments)},"cleanSelection":function(){t._onCleanSelection.apply(t,arguments)},"updateDocumentCanSave":function(){t._onUpdateDocumentCanSave()},"checkCommentRemoveLock":function(lockElem){return t._onCheckCommentRemoveLock(lockElem)},"unlockDefName":function(){t._onUnlockDefName.apply(t,arguments)},"checkDefNameLock":function(lockElem){return t._onCheckDefNameLock(lockElem)},"updateAllLayoutsLock":function(){t._onUpdateAllLayoutsLock.apply(t,
arguments)},"updateAllHeaderFooterLock":function(){t._onUpdateAllHeaderFooterLock.apply(t,arguments)}},this.getViewMode());this.CoAuthoringApi.onConnectionStateChanged=function(e){t.handlers.trigger("asc_onConnectionStateChanged",e)};this.CoAuthoringApi.onLocksAcquired=function(e){if(t._coAuthoringCheckEndOpenDocument(t.CoAuthoringApi.onLocksAcquired,e))return;if(2!=e["state"]){var elementValue=e["blockValue"];var lockElem=t.collaborativeEditing.getLockByElem(elementValue,c_oAscLockTypes.kLockTypeOther);

File diff suppressed because one or more lines are too long

@ -440,6 +440,7 @@ define([
if (e) { return void cb({error: e}); }
store.rpc = call;
store.onRpcReadyEvt.fire();
Store.getPinLimit(null, null, function (obj) {
if (obj.error) { console.error(obj.error); }
@ -1434,7 +1435,7 @@ define([
// Universal
Store.universal = {
execCommand: function (clientId, obj, cb) {
onReadyEvt.reg(function () {
var todo = function () {
var type = obj.type;
var data = obj.data;
if (store.modules[type]) {
@ -1442,7 +1443,11 @@ define([
} else {
return void cb({error: type + ' is disabled'});
}
});
};
// Teams support offline/cache mode
if (obj.type === "team") { return void todo(); }
// Other modules should wait for the ready event
onReadyEvt.reg(todo);
}
};
var loadUniversal = function (Module, type, waitFor, clientId) {
@ -1594,6 +1599,50 @@ define([
});
};
Store.onRejected = function (allowed, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
// There is an allow list: check if we can authenticate
if (!Array.isArray(allowed)) { return void cb('EINVAL'); }
if (!store.loggedIn || !store.proxy.edPublic) { return void cb('EFORBIDDEN'); }
var teamModule = store.modules['team'];
var teams = (teamModule && teamModule.getTeams()) || [];
var _store;
if (allowed.indexOf(store.proxy.edPublic) !== -1) {
// We are allowed: use our own rpc
_store = store;
} else if (teams.some(function (teamId) {
// We're not allowed: check our teams
var ed = Util.find(store, ['proxy', 'teams', teamId, 'keys', 'drive', 'edPublic']);
if (allowed.indexOf(ed) === -1) { return false; }
// This team is allowed: use its rpc
var t = teamModule.getTeam(teamId);
_store = t;
return true;
})) {}
var auth = function () {
var rpc = _store.rpc;
if (!rpc) { return void cb('EFORBIDDEN'); }
rpc.send('COOKIE', '', function (err) {
cb(err);
});
};
// Wait for the RPC we need to be ready and then tyr to authenticate
if (_store.onRpcReadyEvt) {
_store.onRpcReadyEvt.reg(function () {
auth();
});
return;
}
// Fall back to the old system in case onRpcReadyEvt doesn't exist (shouldn't happen)
auth();
};
Store.joinPad = function (clientId, data) {
if (data.versionHash) {
return void getVersionHash(clientId, data);
@ -1697,37 +1746,7 @@ define([
},
onError: onError,
onChannelError: onError,
onRejected: function (allowed, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
// There is an allow list: check if we can authenticate
if (!Array.isArray(allowed)) { return void cb('EINVAL'); }
if (!store.loggedIn || !store.proxy.edPublic) { return void cb('EFORBIDDEN'); }
onReadyEvt.reg(function () {
var rpc;
var teamModule = store.modules['team'];
var teams = (teamModule && teamModule.getTeams()) || [];
if (allowed.indexOf(store.proxy.edPublic) !== -1) {
// We are allowed: use our own rpc
rpc = store.rpc;
} else if (teams.some(function (teamId) {
// We're not allowed: check our teams
var ed = Util.find(store, ['proxy', 'teams', teamId, 'keys', 'drive', 'edPublic']);
if (allowed.indexOf(ed) === -1) { return false; }
// This team is allowed: use its rpc
var t = teamModule.getTeam(teamId);
rpc = t.rpc;
return true;
})) {}
if (!rpc) { return void cb('EFORBIDDEN'); }
rpc.send('COOKIE', '', function (err) {
cb(err);
});
});
},
onRejected: Store.onRejected,
onConnectionChange: function (info) {
if (!info.state) {
channel.bcast("PAD_DISCONNECT");
@ -2574,8 +2593,23 @@ define([
rt: store.realtime
});
var userObject = store.userObject = manager.user.userObject;
addSharedFolderHandler();
userObject.migrate(cb);
nThen(function (waitFor) {
addSharedFolderHandler();
userObject.migrate(waitFor());
}).nThen(function (waitFor) {
var network = store.network || store.networkPromise;
SF.loadSharedFolders(Store, network, store, userObject, waitFor, function (obj) {
var data = {
type: 'sf',
progress: 100*obj.progress/obj.max
};
postMessage(clientId, 'LOADING_DRIVE', data);
}, true);
}).nThen(function (waitFor) {
loadUniversal(Team, 'team', waitFor, clientId);
}).nThen(function () {
cb();
});
};
// onReady: called when the drive is synced (not using the cache anymore)
@ -2627,7 +2661,7 @@ define([
loadUniversal(Messenger, 'messenger', waitFor);
store.messenger = store.modules['messenger'];
loadUniversal(Profile, 'profile', waitFor);
loadUniversal(Team, 'team', waitFor, clientId); // TODO load teams offline
store.modules['team'].onReady(waitFor);
loadUniversal(History, 'history', waitFor);
}).nThen(function () {
var requestLogin = function () {
@ -2763,6 +2797,7 @@ define([
var rt = window.rt = Listmap.create(listmapConfig);
store.driveSecret = secret;
store.proxy = rt.proxy;
store.onRpcReadyEvt = Util.mkEvent(true);
store.loggedIn = typeof(data.userHash) !== "undefined";
var returned = {};

@ -40,7 +40,7 @@ define([
if (!c.id) { c.id = chan.wc.myID + '-' + client; }
getHistory(ctx, client, function () {
ctx.emit('READY', '', [client]);
ctx.emit('READY', chan.clients, [client]);
});
// ==> And push the new tab to the list
@ -149,7 +149,7 @@ define([
}
// End of history: emit READY
if (parsed.state && parsed.state === 1 && parsed.channel) {
ctx.emit('READY', '', chan.clients);
ctx.emit('READY', chan.clients, chan.clients);
return;
}
if (parsed.error && parsed.channel) { return; }

@ -491,13 +491,13 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
if (!config.network) { return void cb("EXPECTED_NETWORK"); }
if (!config.channel || typeof(config.channel) !== 'string' || config.channel.length !== 32) { return void cb("EXPECTED_CHANNEL"); }
if (!config.keys || typeof(config.keys) !== 'object') { return void cb("EXPECTED_CRYPTO_KEYS"); }
if (!config.anon_rpc) { return void cb("EXPECTED_ANON_RPC"); }
if (!config.store) { return void cb("EXPECTED_STORE"); }
var response = Util.response(function (label, info) {
console.error('ROSTER_RESPONSE__' + label, info);
});
var anon_rpc = config.anon_rpc;
var store = config.store;
var keys = config.keys;
var me = keys.myCurvePublic;
var channel = config.channel;
@ -571,6 +571,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
}
};
var ready = false;
var onCacheReady = function () {
if (!config.onCacheReady) { return; }
config.onCacheReady(roster);
};
var onReady = function (info) {
//console.log("READY");
webChannel = info;
@ -670,9 +674,18 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
}, delay);
};
var isCacheCheckpoint = function (msg, author) {
var parsed = Util.tryParse(msg);
if (parsed[0] !== 'CHECKPOINT') { return false; }
var changed = simulate(parsed, author, ref);
return changed;
};
var metadata, crypto;
var send = function (msg, cb) {
if (!isReady()) { return void cb("NOT_READY"); }
var anon_rpc = store.anon_rpc;
if (!anon_rpc) { return void cb("ANON_RPC_NOT_READY"); }
var changed = false;
try {
@ -816,7 +829,8 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
nThen(function (w) {
// get metadata so we know the owners and validateKey
anon_rpc.send('GET_METADATA', channel, function (err, data) {
if (!store.anon_rpc) { return; }
store.anon_rpc.send('GET_METADATA', channel, function (err, data) {
if (err) {
w.abort();
return void console.error(err);
@ -827,6 +841,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
if (!config.keys.teamEdPublic && metadata && metadata.validateKey) {
config.keys.teamEdPublic = metadata.validateKey;
}
if (!config.keys.teamEdPublic) {
w.abort();
return void cb("NO_VALIDATE_KEY");
}
try {
crypto = Crypto.Team.createEncryptor(config.keys);
@ -854,6 +872,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
owners: config.owners,
Cache: config.Cache,
isCacheCheckpoint: isCacheCheckpoint,
onCacheReady: onCacheReady,
onChannelError: onChannelError,
onReady: onReady,
onConnect: onConnect,

@ -107,15 +107,12 @@ define([
// If we try to load an existing shared folder (isNew === false) but this folder
// doesn't exist in the database, abort and cb
nThen(function (waitFor) {
// XXX use a config.cache flag in the new branches
// If we don't have a network yet and we're pulling our own SF (no team id)
// Make sure we have a cache
if (!config.store.id && !config.store.network) {
Cache.getChannelCache(secret.channel, waitFor(function (err, res) {
// If we're in onCacheReady, make sure we have a cache for this shared folder
if (config.cache) {
Cache.getChannelCache(secret.channel, waitFor(function (err) {
if (err === "EINVAL") { // Cache not found
waitFor.abort();
store.manager.restrictedProxy(id, secret.channel);
// XXX unrestrict when we connect?
return void cb(null);
}
}));
@ -178,16 +175,6 @@ define([
readOnly: !Boolean(secondaryKey)
};
// If there is an allow list and we're offline, try again when we're synced
var onRejected = function (allowed, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (store.offline && config.Store) {
config.Store.onReadyEvt.reg(cb);
return;
}
cb('ERESTRICTED');
};
var owners = data.owners;
var listmapConfig = {
data: {},
@ -204,7 +191,7 @@ define([
validateKey: secret.keys.validateKey || undefined,
owners: owners
},
onRejected: onRejected // XXX not working
onRejected: config.Store && config.Store.onRejected
};
var rt = sf.rt = Listmap.create(listmapConfig);
rt.proxy.on('cacheready', function () {
@ -368,7 +355,7 @@ define([
- userObject: userObject associated to the main drive
- handler: a function (sfid, rt) called for each shared folder loaded
*/
SF.loadSharedFolders = function (Store, network, store, userObject, waitFor, progress) {
SF.loadSharedFolders = function (Store, network, store, userObject, waitFor, progress, cache) {
var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
var steps = Object.keys(shared).length;
var i = 1;
@ -381,6 +368,7 @@ define([
network: network,
store: store,
Store: Store,
cache: cache,
isNewChannel: Store.isNewChannel
}, id, sf, waitFor(function () {
progress({

@ -27,6 +27,8 @@ define([
var Team = {};
var Nacl = window.nacl;
var onStoreReady = Util.mkEvent(true);
var openCachedTeamChat = function () {}; // Placeholder
var registerChangeEvents = function (ctx, team, proxy, fId) {
if (!team) { return; }
@ -127,9 +129,11 @@ define([
delete ctx.store.proxy.teams[teamId];
ctx.emit('LEAVE_TEAM', teamId, team.clients);
ctx.updateMetadata();
ctx.store.mailbox.close('team-'+teamId, function () {
// Close team mailbox
});
if (ctx.store.mailbox) {
ctx.store.mailbox.close('team-'+teamId, function () {
// Close team mailbox
});
}
};
var getTeamChannelList = function (ctx, id) {
@ -180,29 +184,15 @@ define([
Pinpad.create(ctx.store.network, data, function (e, call) {
if (e) { return void cb(e); }
team.rpc = call;
team.pin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.pin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
team.unpin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.unpin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
team.onRpcReadyEvt.fire();
cb();
});
});
};
var onReady = function (ctx, id, lm, roster, keys, cId, cb) {
var onCacheReady = function (ctx, id, lm, roster, keys, cId, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (ctx.cache[id]) { return void cb(); }
var proxy = lm.proxy;
var team = {
id: id,
@ -212,8 +202,11 @@ define([
realtime: lm.realtime,
handleSharedFolder: function (sfId, rt) { handleSharedFolder(ctx, id, sfId, rt); },
sharedFolders: {}, // equivalent of store.sharedFolders in async-store
roster: roster
roster: roster,
onRpcReadyEvt: Util.mkEvent(true),
offline: true
};
ctx.cache[id] = team;
// Subscribe to events
if (cId) { team.clients.push(cId); }
@ -240,11 +233,6 @@ define([
rosterData.lastKnownHash = hash;
});
// Update metadata
var state = roster.getState();
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', id]);
if (teamData) { teamData.metadata = state.metadata; }
// Broadcast an event to all the tabs displaying this team
team.sendEvent = function (q, data, sender) {
ctx.emit(q, data, team.clients.filter(function (cId) {
@ -266,70 +254,114 @@ define([
};
};
var secret;
team.pin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); };
team.unpin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); };
team.pin = function (data, cb) {
if (!keys.drive.edPrivate) { return void cb({error: 'EFORBIDDEN'}); }
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.pin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
team.unpin = function (data, cb) {
if (!keys.drive.edPrivate) { return void cb({error: 'EFORBIDDEN'}); }
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.unpin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
// Create the proxy manager
var loadSharedFolder = function (id, data, cb, isNew) {
SF.load({
isNew: isNew,
network: ctx.store.network || ctx.store.networkPromise,
store: team,
isNewChannel: ctx.Store.isNewChannel,
Store: ctx.Store
}, id, data, cb);
};
var teamData = ctx.store.proxy.teams[team.id];
var hash = teamData.hash || teamData.roHash;
var secret = Hash.getSecrets('team', hash, teamData.password);
var manager = team.manager = ProxyManager.create(proxy.drive, {
onSync: function (cb) { ctx.Store.onSync(id, cb); },
edPublic: keys.drive.edPublic,
pin: team.pin,
unpin: team.unpin,
loadSharedFolder: loadSharedFolder,
settings: {
drive: Util.find(ctx.store, ['proxy', 'settings', 'drive'])
},
removeOwnedChannel: function (channel, cb) {
var data;
if (typeof(channel) === "object") {
channel.teamId = id;
data = channel;
} else {
data = {
channel: channel,
teamId: id
};
}
ctx.Store.removeOwnedChannel('', data, cb);
},
Store: ctx.Store
}, {
outer: true,
edPublic: keys.drive.edPublic,
loggedIn: true,
log: function (msg) {
// broadcast to all drive apps
team.sendEvent("DRIVE_LOG", msg);
},
rt: team.realtime,
editKey: secret.keys.secondaryKey,
readOnly: Boolean(!secret.keys.secondaryKey)
});
team.secondaryKey = secret && secret.keys.secondaryKey;
team.userObject = manager.user.userObject;
nThen(function (waitFor) {
// Load the shared folders
ctx.teams[id] = team;
delete ctx.cache[id];
registerChangeEvents(ctx, team, proxy);
var network = ctx.store.network || ctx.store.networkPromise;
SF.loadSharedFolders(ctx.Store, network, team,
team.userObject, waitFor, function (data) {
ctx.progress += 70/(ctx.numberOfTeams * data.max);
ctx.updateProgress({
progress: ctx.progress
});
}, true);
}).nThen(function () {
cb();
});
};
var onReady = function (ctx, id, lm, roster, keys, cId, cb) {
// Update metadata
var state = roster.getState();
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', id]);
if (teamData) { teamData.metadata = state.metadata; }
var team;
if (!ctx.store.proxy.teams[id]) { return; }
nThen(function (waitFor) {
if (ctx.cache[id]) { return; }
onCacheReady(ctx, id, lm, roster, keys, cId, waitFor());
}).nThen(function (waitFor) {
team = ctx.teams[id];
// Init Team RPC
if (!keys.drive.edPrivate) { return; }
initRpc(ctx, team, keys.drive, waitFor(function () {}));
}).nThen(function () {
// Create the proxy manager
var loadSharedFolder = function (id, data, cb, isNew) {
SF.load({
isNew: isNew,
network: ctx.store.network,
store: team,
Store: ctx.Store,
isNewChannel: ctx.Store.isNewChannel
}, id, data, cb);
};
var teamData = ctx.store.proxy.teams[team.id];
var hash = teamData.hash || teamData.roHash;
secret = Hash.getSecrets('team', hash, teamData.password);
var manager = team.manager = ProxyManager.create(proxy.drive, {
onSync: function (cb) { ctx.Store.onSync(id, cb); },
edPublic: keys.drive.edPublic,
pin: team.pin,
unpin: team.unpin,
loadSharedFolder: loadSharedFolder,
settings: {
drive: Util.find(ctx.store, ['proxy', 'settings', 'drive'])
},
removeOwnedChannel: function (channel, cb) {
var data;
if (typeof(channel) === "object") {
channel.teamId = id;
data = channel;
} else {
data = {
channel: channel,
teamId: id
};
}
ctx.Store.removeOwnedChannel('', data, cb);
},
Store: ctx.Store
}, {
outer: true,
edPublic: keys.drive.edPublic,
loggedIn: true,
log: function (msg) {
// broadcast to all drive apps
team.sendEvent("DRIVE_LOG", msg);
},
rt: team.realtime,
editKey: secret.keys.secondaryKey,
readOnly: Boolean(!secret.keys.secondaryKey)
});
team.secondaryKey = secret && secret.keys.secondaryKey;
team.userObject = manager.user.userObject;
team.userObject.fixFiles();
}).nThen(function (waitFor) {
// Load the shared folders
ctx.teams[id] = team;
registerChangeEvents(ctx, team, proxy);
SF.checkMigration(team.secondaryKey, proxy, team.userObject, waitFor());
team.userObject.fixFiles();
SF.checkMigration(team.secondaryKey, team.proxy, team.userObject, waitFor());
SF.loadSharedFolders(ctx.Store, ctx.store.network, team,
team.userObject, waitFor, function (data) {
ctx.progress += 70/(ctx.numberOfTeams * data.max);
@ -352,6 +384,7 @@ define([
}
});
}).nThen(function () {
team.offline = false;
if (ctx.onReadyHandlers[id]) {
ctx.onReadyHandlers[id].forEach(function (obj) {
// Callback and subscribe the client to new notifications
@ -369,10 +402,36 @@ define([
};
var checkTeamChannels = function (ctx, id, channel, roster, waitFor, cb) {
var close = function () {
if (ctx.cache[id] || ctx.teams[id]) { closeTeam(ctx, id); }
delete ctx.store.proxy.teams[id];
delete ctx.onReadyHandlers[id];
waitFor.abort();
cb({error: 'ENOENT'});
};
if (channel) {
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", channel, waitFor(function (e, res) {
if (res && res.length && typeof(res[0]) === 'boolean' && res[0]) {
// Channel is empty: remove this team
close();
}
}));
}
if (roster) {
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", roster, waitFor(function (e, res) {
if (res && res.length && typeof(res[0]) === 'boolean' && res[0]) {
// Channel is empty: remove this team
close();
}
}));
}
};
// Progress:
// One team = (30/(#teams))%
// One shared folder = (70/(#teams * #folders))%
var openChannel = function (ctx, teamData, id, _cb) {
var openChannel = function (ctx, teamData, id, _cb, cache) {
var cb = Util.once(Util.mkAsync(_cb));
var hash = teamData.hash || teamData.roHash;
@ -403,32 +462,55 @@ define([
: Crypto.Team.deriveGuestKeys(rosterData.view || '');
nThen(function (waitFor) {
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", secret.channel, waitFor(function (e, response) {
if (response && response.length && typeof(response[0]) === 'boolean' && response[0]) {
// Channel is empty: remove this team
delete ctx.store.proxy.teams[id];
waitFor.abort();
cb({error: 'ENOENT'});
}
}));
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", rosterKeys.channel, waitFor(function (e, response) {
if (response && response.length && typeof(response[0]) === 'boolean' && response[0]) {
// Channel is empty: remove this team
delete ctx.store.proxy.teams[id];
waitFor.abort();
cb({error: 'ENOENT'});
}
}));
if (cache) {
// If we're in cache mode, make sure we have a cache for this team
Cache.getChannelCache(secret.channel, waitFor(function (err, obj) {
var c = obj && obj.c; // content
if (!c) {
waitFor.abort();
cb({error: 'NOCACHE_DRIVE'});
}
}));
Cache.getChannelCache(rosterKeys.channel, waitFor(function (err, obj) {
var c = obj && obj.c; // content
var k = obj && obj.k;
if (k && !rosterKeys.teamEdPublic) {
rosterKeys.teamEdPublic = k;
}
if (!c) {
waitFor.abort();
cb({error: 'NOCACHE_ROSTER'});
}
}));
return;
}
checkTeamChannels(ctx, id, secret.channel, rosterKeys.channel, waitFor, cb);
}).nThen(function (waitFor) {
var cacheRdy = {
lm: false,
roster: false,
check: function () {
if (!this.lm || !this.roster) { return; }
if (!cache) { return; }
// Both are cacheready!
ctx.progress += 30/ctx.numberOfTeams;
ctx.updateProgress({
progress: ctx.progress
});
onCacheReady(ctx, id, lm, roster, keys, null, waitFor(cb));
this.check = function () {};
}
};
// Load the proxy
var cfg = {
data: {},
readOnly: !Boolean(secret.keys.signKey),
network: ctx.store.network,
network: ctx.store.network || ctx.store.networkPromise,
channel: secret.channel,
crypto: crypto,
ChainPad: ChainPad,
Cache: Cache, // ICE team cache
Cache: Cache,
metadata: {
validateKey: secret.keys.validateKey || undefined,
},
@ -441,6 +523,10 @@ define([
ctx.emit('ROSTER_CHANGE', id, team.clients);
};
lm = Listmap.create(cfg);
lm.proxy.on('cacheready', function () {
cacheRdy.lm = true;
cacheRdy.check();
});
lm.proxy.on('ready', waitFor());
lm.proxy.on('error', function (info) {
if (info && typeof (info.loaded) !== "undefined" && !info.loaded) {
@ -455,11 +541,17 @@ define([
// Load the roster
Roster.create({
network: ctx.store.network,
network: ctx.store.network || ctx.store.networkPromise,
channel: rosterKeys.channel,
keys: rosterKeys,
anon_rpc: ctx.store.anon_rpc,
store: ctx.store,
lastKnownHash: rosterData.lastKnownHash,
onCacheReady: function (_roster) {
roster = _roster;
cacheRdy.roster = true;
cacheRdy.check();
},
Cache: Cache
}, waitFor(function (err, _roster) {
if (err) {
waitFor.abort();
@ -476,16 +568,18 @@ define([
var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
if (!state.members[me]) { return; }
// If you're allowed to edit the roster, try to update your data
if (!rosterData.edit) { return; }
var data = {};
var myData = Messaging.createData(ctx.store.proxy, false);
myData.pending = false;
data[ctx.store.proxy.curvePublic] = myData;
roster.describe(data, function (err) {
if (!err) { return; }
if (err === 'NO_CHANGE') { return; }
console.error(err);
onStoreReady.reg(function () {
// If you're allowed to edit the roster, try to update your data
if (!rosterData.edit) { return; }
var data = {};
var myData = Messaging.createData(ctx.store.proxy, false);
myData.pending = false;
data[ctx.store.proxy.curvePublic] = myData;
roster.describe(data, function (err) {
if (!err) { return; }
if (err === 'NO_CHANGE') { return; }
console.error(err);
});
});
}));
}).nThen(function (waitFor) {
@ -538,10 +632,12 @@ define([
Feedback.send("TEAM_RIGHTS_OWNER");
}
}).nThen(function () {
ctx.progress += 30/ctx.numberOfTeams;
ctx.updateProgress({
progress: ctx.progress
});
if (!cache) {
ctx.progress += 30/ctx.numberOfTeams;
ctx.updateProgress({
progress: ctx.progress
});
}
onReady(ctx, id, lm, roster, keys, null, cb);
});
};
@ -582,12 +678,13 @@ define([
nThen(function (waitFor) {
// Initialize the roster
Roster.create({
network: ctx.store.network,
network: ctx.store.network || ctx.store.networkPromise,
channel: rosterKeys.channel, //sharedConfig.rosterChannel,
owners: [ctx.store.proxy.edPublic],
keys: rosterKeys,
anon_rpc: ctx.store.anon_rpc,
store: ctx.store,
lastKnownHash: void 0,
Cache: Cache
}, waitFor(function (err, _roster) {
if (err) {
waitFor.abort();
@ -898,13 +995,15 @@ define([
}
// Add online status (using messenger data)
var chatData = team.getChatData();
var online = ctx.store.messenger.getOnlineList(chatData.channel) || [];
online.forEach(function (curve) {
if (members[curve]) {
members[curve].online = true;
}
});
if (ctx.store.messenger) {
var chatData = team.getChatData();
var online = ctx.store.messenger.getOnlineList(chatData.channel) || [];
online.forEach(function (curve) {
if (members[curve]) {
members[curve].online = true;
}
});
}
cb(members);
};
@ -945,6 +1044,7 @@ define([
if (!teamId) { return void cb({error: 'EINVAL'}); }
var team = ctx.teams[teamId];
if (!team) { return void cb ({error: 'ENOENT'}); }
if (team.offline) { return void cb({error: 'OFFLINE'}); }
if (!team.roster) { return void cb({error: 'NO_ROSTER'}); }
team.roster.metadata(data.metadata, function (err) {
if (err) { return void cb({error: err}); }
@ -1358,10 +1458,11 @@ define([
try {
ctx.store.messenger.removeClient(cId);
} catch (e) {}
openCachedTeamChat = function () {};
if (!id) { return void cb(); }
// If the team is loading, as ourselves in the list
if (ctx.onReadyHandlers[id]) {
// If the team is loading, add ourselves to the list
if (ctx.onReadyHandlers[id] && !ctx.teams[id]) {
var _idx = ctx.onReadyHandlers[id].indexOf(cId);
if (_idx === -1) {
ctx.onReadyHandlers[id].push({
@ -1389,7 +1490,13 @@ define([
var onUpdate = function () {
ctx.emit('ROSTER_CHANGE', data.teamId, team.clients);
};
ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb);
if (ctx.store.messenger) {
ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb);
} else {
openCachedTeamChat = function () {
ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb);
};
}
};
var createInviteLink = function (ctx, data, cId, _cb) {
@ -1630,10 +1737,11 @@ define([
}
var rosterKeys = Crypto.Team.deriveMemberKeys(rosterData.edit, myKeys);
Roster.create({
network: ctx.store.network,
network: ctx.store.network || ctx.store.networkPromise,
channel: rosterData.channel,
keys: rosterKeys,
anon_rpc: ctx.store.anon_rpc,
store: ctx.store,
Cache: Cache
}, waitFor(function (err, roster) {
if (err) {
waitFor.abort();
@ -1705,6 +1813,7 @@ define([
emit: emit,
onReadyHandlers: {},
teams: {},
cache: {},
updateMetadata: cfg.updateMetadata,
updateProgress: cfg.updateLoadingProgress,
progress: 0
@ -1735,60 +1844,62 @@ define([
};
// Remove duplicate teams
var _teams = {};
Object.keys(teams).forEach(function (id) {
try {
var t = teams[id];
var _t = _teams[t.channel];
var edPrivate = Util.find(t, ['keys', 'drive', 'edPrivate']);
var edPublic = Util.find(t, ['keys', 'drive', 'edPublic']);
// If the edPrivate is corrupted, remove it
if (!edPublic) {
Feedback.send("TEAM_CORRUPTED_EDPUBLIC");
} else if (edPrivate && edPublic && !checkKeyPair(edPrivate, edPublic)) {
Feedback.send("TEAM_CORRUPTED_EDPRIVATE");
delete teams[id].keys.drive.edPrivate;
edPrivate = undefined;
}
var removeDuplicates = function () {
var _teams = {};
Object.keys(teams).forEach(function (id) {
try {
var t = teams[id];
var _t = _teams[t.channel];
var edPrivate = Util.find(t, ['keys', 'drive', 'edPrivate']);
var edPublic = Util.find(t, ['keys', 'drive', 'edPublic']);
// If the edPrivate is corrupted, remove it
if (!edPublic) {
Feedback.send("TEAM_CORRUPTED_EDPUBLIC");
} else if (edPrivate && edPublic && !checkKeyPair(edPrivate, edPublic)) {
Feedback.send("TEAM_CORRUPTED_EDPRIVATE");
delete teams[id].keys.drive.edPrivate;
edPrivate = undefined;
}
// If the hash is corrupted, feedback
if (t.hash) {
var parsed = Hash.parseTypeHash('drive', t.hash);
if (parsed.version === 2 && t.hash.length !== 40) {
Feedback.send("TEAM_CORRUPTED_HASH");
// FIXME ?
// If the hash is corrupted, feedback
if (t.hash) {
var parsed = Hash.parseTypeHash('drive', t.hash);
if (parsed.version === 2 && t.hash.length !== 40) {
Feedback.send("TEAM_CORRUPTED_HASH");
// FIXME ?
}
}
}
// Not found yet? add to the list
if (!_t) {
_teams[t.channel] = id;
return;
}
// Not found yet? add to the list
if (!_t) {
_teams[t.channel] = id;
return;
}
// Duplicate found: update our team to add missing data
var best = teams[_t]; // This is a proxy!
var bestPrivate = Util.find(best, ['keys', 'drive', 'edPrivate']);
var bestChat = Util.find(best, ['keys', 'chat', 'edit']);
var chat = Util.find(t, ['keys', 'chat', 'edit']);
if (!best.hash && t.hash) {
best.hash = t.hash;
}
if (!bestPrivate && edPrivate) {
best.keys.drive.edPrivate = edPrivate;
}
if (!bestChat && chat) {
best.keys.chat.edit = chat;
}
// Duplicate found: update our team to add missing data
var best = teams[_t]; // This is a proxy!
var bestPrivate = Util.find(best, ['keys', 'drive', 'edPrivate']);
var bestChat = Util.find(best, ['keys', 'chat', 'edit']);
var chat = Util.find(t, ['keys', 'chat', 'edit']);
if (!best.hash && t.hash) {
best.hash = t.hash;
}
if (!bestPrivate && edPrivate) {
best.keys.drive.edPrivate = edPrivate;
}
if (!bestChat && chat) {
best.keys.chat.edit = chat;
}
// Deprecate the duplicate
ctx.store.proxy.duplicateTeams = ctx.store.proxy.duplicateTeams || {};
ctx.store.proxy.duplicateTeams[id] = teams[id];
delete teams[id];
} catch (e) { console.error(e); }
});
// Deprecate the duplicate
ctx.store.proxy.duplicateTeams = ctx.store.proxy.duplicateTeams || {};
ctx.store.proxy.duplicateTeams[id] = teams[id];
delete teams[id];
} catch (e) { console.error(e); }
});
};
// Load teams
Object.keys(teams).forEach(function (id) {
@ -1798,14 +1909,69 @@ define([
}
openChannel(ctx, teams[id], id, waitFor(function (err) {
if (err) {
delete ctx.onReadyHandlers[id];
delete ctx.cache[id];
var txt = typeof(err) === "string" ? err : (err.type || err.message);
Feedback.send("TEAM_LOADING_ERROR="+txt);
return void console.error(err);
}
console.debug('Team '+id+' ready');
}));
console.debug('Team '+id+' cache ready');
}), true);
});
// Proxy is ready, check if our team list has changed
team.onReady = function (waitFor) {
removeDuplicates();
// Close all the teams from our cache that have been removed and add waitFor to the
// one that still exist
var checkTeam = function (id) {
if (!teams[id]) {
closeTeam(ctx, id);
delete ctx.onReadyHandlers[id];
return true;
}
return false;
};
Object.keys(ctx.teams).forEach(checkTeam);
Object.keys(ctx.onReadyHandlers).forEach(function (id) {
var closed = checkTeam(id);
if (closed) { return; }
var team = ctx.store.proxy.teams[id];
var rosterChan = Util.find(team, ['keys', 'roster', 'channel']);
var _cb = Util.once(Util.mkAsync(waitFor()));
nThen(function (w) {
checkTeamChannels(ctx, id, team.channel, rosterChan, w, _cb);
});
ctx.onReadyHandlers[id].push({
cb: _cb
});
});
// Load all the teams that weren't in our cache
Object.keys(teams).forEach(function (id) {
// Team already loaded? abort
if (ctx.onReadyHandlers[id] || ctx.teams[id]) { return; }
// Load team
ctx.onReadyHandlers[id] = [];
if (!Util.find(teams, [id, 'keys', 'mailbox'])) {
teams[id].keys.mailbox = deriveMailbox(teams[id]);
}
openChannel(ctx, teams[id], id, waitFor(function (err) {
if (err) {
var txt = typeof(err) === "string" ? err : (err.type || err.message);
Feedback.send("TEAM_LOADING_ERROR="+txt);
return void console.error(err);
}
console.debug('Team '+id+' ready');
}));
});
openCachedTeamChat();
onStoreReady.fire();
};
team.getTeam = function (id) {
return ctx.teams[id];
};

@ -24,6 +24,21 @@
.export_main();
&:not(.cp-app-team-offline) {
.cp-offline {
display: none !important;
}
}
&.cp-app-team-offline {
.cp-online {
display: none !important;
}
.cp-online-alt {
cursor: not-allowed;
opacity: 0.5;
}
}
#cp-sidebarlayout-container {
@media screen and (max-width: 900px) {
.cp-app-drive-toolbar-leftside {

@ -171,12 +171,15 @@ define([
'cp-team-drive'
],
'members': [
'cp-team-offline',
'cp-team-roster'
],
'chat': [
'cp-team-offline',
'cp-team-chat'
],
'admin': [
'cp-team-offline',
'cp-team-edpublic',
'cp-team-name',
'cp-team-avatar',
@ -285,10 +288,12 @@ define([
var buildUI = APP.buildUI = function (common, team, teamAdmin) {
var $rightside = APP.$rightside;
$rightside.empty();
var added = [];
var addItem = function (cssClass) {
var item = cssClass.slice(8);
if (typeof (create[item]) === "function") {
if (typeof (create[item]) === "function" && added.indexOf(item) < 0) {
$rightside.append(create[item](common));
added.push(item);
}
};
var categories = team ? teamCategories : mainCategories;
@ -393,6 +398,14 @@ define([
var t = Messages._getKey('team_title', [Util.fixHTML(team.metadata.name)]);
sframeChan.query('Q_SET_TEAM', id, function (err) {
if (err) { return void console.error(err); }
// Set editable
var metadataMgr = common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData();
if (team.offline) {
APP.$body.addClass('cp-app-team-offline');
} else if (!privateData.offline) {
APP.$body.removeClass('cp-app-team-offline');
}
// Change title
$('.cp-toolbar-title-value').text(t);
sframeChan.event('EV_SET_TAB_TITLE', t);
@ -431,7 +444,6 @@ define([
content.push(h('h3', Messages.team_listTitle + ' ' + slots));
console.error(createSlots, Constants);
APP.teams = {};
var created = 0;
@ -535,9 +547,10 @@ define([
name: name
}, function (obj) {
if (obj && obj.error) {
if (obj.error === "OFFLINE") { return UI.alert(Messages.driveOfflineError); }
console.error(obj.error);
$spinner.hide();
state = false;
if (obj.error === "OFFLINE") { return UI.alert(Messages.disconnected); } // XXX
console.error(obj.error);
return void UI.warn(Messages.error);
}
// Redraw the create block
@ -701,7 +714,7 @@ define([
// Status
var status = h('span.cp-team-member-status'+(data.online ? '.online' : ''));
// Actions
var actions = h('span.cp-team-member-actions');
var actions = h('span.cp-online.cp-team-member-actions');
var $actions = $(actions);
var isMe = me && me.curvePublic === data.curvePublic;
var myRole = me ? (ROLES.indexOf(me.role) || 1) : -1;
@ -867,7 +880,7 @@ define([
// If you're an admin or an owner, you can invite your friends to the team
// TODO and acquaintances later?
if (me && (me.role === 'ADMIN' || me.role === 'OWNER')) {
var invite = h('button.btn.btn-primary', Messages.team_inviteButton);
var invite = h('button.cp-online.btn.btn-primary', Messages.team_inviteButton);
var inviteFriends = common.getFriends();
Object.keys(inviteFriends).forEach(function (curve) {
// Keep only friends that are not already in the team and that you can contact
@ -889,7 +902,7 @@ define([
}
if (me && (me.role !== 'OWNER')) {
var leave = h('button.btn.btn-danger', Messages.team_leaveButton);
var leave = h('button.cp-online.btn.btn-danger', Messages.team_leaveButton);
$(leave).click(function () {
UI.confirm(Messages.team_leaveConfirm, function (yes) {
if (!yes) { return; }
@ -938,6 +951,11 @@ define([
cb(content);
});
makeBlock('offline', function (common, cb, $div) {
$div.addClass('cp-offline');
cb(h('div.alert.alert-danger', Messages.disconnected));
});
makeBlock('chat', function (common, cb) {
var container = h('div#cp-app-contacts-container.cp-app-contacts-inapp');
var content = [container];
@ -945,6 +963,9 @@ define([
teamId: APP.team
}, function (obj) {
if (obj && obj.error) {
if (obj.error === 'OFFLINE') {
return; // XXX show offline message in chat section
}
return void UI.alert(Messages.error);
}
common.setTeamChat(obj.channel);
@ -983,7 +1004,7 @@ define([
'type': 'text',
'id': 'cp-settings-displayname',
'placeholder': Messages.anonymous}).appendTo($inputBlock);
var $save = $('<button>', {'class': 'btn btn-primary'}).text(Messages.settings_save).appendTo($inputBlock);
var $save = $('<button>', {'class': 'cp-online-alt btn btn-primary'}).text(Messages.settings_save).appendTo($inputBlock);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide();
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide();
@ -996,12 +1017,20 @@ define([
teamId: APP.team
}, function (obj) {
if (obj && obj.error) { return void UI.warn(Messages.error); }
var oldName = obj.name;
obj.name = newName;
APP.module.execCommand('SET_TEAM_METADATA', {
teamId: APP.team,
metadata: obj
}, function () {
}, function (res) {
$spinner.hide();
if (res && res.error) {
$input.val(oldName);
if (res.error === 'OFFLINE') {
return void UI.warn(Messages.disconnected);
}
return void UI.warn(Messages.error);
}
$ok.show();
});
});
@ -1049,6 +1078,7 @@ define([
});
});
var $upButton = common.createButton('upload', false, data);
$upButton.addClass('cp-online');
$upButton.removeProp('title');
$upButton.text(Messages.profile_upload);
$upButton.prepend($('<span>', {'class': 'fa fa-upload'}));
@ -1124,7 +1154,8 @@ define([
cb(button);
}, true);
makeBlock('delete', function (common, cb) {
makeBlock('delete', function (common, cb, $div) {
$div.addClass('cp-online');
var deleteTeam = h('button.btn.btn-danger', Messages.team_deleteButton);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide();
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide();
@ -1461,7 +1492,7 @@ define([
delete mainCategories.create;
}
$('body').css('display', '');
var $body = APP.$body = $('body').css('display', '');
loadMain(common);
metadataMgr.onChange(function () {
@ -1488,6 +1519,7 @@ define([
var onDisconnect = function (teamId) {
if (APP.team && teamId && APP.team !== teamId) { return; }
setEditable(false);
$body.addClass('cp-app-team-offline');
if (APP.team && driveAPP.refresh) { driveAPP.refresh(); }
toolbar.failed();
UIElements.disconnectAlert();
@ -1495,6 +1527,7 @@ define([
var onReconnect = function (teamId) {
if (APP.team && teamId && APP.team !== teamId) { return; }
setEditable(true);
$body.removeClass('cp-app-team-offline');
if (APP.team && driveAPP.refresh) { driveAPP.refresh(); }
toolbar.reconnecting();
UIElements.reconnectAlert();

@ -114,6 +114,7 @@ define([
addRpc: addRpc,
addData: addData,
isDrive: true, // Used for history...
cache: true,
});
});
});

@ -14,8 +14,29 @@ define([
var canvas = new Fabric.Canvas(canvas_node);
var content = userDoc.content;
canvas.loadFromJSON(content, function () {
var w = 0;
var h = 0;
var MAX = 8192;
canvas.forEachObject(function (obj) {
var c = obj.getCoords();
Object.keys(c).forEach(function (k) {
if (c[k].x > w) { w = c[k].x + 1; }
if (c[k].y > h) { h = c[k].y + 1; }
});
});
w = Math.min(w, MAX);
h = Math.min(h, MAX);
canvas.setWidth(w);
canvas.setHeight(h);
canvas.calcOffset();
module.ext = '.png';
canvas_node.toBlob(cb);
/*
module.ext = '.svg';
cb(canvas.toSVG());
cb(new Blob([canvas.toSVG()], {type: 'image/svg+xml'}));
*/
});
};

Loading…
Cancel
Save