You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
578 lines
16 KiB
578 lines
16 KiB
/**
|
|
* Plugin for embed mode in Nextcloud
|
|
*/
|
|
Draw.loadPlugin(function(ui)
|
|
{
|
|
var loadDescriptor = null;
|
|
var ncUser = null;
|
|
|
|
mxEvent.addListener(window, 'message', mxUtils.bind(this, function(evt)
|
|
{
|
|
var data = evt.data;
|
|
|
|
try
|
|
{
|
|
data = JSON.parse(data);
|
|
|
|
if (data.action == 'load')
|
|
{
|
|
if (data.desc != null)
|
|
{
|
|
loadDescriptor = data.desc;
|
|
}
|
|
|
|
if (data.disableAutoSave)
|
|
{
|
|
ui.editor.setAutosave(false);
|
|
}
|
|
}
|
|
}
|
|
catch (e)
|
|
{
|
|
// Ignore
|
|
}
|
|
}));
|
|
|
|
ui.getCurrentUser = function()
|
|
{
|
|
if (ncUser == null)
|
|
{
|
|
ui.remoteInvoke('getCurrentUser', null, null, function(user)
|
|
{
|
|
ncUser = user == null? new DrawioUser(Date.now(), null, 'Anonymous')
|
|
: new DrawioUser(user.uid, null, user.displayName);
|
|
}, function()
|
|
{
|
|
//ignore such that next call we retry
|
|
});
|
|
|
|
//Return a dummy user until we have the actual user in order for UI to be populated
|
|
return new DrawioUser(Date.now(), null, 'Anonymous');
|
|
}
|
|
|
|
return ncUser;
|
|
};
|
|
|
|
//======================== Revisions ========================
|
|
|
|
ui.isRevisionHistoryEnabled = function()
|
|
{
|
|
var file = ui.getCurrentFile();
|
|
return file && file.desc && (!file.desc.ver || file.desc.versionsEnabled);
|
|
};
|
|
|
|
ui.isRevisionHistorySupported = function()
|
|
{
|
|
return ui.isRevisionHistoryEnabled();
|
|
};
|
|
|
|
/**
|
|
* Get revisions of current file
|
|
*/
|
|
ui.getRevisions = function(success, error)
|
|
{
|
|
var desc = ui.getCurrentFile().desc;
|
|
var id = desc.ver > 1? desc.id : desc.path;
|
|
|
|
function getXml(success, error)
|
|
{
|
|
ui.remoteInvoke('loadFileVersion', [id, this.revId], null, success, error);
|
|
};
|
|
|
|
function restoreFn(xml)
|
|
{
|
|
if (ui.spinner.spin(document.body, mxResources.get('restoring')))
|
|
{
|
|
ui.tryAndHandle(function()
|
|
{
|
|
ui.replaceFileData(xml);
|
|
ui.spinner.stop();
|
|
ui.hideDialog();
|
|
});
|
|
}
|
|
};
|
|
|
|
ui.remoteInvoke('getFileRevisions', [id], null, function(revisions)
|
|
{
|
|
revisions.sort(function(a, b)
|
|
{
|
|
return a.timestamp - b.timestamp;
|
|
});
|
|
|
|
//convert to editor format and add getXml function
|
|
var revs = [];
|
|
|
|
for (var i = 0; i < revisions.length; i++)
|
|
{
|
|
var rev = revisions[i];
|
|
rev.modifiedDate = rev.timestamp * 1000;
|
|
rev.getXml = mxUtils.bind(rev, getXml);
|
|
revs.push(rev);
|
|
}
|
|
|
|
success(revs, restoreFn);
|
|
}, error);
|
|
};
|
|
|
|
//============= Embed File with real-time collab support =================
|
|
// Use optimistic sync since we cannot save file properties/metadata so far
|
|
|
|
/**
|
|
* Shorter autosave delay for optimistic sync.
|
|
*/
|
|
EmbedFile.prototype.autosaveDelay = 500;
|
|
|
|
/**
|
|
* Delay for last save in ms.
|
|
*/
|
|
EmbedFile.prototype.saveDelay = 0;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.isConflict = function(err)
|
|
{
|
|
return err != null && err.status == 409;
|
|
};
|
|
|
|
/**
|
|
* Returns the current user.
|
|
*/
|
|
EmbedFile.prototype.getCurrentUser = function()
|
|
{
|
|
return ui.getCurrentUser();
|
|
};
|
|
|
|
EmbedFile.prototype.isRealtimeSupported = function()
|
|
{
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.save = function(revision, success, error, unloading, overwrite)
|
|
{
|
|
this.saveStarted = true;
|
|
|
|
DrawioFile.prototype.save.apply(this, [revision, mxUtils.bind(this, function()
|
|
{
|
|
this.saveFile(null, revision, success, error, unloading, overwrite);
|
|
this.saveStarted = false;
|
|
}), error, unloading, overwrite]);
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.setModified = function(value)
|
|
{
|
|
DrawioFile.prototype.setModified.apply(this, arguments);
|
|
|
|
//Set editor modified also to prevent accidental closure or exiting without saving
|
|
ui.editor.modified = value;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.saveFile = function(title, revision, success, error, unloading, overwrite)
|
|
{
|
|
EditorUi.debug('EmbedFile.saveFile', [this], 'saving', this.savingFile);
|
|
|
|
try
|
|
{
|
|
if (!this.isEditable())
|
|
{
|
|
if (success != null)
|
|
{
|
|
success();
|
|
}
|
|
}
|
|
else if (!this.savingFile)
|
|
{
|
|
// Sets shadow modified state during save
|
|
this.savingFileTime = new Date();
|
|
this.setShadowModified(false);
|
|
this.savingFile = true;
|
|
|
|
|
|
var doSave = mxUtils.bind(this, function()
|
|
{
|
|
try
|
|
{
|
|
var lastDesc = this.desc;
|
|
var savedData = this.getData();
|
|
var etag = this.getCurrentEtag();
|
|
|
|
if (this.sync != null)
|
|
{
|
|
this.sync.fileSaving();
|
|
}
|
|
|
|
ui.remoteInvoke('saveFile', this.desc.ver > 1? [this.desc.id, this.desc.shareToken, savedData, etag] :
|
|
[this.desc.path, savedData, etag], null, mxUtils.bind(this, function(resp)
|
|
{
|
|
try
|
|
{
|
|
// Checks for changes during save
|
|
this.setModified(this.getShadowModified());
|
|
this.savingFile = false;
|
|
this.desc = Object.assign({}, this.desc); // Clone the object
|
|
Object.assign(this.desc, resp); // Assign the new values
|
|
|
|
this.fileSaved(savedData, lastDesc, mxUtils.bind(this, function()
|
|
{
|
|
this.contentChanged();
|
|
|
|
if (success != null)
|
|
{
|
|
success();
|
|
}
|
|
}), error);
|
|
}
|
|
catch (e)
|
|
{
|
|
this.savingFile = false;
|
|
|
|
if (error != null)
|
|
{
|
|
error(e);
|
|
}
|
|
else
|
|
{
|
|
throw e;
|
|
}
|
|
}
|
|
}),
|
|
mxUtils.bind(this, function(err)
|
|
{
|
|
try
|
|
{
|
|
this.savingFile = false;
|
|
|
|
if (this.isConflict(err))
|
|
{
|
|
this.inConflictState = true;
|
|
|
|
if (this.sync != null)
|
|
{
|
|
this.savingFile = true;
|
|
|
|
this.sync.fileConflict(null, mxUtils.bind(this, function()
|
|
{
|
|
// Adds random cool-off
|
|
var delay = 100 + Math.random() * 500;
|
|
window.setTimeout(mxUtils.bind(this, function()
|
|
{
|
|
this.updateFileData();
|
|
doSave();
|
|
}), delay);
|
|
|
|
EditorUi.debug('EmbedFile.saveFile.conflict',
|
|
[this], 'err', err, 'delay', delay);
|
|
}), mxUtils.bind(this, function()
|
|
{
|
|
this.savingFile = false;
|
|
|
|
if (error != null)
|
|
{
|
|
error();
|
|
}
|
|
}));
|
|
}
|
|
else if (error != null)
|
|
{
|
|
error();
|
|
}
|
|
}
|
|
else if (error != null)
|
|
{
|
|
error(err);
|
|
}
|
|
}
|
|
catch (e)
|
|
{
|
|
this.savingFile = false;
|
|
|
|
if (error != null)
|
|
{
|
|
error(e);
|
|
}
|
|
else
|
|
{
|
|
throw e;
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
catch (e)
|
|
{
|
|
this.savingFile = false;
|
|
|
|
if (error != null)
|
|
{
|
|
error(e);
|
|
}
|
|
else
|
|
{
|
|
throw e;
|
|
}
|
|
}
|
|
});
|
|
|
|
doSave();
|
|
}
|
|
}
|
|
catch (e)
|
|
{
|
|
if (error != null)
|
|
{
|
|
error(e);
|
|
}
|
|
else
|
|
{
|
|
throw e;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.getTitle = function()
|
|
{
|
|
return this.desc.name;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.getHash = function()
|
|
{
|
|
return 'E' + this.getId();
|
|
};
|
|
|
|
/**
|
|
* Overridden to enable the autosave option in the document properties dialog.
|
|
*/
|
|
EmbedFile.prototype.isAutosaveOptional = function()
|
|
{
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.getId = function()
|
|
{
|
|
return this.desc.instanceId + '$$' + this.desc.id;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.isSyncSupported = function()
|
|
{
|
|
return this.desc != null && this.desc.id != null && this.desc.instanceId != null;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.isRevisionHistorySupported = function()
|
|
{
|
|
return true;
|
|
};
|
|
|
|
EmbedFile.prototype.isOptimisticSync = function()
|
|
{
|
|
return true;
|
|
};
|
|
EmbedFile.prototype.getSize = function()
|
|
{
|
|
return this.desc.size;
|
|
};
|
|
|
|
EmbedFile.prototype.isEditable = function()
|
|
{
|
|
return this.desc != null && this.desc.writeable;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.getLatestVersion = function(success, error)
|
|
{
|
|
ui.remoteInvoke('loadFile', this.desc.ver > 1? [this.desc.id, this.desc.shareToken] : [this.desc.path],
|
|
null, mxUtils.bind(this, function(data)
|
|
{
|
|
var xml = data.xml;
|
|
delete data.xml;
|
|
success(new EmbedFile(ui, xml, data));
|
|
}), error);
|
|
};
|
|
|
|
/**
|
|
* Gets the channel ID from the given descriptor.
|
|
*/
|
|
EmbedFile.prototype.getChannelId = function()
|
|
{
|
|
return 'C-' + DrawioFile.prototype.getChannelId.apply(this, arguments);
|
|
};
|
|
|
|
EmbedFile.prototype.getHash = function()
|
|
{
|
|
return 'C' + encodeURIComponent(this.getId());
|
|
};
|
|
|
|
/**
|
|
* Using MD5 of create timestamp and user ID as crypto key.
|
|
*/
|
|
EmbedFile.prototype.getChannelKey = function()
|
|
{
|
|
if (typeof CryptoJS !== 'undefined')
|
|
{
|
|
return CryptoJS.MD5(this.desc.instanceId + this.desc.id).toString();
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.getLastModifiedDate = function()
|
|
{
|
|
return new Date(this.desc.mtime * 1000);
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.getDescriptor = function()
|
|
{
|
|
return this.desc;
|
|
};
|
|
|
|
/**
|
|
* Updates the descriptor of this file with the one from the given file.
|
|
*/
|
|
EmbedFile.prototype.setDescriptor = function(desc)
|
|
{
|
|
this.desc = desc;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.getDescriptorEtag = function(desc)
|
|
{
|
|
return desc.etag;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.setDescriptorEtag = function(desc, etag)
|
|
{
|
|
desc.etag = etag;
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EmbedFile.prototype.loadDescriptor = function(success, error)
|
|
{
|
|
ui.remoteInvoke('getFileInfo', this.desc.ver > 1? [this.desc.id, this.desc.shareToken] : [this.desc.path], null, success, error);
|
|
};
|
|
|
|
var allowAutoSave = true;
|
|
|
|
EmbedFile.prototype.isAutosaveNow = function(success, error)
|
|
{
|
|
return allowAutoSave;
|
|
};
|
|
|
|
//Ensure saving is via the file
|
|
ui.actions.get('save').funct = function(exit)
|
|
{
|
|
if (ui.editor.graph.isEditing())
|
|
{
|
|
ui.editor.graph.stopEditing();
|
|
}
|
|
|
|
var curFile = ui.getCurrentFile();
|
|
|
|
if (exit)
|
|
{
|
|
allowAutoSave = false;
|
|
}
|
|
|
|
function doActions()
|
|
{
|
|
if (exit)
|
|
{
|
|
ui.actions.get('exit').funct();
|
|
}
|
|
};
|
|
|
|
function doSave()
|
|
{
|
|
if (curFile.saveStarted || curFile.savingFile)
|
|
{
|
|
setTimeout(doSave, 100);
|
|
return;
|
|
}
|
|
|
|
if (curFile.isModified())
|
|
{
|
|
ui.saveFile(null, doActions);
|
|
}
|
|
else
|
|
{
|
|
doActions();
|
|
}
|
|
};
|
|
|
|
doSave();
|
|
};
|
|
|
|
//Add file opening here (or it should be for all in EditorUi?)
|
|
var origInstallMessageHandler = ui.installMessageHandler;
|
|
|
|
ui.installMessageHandler = function(callback)
|
|
{
|
|
origInstallMessageHandler.call(this, function(xml, evt)
|
|
{
|
|
try
|
|
{
|
|
// New empty files
|
|
if (JSON.parse(evt.data).xml == ' ') return;
|
|
}
|
|
catch (e) {} // Ignore
|
|
|
|
callback.apply(this, arguments);
|
|
|
|
var file = ui.getCurrentFile();
|
|
loadDescriptor = loadDescriptor || {};
|
|
|
|
// New files call this twice, so we need to check if the file is loaded
|
|
if (!loadDescriptor.isLoaded)
|
|
{
|
|
loadDescriptor.isLoaded = true;
|
|
file.setDescriptor(loadDescriptor);
|
|
ui.fileLoaded(file, true);
|
|
}
|
|
});
|
|
}
|
|
|
|
ui.editor.setModified = function()
|
|
{
|
|
//Cancel set modified of the editor and use the file's one
|
|
};
|
|
|
|
//Prefetch current user
|
|
ui.getCurrentUser();
|
|
});
|