/** * 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(); });