12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535 |
- // Renamed from ac.js. This is the version used for release 1.4.8-AC onwards
- var AC = {};
- AC.autosaveTimeout = 10000;
- AC.draftExtension = '.tmp';
- AC.draftPrefix = '~';
- AC.timeout = 25000;
- // If save should also exit. To disable this, multiple saveMacro calls
- // must be possible (not yet in production on 08-AUG-2017)
- AC.autoExit = true;
- // Last Checked on 08-AUG-2017: No delete scope needed to delete drafts
- // LATER: If delete scope is needed users must upgrade to the latest json
- // Disabled. Flag to mute notifications for drafts is needed. 16-AUG-2017
- AC.draftEnabled = true; //Enabled with the new save that mute notifications for saving TODO is there notification for deleting a draft?
- AC.getUrlParam = function(param, escape, url){
- try{
- var url = url || window.location.search;
- var regex = new RegExp(param + '=([^&]+)'),
- data = regex.exec(url)[1];
- // decode URI with plus sign fix.
- return (escape) ? window.decodeURIComponent(data.replace(/\+/g, '%20')) : data;
- } catch (e){
- return undefined;
- }
- };
- AC.getSpaceKey = function(url)
- {
- try{
- var url = url || window.location.href;
- var regex = new RegExp(/\/(spaces|space)\/([^\/]+)/);
- return regex.exec(url)[2];
- } catch (e){
- return undefined;
- }
- };
- AC.getMetaTag = function(name) {
- return document.getElementsByTagName('meta')[name].getAttribute('content');
- }
- //Code from: https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
- AC.b64toBlob = function(b64Data, contentType, sliceSize, isByteCharacters)
- {
- contentType = contentType || '';
- sliceSize = sliceSize || 512;
- var byteCharacters = isByteCharacters? b64Data : atob(b64Data);
- var byteArrays = [];
- for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
- var slice = byteCharacters.slice(offset, offset + sliceSize);
- var byteNumbers = new Array(slice.length);
- for (var i = 0; i < slice.length; i++) {
- byteNumbers[i] = slice.charCodeAt(i);
- }
- var byteArray = new Uint8Array(byteNumbers);
- byteArrays.push(byteArray);
- }
- var blob = new Blob(byteArrays, {type: contentType});
- return blob;
- }
- AC.initAsync = function(baseUrl)
- {
- var link = document.createElement('a');
- link.href = location.href;
- link.href = link.href; //to have 'host' populated under IE
- var hostUrl = link.protocol + '//' + link.hostname;
- var lang = AC.getUrlParam('loc', true);
- var site = AC.getUrlParam('xdm_e', true);
- var user = AC.getUrlParam('user_id', true);
- if (lang != null)
- {
- var dash = lang.indexOf('-');
-
- if (dash >= 0)
- {
- lang = lang.substring(0, dash);
- }
- }
- var editor = document.createElement('iframe');
- editor.setAttribute('width', '100%');
- editor.setAttribute('height', '100%');
- editor.style.width = '100%';
- editor.style.height = '100%';
- editor.setAttribute('id', 'editorFrame');
- editor.setAttribute('frameborder', '0');
- //editor.setAttribute('src', 'https://9674265b.ngrok.io/?dev=1&drawdev=1&' +
- editor.setAttribute('src', hostUrl + '/?' +
- 'ui=atlas&p=ac148&embed=1&modified=unsavedChanges' +
- ((!AC.autoExit) ? '&saveAndExit=1' : '') +
- '&keepmodified=1&spin=1&libraries=1&proto=json' +
- ((lang != null) ? '&lang=' + lang : '') + ((site != null) ? '&site=' + encodeURIComponent(site) : '') +
- ((user != null) ? '&user=' + encodeURIComponent(user) : ''));
- var initReceived = false;
- var draftHandled = false;
- var waitingForAttachments = false;
- var xmlReceived = null;
- var draftXml = null;
- var draftName = null;
- var filename = null;
- var theMacroData = null;
- var pageId = null;
- var draftPage = false;
- var theLocation = null;
- var attachments = null;
- var serverName = document.referrer;
- var index1 = serverName.indexOf('//');
- if (index1 > 0)
- {
- var index2 = serverName.indexOf('/', index1 + 2);
-
- if (index2 > index1)
- {
- serverName = serverName.substring(index1 + 2, index2);
- }
- }
-
- function startEditor()
- {
- if (initReceived && xmlReceived != null && draftHandled && !waitingForAttachments)
- {
- AC.init(baseUrl, theLocation, pageId, editor, filename, xmlReceived, draftName, draftXml, theMacroData, draftPage);
- }
- };
-
- function loadDraft()
- {
- if (waitingForAttachments)
- {
- return;
- }
-
- if (AC.draftEnabled && pageId != null && attachments != null &&
- (draftName != null || xmlReceived == '') && !draftHandled)
- {
- // Searches for pending new drafts from this user
- var prefix = '~drawio~' + user + '~';
-
- // Check if attachments contains draftName
- for (var i = 0; i < attachments.length; i++)
- {
- var fn = attachments[i].fileName;
-
- if (draftName == null && attachments[i].fileSize > 0 &&
- fn.substring(0, prefix.length) === prefix &&
- fn.substring(fn.length - AC.draftExtension.length) === AC.draftExtension)
- {
- filename = fn.substring(prefix.length, fn.length - AC.draftExtension.length);
- draftName = fn;
- }
-
- if (fn == draftName)
- {
- AP.require(['messages', 'confluence'], function (messages, confluence)
- {
- var acceptResponse = true;
- var timeoutHandler = function()
- {
- acceptResponse = false;
- document.body.style.backgroundSize = 'auto auto';
- document.body.style.backgroundImage = 'url(/images/stop-flat-icon-80.png)';
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner', show: false}), '*');
-
- var message = messages.error('The connection has timed out', 'The server at ' +
- serverName + ' is taking too long to respond.');
-
- messages.onClose(message, function()
- {
- confluence.closeMacroEditor();
- });
- };
-
- var timeoutThread = window.setTimeout(timeoutHandler, AC.timeout);
-
- AC.loadDiagram(pageId, draftName, null, function(loadResp)
- {
- //console.trace('DRAFT: Found', draftName, loadResp);
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- if (loadResp != null && loadResp.length > 0)
- {
- draftXml = loadResp;
- }
-
- draftHandled = true;
- startEditor();
- }
- }, function()
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- draftHandled = true;
- startEditor();
- }
- });
- });
-
- // Terminates function
- return;
- }
- }
- }
- draftHandled = true;
- startEditor();
- };
-
- var initHandler = function(evt)
- {
- if (evt.origin == hostUrl)
- {
- var msg = JSON.parse(evt.data);
-
- if (msg.event == 'init')
- {
- window.removeEventListener('message', initHandler);
- document.body.style.backgroundImage = 'none';
- initReceived = true;
- startEditor();
- }
- }
- };
- window.addEventListener('message', initHandler);
- // Parallel loading for data and iframe
- document.body.appendChild(editor);
-
- AP.getLocation(function(location)
- {
- theLocation = location;
-
- AP.require(['messages', 'navigator', 'confluence', 'request'], function (messages, navigator, confluence, request)
- {
- navigator.getLocation(function (data)
- {
- if (data != null && data.target != null && data.context!= null &&
- (data.target == 'contentedit' || data.target == 'contentcreate'))
- {
- draftPage = (data.target == 'contentcreate');
- pageId = data.context.contentId;
- }
- if (pageId == null || isNaN(pageId))
- {
- document.body.style.backgroundImage = 'url(/images/stop-flat-icon-80.png)';
- document.body.style.backgroundSize = 'auto auto';
- editor.parentNode.removeChild(editor);
-
- var message;
-
- if (data != null && data.target == 'contentcreate')
- {
- message = messages.error('Cannot insert draw.io diagram to a new Confluence page',
- 'Please save the page and try again.');
- }
- else
- {
- message = messages.error('Unable to determine page ID',
- 'Please contact your Confluence administrator.');
- }
-
-
- messages.onClose(message, function()
- {
- confluence.closeMacroEditor();
- });
- }
- else
- {
- // Not needed if drafts not enabled
- if (AC.draftEnabled)
- {
- waitingForAttachments = true;
- var acceptResponse2 = true;
- var timeoutHandler2 = function()
- {
- acceptResponse2 = false;
- document.body.style.backgroundSize = 'auto auto';
- document.body.style.backgroundImage = 'url(/images/stop-flat-icon-80.png)';
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner', show: false}), '*');
- var message = messages.error('The connection has timed out', 'The server at ' +
- serverName + ' is taking too long to respond.');
-
- messages.onClose(message, function()
- {
- confluence.closeMacroEditor();
- });
- };
-
- var timeoutThread2 = window.setTimeout(timeoutHandler2, AC.timeout);
-
- request({
- type: 'POST',
- data: JSON.stringify([pageId]),
- url: '/rpc/json-rpc/confluenceservice-v2/getAttachments',
- contentType: 'application/json;charset=UTF-8',
- success: function(res)
- {
- window.clearTimeout(timeoutThread2);
-
- if (acceptResponse2)
- {
- waitingForAttachments = false;
- attachments = JSON.parse(res);
- loadDraft();
- }
- },
- error: function(res)
- {
- window.clearTimeout(timeoutThread2);
-
- if (acceptResponse2)
- {
- waitingForAttachments = false;
- draftHandled = true;
- }
- }
- });
- }
-
- var acceptResponse = true;
- var timeoutHandler = function()
- {
- acceptResponse = false;
- document.body.style.backgroundSize = 'auto auto';
- document.body.style.backgroundImage = 'url(/images/stop-flat-icon-80.png)';
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner', show: false}), '*');
- var message = messages.error('The connection has timed out', 'The server at ' +
- serverName + ' is taking too long to respond.');
-
- messages.onClose(message, function()
- {
- confluence.closeMacroEditor();
- });
- };
-
- var timeoutThread = window.setTimeout(timeoutHandler, AC.timeout);
-
- confluence.getMacroData(function (macroData)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- var name = (macroData != null) ? (macroData.diagramName || '') : null;
- theMacroData = macroData;
-
- if (name != null && name.length > 0)
- {
- var revision = parseInt(macroData.revision);
- var owningPageId = macroData.pageId;
- draftName = (name != null) ? AC.draftPrefix + name + AC.draftExtension : null;
- loadDraft();
-
- if (isNaN(revision))
- {
- revision = null;
- }
-
- timeoutThread = window.setTimeout(timeoutHandler, AC.timeout);
-
- AC.loadDiagram(pageId, name, revision, function(loadResp)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- xmlReceived = loadResp;
- filename = name;
- //console.trace('DRAFT: Created', AC.draftPrefix + filename + AC.draftExtension);
- startEditor();
- }
- },
- function(resp)
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- editor.parentNode.removeChild(editor);
- var message = messages.error('Read Error', (resp.status == 404) ?
- 'File not found' : 'Error loading file');
-
- messages.onClose(message, function()
- {
- confluence.closeMacroEditor();
- });
- }
- }, owningPageId, true);
- }
- else
- {
- filename = null;
- xmlReceived = '';
- loadDraft();
- }
- }
- });
- }
- });
- });
- });
- };
- AC.init = function(baseUrl, location, pageId, editor, diagramName, initialXml, draftName, draftXml, macroData, draftPage)
- {
- // Hides the logo
- document.body.style.backgroundImage = 'none';
- var user = AC.getUrlParam('user_id', true);
- var draftExists = false;
-
- AP.require(['messages', 'confluence', 'request'], function(messages, confluence, request)
- {
- var newPage = location.indexOf('createpage.action') > -1 ? true : false;
- var diagramXml = null;
- var link = document.createElement('a');
- link.href = location.href;
- link.href = link.href; //to have 'host' populated under IE
- var hostUrl = link.protocol + '//' + link.hostname;
-
- function removeDraft(fn, err)
- {
- if (draftExists)
- {
- AC.removeAttachment(pageId, draftName, fn, err);
- }
- else
- {
- fn();
- }
- };
-
- function saveDraft(xml, fn, err)
- {
- //console.trace('DRAFT: Save', draftName, xml);
-
- AC.saveDiagram(pageId, draftName,
- xml,
- function(res)
- {
- var obj = null;
-
- try
- {
- obj = JSON.parse(res);
- }
- catch (e)
- {
- // ignore
- }
-
- //console.trace('DRAFT: Saved', obj);
-
- if (obj != null && obj.error != null)
- {
- if (err != null)
- {
- err(obj);
- }
- }
- else
- {
- draftExists = true;
-
- if (fn != null)
- {
- fn(obj);
- }
- }
- },
- function(res)
- {
- //console.trace('DRAFT: Save error');
- var obj = null;
-
- try
- {
- obj = JSON.parse(res);
- }
- catch (e)
- {
- // ignore
- }
-
- if (obj != null && obj.error != null)
- {
- if (err != null)
- {
- err(obj);
- }
- }
- }, false, 'text/plain', 'Created by Draw.io', false, draftPage);
- };
-
- function showTemplateDialog()
- {
- if (AC.draftEnabled)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'template', callback: true, enableRecent: true, enableSearch: true}), '*');
- }
- else
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'template', enableRecent: true, enableSearch: true}), '*');
- }
- };
-
- function promptName(name, err, errKey)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'prompt',
- titleKey: 'filename', okKey: 'save', defaultValue: name || '' }), '*');
-
- if (err != null || errKey != null)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'dialog',
- titleKey: 'error', message: err, messageKey: errKey,
- buttonKey: 'ok'}), '*');
- }
- };
-
- function checkName(name, fn, err)
- {
- if (name == null || name.length == 0)
- {
- err(name, 'Filename too short');
- }
- else if (/[&\*+=\\;/{}|\":<>\?~]/g.test(name))
- {
- err(name, 'Invalid characters \\ / | : { } < > & + ? = ; * " ~');
- }
- else
- {
- request({
- type: 'POST',
- data: JSON.stringify([pageId]),
- url: '/rpc/json-rpc/confluenceservice-v2/getAttachments',
- contentType: 'application/json;charset=UTF-8',
- success: function(res)
- {
- var attachments = JSON.parse(res);
-
- if (attachments.error != null)
- {
- err(name, attachments.error.message);
- }
- else
- {
- var draftPattern = new RegExp('^~drawio~.*~' + name.
- replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + '.tmp$', 'i');
- var lc = name.toLowerCase();
- var dn = AC.draftPrefix + lc + AC.draftExtension
- var fileExists = false;
- // Checks if any files will be overwritten
- for (var i = 0; i < attachments.length && !fileExists; i++)
- {
- // To avoid name clash with new diagrams of other users,
- // we need to check for ~drawio~.*~filename.tmp
- var an = attachments[i].fileName.toLowerCase();
- if (an == lc || an == lc + '.png' || (AC.draftEnabled &&
- (an == dn || draftPattern.test(an))))
- {
- fileExists = true;
- }
- }
-
- if (fileExists)
- {
- err(name, name + ' already exists');
- }
- else
- {
- fn(name);
- }
- }
- },
- error: function(res)
- {
- // LATER: What error message to return here?
- err(name, res);
- }
- });
- }
- };
- var autosaveThread = null;
- var autosaveCounter = 0;
- var currentXml = null;
- // Shows template dialog for new diagrams with no draft state
- if (initialXml != '')
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'load',
- autosave: 1, xml: initialXml, title: diagramName,
- macroData: macroData}), '*');
- }
- if (draftXml != null)
- {
- // Keeps ignore option even for existing files
- editor.contentWindow.postMessage(JSON.stringify({action: 'draft', xml: draftXml,
- name: diagramName, discardKey: 'discardChanges', ignore: true}), '*');
- }
- else if (initialXml == '')
- {
- showTemplateDialog();
- }
-
- var messageListener = function(evt)
- {
- if (typeof window.AC !== 'undefined' && evt.origin == hostUrl)
- {
- var drawMsg = JSON.parse(evt.data);
-
- if (drawMsg.event == 'draft')
- {
- if (drawMsg.error != null)
- {
- //console.log('DRAFT: error', drawMsg);
-
- editor.parentNode.removeChild(editor);
- var message = messages.error('Draft Read Error', drawMsg.error);
-
- messages.onClose(message, function()
- {
- confluence.closeMacroEditor();
- });
- }
- else if (drawMsg.result == 'edit')
- {
- // Use draft
- //console.trace('DRAFT: Using', draftName);
- editor.contentWindow.postMessage(JSON.stringify({action: 'load',
- autosave: 1, xml: drawMsg.message.xml, title: diagramName}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'status',
- messageKey: 'unsavedChanges', modified: true}), '*');
- draftExists = true;
- }
- else
- {
- if (initialXml == '' || drawMsg.result == 'ignore')
- {
- if (initialXml != '')
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'load',
- autosave: 1, xml: initialXml, title: diagramName,
- macroData: macroData}), '*');
- }
- else
- {
- diagramName = null;
- showTemplateDialog();
- }
- }
- else
- {
- //console.trace('DRAFT: Discarding', draftName);
-
- AC.removeAttachment(pageId, draftName);
- }
- }
- }
- else if (drawMsg.event == 'searchDocs')
- {
- //since we don't use a unique file extension for draw.io diagrams, we need to find pages having draw.io macro also
- //So, two search requests are needed
- AP.require('request', function(request) {
- request({
- //TODO this query can be enhanced to get part of the name matching but the problem is with the png!
- url: '/rest/api/content/search?cql=' + encodeURIComponent('type=attachment and (title ~ "' + drawMsg.searchStr + '" or title ~ "' + drawMsg.searchStr + '.png")') + '&limit=100', //limit is 100 and the pages limit is 50 since each diagram has two attachments (and we assume one diagram per page)
- success: function(resp)
- {
- resp = JSON.parse(resp);
- var retList = [];
- var list = resp.results;
- if (list)
- {
- var attMap = {};
- //convert the list to map so we can search by name effeciently
- for (var i = 0; i < list.length; i++)
- {
- //key is pageId + | + att name
- var pageId = list[i]["_links"]["webui"].match(/pages\/(\d+)/);
-
- if (pageId != null)
- {
- attMap[pageId[1] + '|' + list[i].title] = {att: list[i], pageId: pageId[1]};
- }
- }
- //TODO confirm that the attachments are in a page having draw.io macro
- for (var key in attMap)
- {
- var att = attMap[key];
-
- if (attMap[key+'.png']) //each draw.io attachment should have an associated png preview
- {
- //We cannot get the latest version info, it can searched when a diagram is selected
- retList.push({
- title: att.att.title,
- url: "/download/attachments/" + att.pageId + "/"
- + encodeURIComponent(att.att.title),
- info: {
- id: att.att.id,
- pageId: att.pageId
- },
- imgUrl: baseUrl + "/download/attachments/" + att.pageId + "/"
- + encodeURIComponent(att.att.title)
- + ".png?api=v2"
- });
- }
- }
- editor.contentWindow.postMessage(JSON.stringify({action: 'searchDocsList',
- list: retList}), '*');
- }
- },
- error : function(resp)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'searchDocsList',
- list: [], errorMsg: "Network Error!"}), '*');
- }
- });
- });
- }
- else if (drawMsg.event == 'recentDocs')
- {
- //since we don't use a unique file extension for draw.io diagrams, we need to find pages having draw.io macro also
- //So, two search requests are needed
- AP.require('request', function(request) {
- request({
- url: '/rest/api/content/search?cql=type=attachment%20and%20lastmodified%3E%20startOfDay(%22-7d%22)&limit=100', //cql= type=attachment and lastmodified > startOfDay("-7d") //modified in the last 7 days
- //limit is 100 and the pages limit is 50 since each diagram has two attachments (and we assume one diagram per page)
- success: function(resp)
- {
- resp = JSON.parse(resp);
- var retList = [];
- var list = resp.results;
- if (list)
- {
- var attMap = {};
- //convert the list to map so we can search by name effeciently
- for (var i = 0; i < list.length; i++)
- {
- //key is pageId + | + att name
- var pageId = list[i]["_links"]["webui"].match(/pages\/(\d+)/);
-
- if (pageId != null)
- {
- attMap[pageId[1] + '|' + list[i].title] = {att: list[i], pageId: pageId[1]};
- }
- }
- //confirm that the attachments are in a page having draw.io macro
- request({
- url: '/rest/api/content/search?cql=macro=drawio%20and%20lastmodified%3E%20startOfDay(%22-7d%22)&limit=50', //cql= macro=drawio and lastmodified > startOfDay("-7d") //modified in the last 7 days
- success: function(resp)
- {
- resp = JSON.parse(resp);
- var pages = {};
- var list = resp.results;
- if (list)
- {
- for (var i = 0; i < list.length; i++)
- {
- pages[list[i].id] = list[i];
- }
- }
-
-
- for (var key in attMap)
- {
- var att = attMap[key];
-
- if (attMap[key+'.png'] && pages[att.pageId] != null) //each draw.io attachment should have an associated png preview
- {
- //We cannot get the latest version info, it can searched when a diagram is selected
- retList.push({
- title: att.att.title,
- url: "/download/attachments/" + att.pageId + "/"
- + encodeURIComponent(att.att.title),
- info: {
- id: att.att.id,
- pageId: att.pageId
- },
- imgUrl: baseUrl + "/download/attachments/" + att.pageId + "/"
- + encodeURIComponent(att.att.title)
- + ".png?api=v2"
- });
- }
- }
- editor.contentWindow.postMessage(JSON.stringify({action: 'recentDocsList',
- list: retList}), '*');
- },
- error : function(resp)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'recentDocsList',
- list: [], errorMsg: "Network Error!"}), '*');
- }
- });
- }
- },
- error : function(resp)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'recentDocsList',
- list: [], errorMsg: "Network Error!"}), '*');
- }
- });
- });
-
- }
- else if (drawMsg.event == 'template')
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: true, messageKey: 'inserting'}), '*');
-
- if (drawMsg.docUrl)
- {
- checkName(drawMsg.name, function(name)
- {
- diagramName = name;
-
- AP.require('request', function(request) {
-
- var loadTemplate = function(version)
- {
- request({
- url: drawMsg.docUrl + (version != null? "?version=" + version : ""),
- success: function(xml)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'load',
- autosave: 1, xml: xml, title: diagramName}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: false}), '*');
- },
- error : function(resp)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: false}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'dialog',
- titleKey: 'error', message: "Diagram cannot be loaded", messageKey: null,
- buttonKey: 'ok'}), '*');
- }
- });
- }
-
- request({
- url: '/rest/api/content/' + drawMsg.info.id,
- success: function(resp)
- {
- resp = JSON.parse(resp);
-
- try
- {
- loadTemplate(resp.version.number);
- }
- catch(e)
- {
- loadTemplate();
- }
- },
- error : function(resp)
- {
- loadTemplate();
- }
- });
- });
- },
- function(name, err, errKey)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: false}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'dialog',
- titleKey: 'error', message: err, messageKey: errKey,
- buttonKey: 'ok'}), '*');
- });
- }
- else
- {
- checkName(drawMsg.name, function(name)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: false}), '*');
- diagramName = name;
-
- if (AC.draftEnabled)
- {
- draftName = '~drawio~' + user + '~' + diagramName + AC.draftExtension;
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: true, messageKey: 'inserting'}), '*');
-
- saveDraft(drawMsg.xml, function()
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner', show: false}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'load',
- autosave: 1, xml: drawMsg.xml, title: diagramName}), '*');
- },
- function()
- {
- editor.parentNode.removeChild(editor);
- var message = messages.error('Draft Write Error', 'Draft could not be created');
-
- messages.onClose(message, function()
- {
- confluence.closeMacroEditor();
- });
- });
- }
- else
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'load',
- autosave: 1, xml: drawMsg.xml, title: diagramName}), '*');
- }
- },
- function(name, err, errKey)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: false}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'dialog',
- titleKey: 'error', message: err, messageKey: errKey,
- buttonKey: 'ok'}), '*');
- });
- }
- }
- else if (drawMsg.event == 'autosave')
- {
- // Saves all changes to draft attachment
- currentXml = drawMsg.xml;
-
- if (autosaveThread == null && AC.draftEnabled)
- {
- //console.trace('DRAFT: Starting timer');
-
- autosaveThread = window.setTimeout(function()
- {
- //console.log('DRAFT: Saving', currentXml);
-
- autosaveThread = null
- saveDraft(currentXml);
- autosaveCounter++;
- }, (autosaveCounter == 0) ? 0 : AC.autosaveTimeout);
- }
- }
- else if (drawMsg.event == 'exit')
- {
- removeDraft(function()
- {
- confluence.closeMacroEditor();
- });
- }
- else if (drawMsg.event == 'save')
- {
- diagramXml = drawMsg.xml;
-
- if (diagramName == null)
- {
- promptName('');
- }
- else
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'export',
- format: 'png', spinKey: 'saving', message: drawMsg}), '*');
- }
- }
- else if (drawMsg.event == 'prompt')
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: true, messageKey: 'inserting'}), '*');
- checkName(drawMsg.value, function(name)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: false}), '*');
- diagramName = name;
- editor.contentWindow.postMessage(JSON.stringify({action: 'export',
- format: 'png', spinKey: 'saving'}), '*');
- },
- function(name, err, errKey)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: false}), '*');
- promptName(name, err, errKey);
- });
- }
- else if (drawMsg.event == 'export')
- {
- // Proceeds from sending the export message by saving the exported files
- var imageData = drawMsg.data.substring(drawMsg.data.indexOf(',') + 1);
- var diaWidth = drawMsg.bounds.width / drawMsg.scale;
- var diaHeight = drawMsg.bounds.height / drawMsg.scale;
-
- function showError(key, message)
- {
- var msg = {action: 'dialog', titleKey: 'error', modified: true, buttonKey: 'close'};
-
- if (message != null)
- {
- msg.message = message;
- }
- else
- {
- msg.messageKey = key || 'errorSavingFile';
- }
-
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner', show: false}), '*');
- editor.contentWindow.postMessage(JSON.stringify(msg), '*');
- };
-
- function saveError(err)
- {
- var key = null;
- var message = null;
-
- if (err.status == 409)
- {
- diagramName = null;
- key = 'fileExists';
- }
- else if (err.status == 401)
- {
- // Session expired
- message = 'Looks like your session expired. Log in again to keep working. ' +
- '<a href="' + baseUrl + '/pages/dashboard.action" target="_blank">Login</a>';
- }
-
- showError(key, message);
- };
-
- function successXml(responseText)
- {
- var resp = null;
- var revision = '1';
- var contentId = null;
- var contentVer = null;
-
- //TODO Why this code (Is it expected to have incorrect responseText?)
- try
- {
- resp = JSON.parse(responseText);
- }
- catch (e)
- {
- // Ignores and use default value for revision
- }
- // LATER: Get revision from metadata of attachment and check
- // what condition makes the response not contain an URL
- //TODO Is prev comment still needed with REST API?
- if (resp != null && resp.results != null && resp.results[0])
- {
- var attObj = resp.results[0];
- revision = attObj.version.number;
- //Save/update the custom content
- var spaceKey = AC.getSpaceKey(attObj._expandable.space);
- var pageType = attObj.container.type;
- AC.saveCustomContent(spaceKey, pageId, pageType, diagramName, revision,
- (drawMsg.macroData != null) ? drawMsg.macroData.contentId : null,
- (drawMsg.macroData != null) ? drawMsg.macroData.contentVer : null,
- function(responseText)
- {
- var content = JSON.parse(responseText);
-
- contentId = content.id;
- contentVer = content.version.number;
-
- AC.saveDiagram(pageId, diagramName + '.png', AC.b64toBlob(imageData, 'image/png'),
- successPng, saveError, false, 'image/png', 'draw.io preview', false, draftPage);
- }, saveError);
- }
- else
- {
- // Logs special case where save response has no URL
- try
- {
- var img = new Image();
- var message = 'Invalid Confluence Cloud response';
- img.src = '/images/2x2.png?msg=' + encodeURIComponent(message) +
- ((responseText != null) ? '&resp=' + encodeURIComponent(responseText) : '&resp=[null]');
- '&url=' + encodeURIComponent(window.location.href) +
- '&v=' + encodeURIComponent(EditorUi.VERSION);
- }
- catch (err)
- {
- // do nothing
- }
-
- //TODO Save png here in case responseText is incorrect (But why it can be incorrect?)
- AC.saveDiagram(pageId, diagramName + '.png', AC.b64toBlob(imageData, 'image/png'),
- successPng, saveError, false, 'image/png', 'draw.io preview', false, draftPage);
- }
- function successPng(pngResponseText)
- {
- try
- {
- confluence.saveMacro(
- {
- diagramName: diagramName,
- revision: revision,
- pageId: newPage ? null : pageId,
- contentId: contentId,
- contentVer: contentVer,
- baseUrl: baseUrl,
- width: diaWidth,
- height: diaHeight,
- tbstyle: (drawMsg.macroData != null) ? drawMsg.macroData.tbstyle : '',
- links: (drawMsg.macroData != null) ? drawMsg.macroData.links : '',
- lbox: (drawMsg.macroData != null && drawMsg.macroData.lbox != null) ? drawMsg.macroData.lbox : '1',
- zoom: (drawMsg.macroData != null && drawMsg.macroData.zoom != null) ? drawMsg.macroData.zoom : '1'
- });
-
- if (AC.autoExit || drawMsg.message == null || drawMsg.message.message == null || drawMsg.message.message.exit)
- {
- removeDraft(function()
- {
- confluence.closeMacroEditor();
- });
- }
- else
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner', show: false}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'status', message: '', modified: false}), '*');
- }
- }
- catch (e)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner', show: false}), '*');
- editor.contentWindow.postMessage(JSON.stringify({action: 'dialog',
- titleKey: 'errorSavingFile', message: e.message, buttonKey: 'ok'}), '*');
- }
- };
- };
- if (diagramName != null)
- {
- editor.contentWindow.postMessage(JSON.stringify({action: 'spinner',
- show: true, messageKey: 'saving'}), '*');
-
- AC.saveDiagram(pageId, diagramName, diagramXml,
- successXml, saveError, false, 'text/plain', 'draw.io diagram', false, draftPage);
- }
- }
- }
- };
- window.addEventListener('message', messageListener);
- });
- };
- AC.loadDiagram = function (pageId, diagramName, revision, success, error, owningPageId, tryRev1, dontCheckVer) {
- // TODO: Get binary
-
- AP.require(['request', 'confluence'], function(request, confluence) {
- //Confirm that the macro is in sync with the diagram
- //Sometimes the diagram is saved but the macro is not updated
- var attInfo = null;
- var pageInfo = null;
-
- function confirmDiagramInSync()
- {
- if (attInfo == null || pageInfo == null)
- return;
-
- //TODO is this condition enough or we need to check timestamps also?
- if (attInfo.version.number > revision
- && (pageInfo.version.message == null || pageInfo.version.message.indexOf("Reverted") < 0))
- {
- AC.loadDiagram(pageId, diagramName, attInfo.version.number, success, error, owningPageId, tryRev1, true);
- //Update the macro
- //Custom Content version will be fixed on next save, this will not affect correctness
- confluence.getMacroData(function (macroData)
- {
- if (macroData != null)
- {
- confluence.saveMacro(
- {
- diagramName: macroData.diagramName,
- revision: attInfo.version.number,
- pageId: macroData.pageId,
- contentId: macroData.contentId,
- contentVer: macroData.contentVer,
- baseUrl: macroData.baseUrl,
- width: macroData.width,
- height: macroData.height,
- tbstyle: macroData.tbstyle,
- links: macroData.links,
- lbox: macroData.lbox != null ? macroData.lbox : '1',
- zoom: macroData.zoom != null ? macroData.zoom : '1'
- });
- }
- });
- }
- }
-
- //To avoid race we do the version check after loading the diagram in the macro
- var localSuccess = function()
- {
- success.apply(this, arguments);
-
- if (!dontCheckVer && revision != null)
- {
- request({
- type: 'GET',
- url: '/rest/api/content/' + pageId + '?expand=version',
- contentType: 'application/json;charset=UTF-8',
- success: function (resp)
- {
- pageInfo = JSON.parse(resp);
-
- confirmDiagramInSync();
- },
- error: function (resp)
- {
- //Ignore
- }
- });
-
- request({
- type: 'GET',
- url: '/rest/api/content/' + pageId + '/child/attachment?filename=' +
- encodeURIComponent(diagramName) + '&expand=version',
- contentType: 'application/json;charset=UTF-8',
- success: function (resp)
- {
- var tmp = JSON.parse(resp);
-
- if (tmp.results && tmp.results.length == 1)
- {
- attInfo = tmp.results[0];
- }
-
- confirmDiagramInSync();
- },
- error: function (resp)
- {
- //Ignore
- }
- });
- }
- }
-
- request({
- //TODO find out the ID of the page that actually holds the attachments because historical revisions do not have attachments
- url: '/download/attachments/' + pageId + '/' + encodeURIComponent(diagramName) +
- ((revision != null) ? '?version=' + revision : ''),
- success: localSuccess,
- error : function(resp)
- {
- //When a page is copied, attachments are reset to version 1 while the revision parameter remains the same
- if (tryRev1 && revision > 1 && resp.status == 404)
- {
- request({
- url: '/download/attachments/' + pageId + '/' + encodeURIComponent(diagramName),
- success: localSuccess,
- error : function(resp) { //If revesion 1 failed, then try the owningPageId
- if (owningPageId && resp.status == 404)
- {
- request({
- url: '/download/attachments/' + owningPageId + '/' + encodeURIComponent(diagramName)
- +'?version=' + revision, //this version should exists in the original owning page
- success: localSuccess,
- error : error
- });
- }
- }
- });
- }
- else if (owningPageId && resp.status == 404) //We are at revesion 1, so try the owningPageId directly
- {
- request({
- url: '/download/attachments/' + owningPageId + '/' + encodeURIComponent(diagramName),
- success: localSuccess,
- error : error
- });
- }
- else
- {
- error(resp);
- }
- }
- });
- });
- };
- AC.saveCustomContent = function(spaceKey, pageId, pageType, diagramName, revision, contentId, contentVer, success, error)
- {
- var customObj = {
- "type": "ac:com.mxgraph.confluence.plugins.diagramly:drawio-diagram",
- "space": {
- "key": spaceKey
- },
- "container": {
- "type": pageType,
- "id": pageId
- },
- "title": diagramName,
- "body": {
- "storage": {
- "value": encodeURIComponent(JSON.stringify({
- "pageId": pageId,
- "diagramName": diagramName,
- "version": revision
- })),
- "representation": "storage"
- }
- },
- "status": "current"
- };
-
- if (contentId)
- {
- customObj.version = {
- "number": ++contentVer
- };
- }
-
- AP.require(['request'], function(request)
- {
- request({
- type: contentId? 'PUT' : 'POST',
- data: JSON.stringify(customObj),
- url: "/rest/api/content/" + (contentId? contentId : ""),
- contentType: "application/json",
- success: success,
- error: function(resp) {
- //User can delete a custom content externally and we will get error 403 and message will contain the given id
- //Then save a new one
- var err = JSON.parse(resp.responseText);
-
- if (contentId && err.statusCode == 403 && err.message.indexOf(contentId) > 0)
- {
- AC.saveCustomContent(spaceKey, pageId, pageType, diagramName, revision, null, null, success, error);
- }
- //Sometimes the macro is not updated such that the version is not correct. The same happens when a page version is restored
- else if (err.statusCode == 409 && err.message.indexOf("Current version is:") > 0)
- {
- //We will use the error message to detect the correct version instead of doing another request.
- //It should be safe as long as error messages are not translated or changed
- var curContentVer = err.message.match(/\d+/);
-
- if (curContentVer != null)
- {
- AC.saveCustomContent(spaceKey, pageId, pageType, diagramName, revision, contentId, curContentVer[0], success, error);
- }
- }
- else
- {
- error(resp);
- }
- }
- });
- });
- };
- //TODO We can upload both the diagram and its png in one call if needed?
- AC.saveDiagram = function(pageId, diagramName, xml, success, error, newSave, mime, comment, sendNotif, draftPage)
- {
- loadSucess = function(resp)
- {
- error({status: 409, message: 'File already exists'});
- };
-
- loadError = function(resp)
- {
- if (resp.status == 404) // file under given name does not exist means we can proceed with saving
- {
- doSave();
- }
- else
- {
- error({status: resp.status, message : resp.statusText });
- }
- };
-
- var sessionCheck = function(responseText)
- {
- if (responseText != null)
- {
- var obj = JSON.parse(responseText);
-
- if (obj != null && obj.code == -32600) //TODO is the codes the same with new REST APIs
- {
- error({status: 401});
-
- return;
- }
- }
-
- success(responseText);
- }
-
- doSave = function()
- {
- AP.require(['request'], function(request)
- {
- var attFile = (xml instanceof Blob)? xml : new Blob([xml], {type: mime});
- attFile.name = diagramName;
-
- var reqData = {file: attFile, minorEdit: sendNotif? false : true};
- var draft = draftPage ? "?status=draft" : "";
- if (comment != null)
- {
- reqData.comment = comment;
- }
-
- request({
- type: 'PUT',
- data: reqData,
- url: "/rest/api/content/"+ pageId +"/child/attachment" + draft,
- contentType: "multipart/form-data",
- success: sessionCheck,
- error: error
- });
- });
- };
-
- if(newSave && mime == 'text/plain')
- {
- this.loadDiagram(pageId, diagramName, 0, loadSucess, loadError);
- }
- else
- {
- doSave();
- }
- };
- AC.removeAttachment = function(pageId, filename, fn, err)
- {
- if (pageId != null && filename != null)
- {
- AP.require('request', function(request) {
- request({
- type: 'POST',
- data: JSON.stringify([pageId, filename]),
- url: '/rpc/json-rpc/confluenceservice-v2/removeAttachment',
- contentType: 'application/json;charset=UTF-8',
- success: function()
- {
- if (fn != null)
- {
- fn();
- }
- },
- error: function()
- {
- if (err != null)
- {
- err();
- }
-
- if (fn != null)
- {
- fn();
- }
- }
- });
- });
- }
- else
- {
- fn();
- }
- };
- AC.getMacroData = function(fn) {
- AP.require('confluence', function(confluence) {
- confluence.getMacroData(fn);
- });
- }
- //From mxUtils
- AC.htmlEntities = function(s, newline)
- {
- s = String(s || '');
-
- s = s.replace(/&/g,'&'); // 38 26
- s = s.replace(/"/g,'"'); // 34 22
- s = s.replace(/\'/g,'''); // 39 27
- s = s.replace(/</g,'<'); // 60 3C
- s = s.replace(/>/g,'>'); // 62 3E
- if (newline == null || newline)
- {
- s = s.replace(/\n/g, '
');
- }
-
- return s;
- };
- AC.fromHtmlEntities = function(s, newline)
- {
- s = String(s || '');
-
- s = s.replace(/&/g,'&'); // 38 26
- s = s.replace(/"/g,'"'); // 34 22
- s = s.replace(/'/g,'\\'); // 39 27
- s = s.replace(/</g,'<'); // 60 3C
- s = s.replace(/>/g,'>'); // 62 3E
- if (newline == null || newline)
- {
- s = s.replace(/
/g, '\n');
- }
-
- return s;
- };
|