浏览代码

14.8.5 release

David Benson [draw.io] 4 年之前
父节点
当前提交
57df800c05
共有 79 个文件被更改,包括 2160 次插入819 次删除
  1. 5 0
      ChangeLog
  2. 1 1
      VERSION
  3. 3 0
      etc/build/build.xml
  4. 17 15
      src/main/java/com/mxgraph/online/GoogleGadgetServlet.java
  5. 1 0
      src/main/webapp/images/notion-logo-white.svg
  6. 1 0
      src/main/webapp/images/notion-logo.svg
  7. 608 579
      src/main/webapp/js/app.min.js
  8. 108 3
      src/main/webapp/js/diagramly/App.js
  9. 3 0
      src/main/webapp/js/diagramly/Devel.js
  10. 60 2
      src/main/webapp/js/diagramly/Dialogs.js
  11. 5 0
      src/main/webapp/js/diagramly/EditorUi.js
  12. 36 0
      src/main/webapp/js/diagramly/Menus.js
  13. 804 0
      src/main/webapp/js/diagramly/NotionClient.js
  14. 202 0
      src/main/webapp/js/diagramly/NotionFile.js
  15. 28 0
      src/main/webapp/js/diagramly/NotionLibrary.js
  16. 40 40
      src/main/webapp/js/viewer-static.min.js
  17. 40 40
      src/main/webapp/js/viewer.min.js
  18. 1 1
      src/main/webapp/mxgraph/mxClient.js
  19. 1 0
      src/main/webapp/plugins/googledrive.js
  20. 1 0
      src/main/webapp/resources/dia.txt
  21. 1 0
      src/main/webapp/resources/dia_am.txt
  22. 1 0
      src/main/webapp/resources/dia_ar.txt
  23. 1 0
      src/main/webapp/resources/dia_bg.txt
  24. 1 0
      src/main/webapp/resources/dia_bn.txt
  25. 1 0
      src/main/webapp/resources/dia_bs.txt
  26. 1 0
      src/main/webapp/resources/dia_ca.txt
  27. 137 136
      src/main/webapp/resources/dia_cs.txt
  28. 1 0
      src/main/webapp/resources/dia_da.txt
  29. 1 0
      src/main/webapp/resources/dia_de.txt
  30. 1 0
      src/main/webapp/resources/dia_el.txt
  31. 1 0
      src/main/webapp/resources/dia_eo.txt
  32. 1 0
      src/main/webapp/resources/dia_es.txt
  33. 1 0
      src/main/webapp/resources/dia_et.txt
  34. 1 0
      src/main/webapp/resources/dia_eu.txt
  35. 1 0
      src/main/webapp/resources/dia_fa.txt
  36. 1 0
      src/main/webapp/resources/dia_fi.txt
  37. 1 0
      src/main/webapp/resources/dia_fil.txt
  38. 1 0
      src/main/webapp/resources/dia_fr.txt
  39. 1 0
      src/main/webapp/resources/dia_gl.txt
  40. 1 0
      src/main/webapp/resources/dia_gu.txt
  41. 1 0
      src/main/webapp/resources/dia_he.txt
  42. 1 0
      src/main/webapp/resources/dia_hi.txt
  43. 1 0
      src/main/webapp/resources/dia_hr.txt
  44. 1 0
      src/main/webapp/resources/dia_hu.txt
  45. 1 0
      src/main/webapp/resources/dia_i18n.txt
  46. 1 0
      src/main/webapp/resources/dia_id.txt
  47. 1 0
      src/main/webapp/resources/dia_it.txt
  48. 1 0
      src/main/webapp/resources/dia_ja.txt
  49. 1 0
      src/main/webapp/resources/dia_kn.txt
  50. 1 0
      src/main/webapp/resources/dia_ko.txt
  51. 1 0
      src/main/webapp/resources/dia_lt.txt
  52. 1 0
      src/main/webapp/resources/dia_lv.txt
  53. 1 0
      src/main/webapp/resources/dia_ml.txt
  54. 1 0
      src/main/webapp/resources/dia_mr.txt
  55. 1 0
      src/main/webapp/resources/dia_ms.txt
  56. 1 0
      src/main/webapp/resources/dia_my.txt
  57. 1 0
      src/main/webapp/resources/dia_nl.txt
  58. 1 0
      src/main/webapp/resources/dia_no.txt
  59. 1 0
      src/main/webapp/resources/dia_pl.txt
  60. 1 0
      src/main/webapp/resources/dia_pt-br.txt
  61. 1 0
      src/main/webapp/resources/dia_pt.txt
  62. 1 0
      src/main/webapp/resources/dia_ro.txt
  63. 1 0
      src/main/webapp/resources/dia_ru.txt
  64. 1 0
      src/main/webapp/resources/dia_si.txt
  65. 1 0
      src/main/webapp/resources/dia_sk.txt
  66. 1 0
      src/main/webapp/resources/dia_sl.txt
  67. 1 0
      src/main/webapp/resources/dia_sr.txt
  68. 1 0
      src/main/webapp/resources/dia_sv.txt
  69. 1 0
      src/main/webapp/resources/dia_sw.txt
  70. 1 0
      src/main/webapp/resources/dia_ta.txt
  71. 1 0
      src/main/webapp/resources/dia_te.txt
  72. 1 0
      src/main/webapp/resources/dia_th.txt
  73. 1 0
      src/main/webapp/resources/dia_tr.txt
  74. 1 0
      src/main/webapp/resources/dia_uk.txt
  75. 1 0
      src/main/webapp/resources/dia_vi.txt
  76. 1 0
      src/main/webapp/resources/dia_zh-tw.txt
  77. 1 0
      src/main/webapp/resources/dia_zh.txt
  78. 1 1
      src/main/webapp/service-worker.js
  79. 1 1
      src/main/webapp/service-worker.js.map

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+07-JUL-2021: 14.8.5
+
+- Fixes XSS attack on GoogleGadgetServlet. Thanks to Alfred Berg for report.
+- Updates to cs translations
+
 02-JUL-2021: 14.8.4
 
 - Fixes drag and drop for edge labels with selected parents

+ 1 - 1
VERSION

@@ -1 +1 @@
-14.8.4
+14.8.5

+ 3 - 0
etc/build/build.xml

@@ -424,6 +424,9 @@
 				<file name="GitLabFile.js" />
 				<file name="GitLabLibrary.js" />
 				<file name="GitLabClient.js" />
+				<file name="NotionFile.js" />
+				<file name="NotionLibrary.js" />
+				<file name="NotionClient.js" />
 				<file name="DrawioComment.js" />
 				<file name="DriveComment.js" />
 			</sources>

+ 17 - 15
src/main/java/com/mxgraph/online/GoogleGadgetServlet.java

@@ -27,6 +27,8 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
+
 /**
  * Servlet implementation class OpenServlet
  */
@@ -51,21 +53,21 @@ public class GoogleGadgetServlet extends HttpServlet
 	protected void doGet(HttpServletRequest request,
 			HttpServletResponse response) throws ServletException, IOException
 	{
-		String diagram = request.getParameter("diagram");
-		String type = request.getParameter("type");
-		String title = request.getParameter("title");
-		String edit = request.getParameter("edit");
-		String embed = request.getParameter("embed");
-		String b = request.getParameter("border");
-		String zoom = request.getParameter("zoom");
-		String pan = request.getParameter("pan");
-		String fit = request.getParameter("fit");
-		String resize = request.getParameter("resize");
-		String height = request.getParameter("height");
-		String math = request.getParameter("math");
-		String x0 = request.getParameter("x0");
-		String y0 = request.getParameter("y0");
-		String s = request.getParameter("s");
+		String diagram = escapeHtml4(request.getParameter("diagram"));
+		String type = escapeHtml4(request.getParameter("type"));
+		String title = escapeHtml4(request.getParameter("title"));
+		String edit = escapeHtml4(request.getParameter("edit"));
+		String embed = escapeHtml4(request.getParameter("embed"));
+		String b = escapeHtml4(request.getParameter("border"));
+		String zoom = escapeHtml4(request.getParameter("zoom"));
+		String pan = escapeHtml4(request.getParameter("pan"));
+		String fit = escapeHtml4(request.getParameter("fit"));
+		String resize = escapeHtml4(request.getParameter("resize"));
+		String height = escapeHtml4(request.getParameter("height"));
+		String math = escapeHtml4(request.getParameter("math"));
+		String x0 = escapeHtml4(request.getParameter("x0"));
+		String y0 = escapeHtml4(request.getParameter("y0"));
+		String s = escapeHtml4(request.getParameter("s"));
 		boolean showEmbed = (embed != null) ? embed.equals("1") : true;
 		
 		if (diagram == null)

文件差异内容过多而无法显示
+ 1 - 0
src/main/webapp/images/notion-logo-white.svg


文件差异内容过多而无法显示
+ 1 - 0
src/main/webapp/images/notion-logo.svg


文件差异内容过多而无法显示
+ 608 - 579
src/main/webapp/js/app.min.js


+ 108 - 3
src/main/webapp/js/diagramly/App.js

@@ -235,6 +235,11 @@ App.MODE_BROWSER = 'browser';
  */
 App.MODE_TRELLO = 'trello';
 
+/**
+ * Notion App Mode
+ */
+App.MODE_NOTION = 'notion';
+
 /**
  * Embed App Mode
  */
@@ -1454,6 +1459,26 @@ App.prototype.init = function()
 		}));
 	}
 
+	/**
+	 * Creates notion client.
+	 */
+	this.notion = 
+	//TODO disabled by default
+		/*(!mxClient.IS_IE || document.documentMode == 10 ||
+		mxClient.IS_IE11 || mxClient.IS_EDGE) &&
+		(urlParams['ntn'] != '0' && (urlParams['embed'] != '1' ||
+		urlParams['ntn'] == '1')) */
+		urlParams['ntn'] == '1' ? new NotionClient(this) : null;
+
+	if (this.notion != null)
+	{
+		this.notion.addListener('userChanged', mxUtils.bind(this, function()
+		{
+			this.updateUserElement();
+			this.restoreLibraries();
+		}));
+	}
+
 	/**
 	 * Lazy-loading for individual backends
 	 */
@@ -2672,6 +2697,10 @@ App.prototype.appIconClicked = function(evt)
 		{
 			this.openLink('https://trello.com/');
 		}
+		else if (mode == App.MODE_NOTION)
+		{
+			this.openLink('https://www.notion.so/');
+		}
 		else if (mode == App.MODE_GITHUB)
 		{
 			if (file != null && file.constructor == GitHubFile)
@@ -4013,14 +4042,16 @@ App.prototype.pickLibrary = function(mode)
 	mode = (mode != null) ? mode : this.mode;
 	
 	if (mode == App.MODE_GOOGLE || mode == App.MODE_DROPBOX || mode == App.MODE_ONEDRIVE ||
-		mode == App.MODE_GITHUB || mode == App.MODE_GITLAB || mode == App.MODE_TRELLO)
+		mode == App.MODE_GITHUB || mode == App.MODE_GITLAB || mode == App.MODE_TRELLO ||
+		mode == App.MODE_NOTION)
 	{
 		var peer = (mode == App.MODE_GOOGLE) ? this.drive :
 			((mode == App.MODE_ONEDRIVE) ? this.oneDrive :
 			((mode == App.MODE_GITHUB) ? this.gitHub :
 			((mode == App.MODE_GITLAB) ? this.gitLab :
 			((mode == App.MODE_TRELLO) ? this.trello :
-			this.dropbox))));
+			((mode == App.MODE_NOTION) ? this.notion :
+			this.dropbox)))));
 		
 		if (peer != null)
 		{
@@ -4225,6 +4256,15 @@ App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload,
 						this.libraryLoaded(newFile, images);
 					}), error, folderId);
 				}
+				else if (mode == App.MODE_NOTION && this.notion != null && this.spinner.spin(document.body, mxResources.get('inserting')))
+				{
+					this.notion.insertLibrary(name, xml, mxUtils.bind(this, function(newFile)
+					{
+						this.spinner.stop();
+						this.hideDialog(true);
+						this.libraryLoaded(newFile, images);
+					}), error, folderId);
+				}
 				else if (mode == App.MODE_TRELLO && this.trello != null && this.spinner.spin(document.body, mxResources.get('inserting')))
 				{
 					this.trello.insertLibrary(name, xml, mxUtils.bind(this, function(newFile)
@@ -4624,7 +4664,11 @@ App.prototype.getPeerForMode = function(mode)
 	else if (mode == App.MODE_TRELLO)
 	{
 		return this.trello;
-	} 
+	}
+	else if (mode == App.MODE_NOTION)
+	{
+		return this.notion;
+	}
 	else
 	{
 		return null;
@@ -4695,6 +4739,14 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
 					this.fileCreated(file, libs, replace, done, clibs);
 				}), error, false, folderId);
 			}
+			else if (mode == App.MODE_NOTION && this.notion != null)
+			{
+				this.notion.insertFile(title, data, mxUtils.bind(this, function(file)
+				{
+					complete();
+					this.fileCreated(file, libs, replace, done, clibs);
+				}), error, false, folderId);
+			}
 			else if (mode == App.MODE_TRELLO && this.trello != null)
 			{
 				this.trello.insertFile(title, data, mxUtils.bind(this, function(file)
@@ -5222,6 +5274,10 @@ App.prototype.loadFile = function(id, sameWindow, file, success, force)
 				{
 					peer = this.trello;
 				}
+				else if (id.charAt(0) == 'N')
+				{
+					peer = this.notion;
+				}
 				
 				if (peer == null)
 				{
@@ -6125,6 +6181,14 @@ App.prototype.pickFolder = function(mode, fn, enabled, direct, force)
 			fn(folderPath);
 		}));
 	}
+	else if (enabled && mode == App.MODE_NOTION && this.notion != null)
+	{
+		this.notion.pickFolder(mxUtils.bind(this, function(folderPath)
+		{
+			resume();
+			fn(folderPath);
+		}));
+	}
 	else if (enabled && mode == App.MODE_TRELLO && this.trello != null)
 	{
 		this.trello.pickFolder(mxUtils.bind(this, function(cardId)
@@ -6631,6 +6695,11 @@ App.prototype.updateHeader = function()
 					this.appIcon.style.backgroundImage = 'url(' + IMAGE_PATH + '/gitlab-logo-white.svg)';
 					this.appIcon.style.backgroundSize = '100% 100%';
 				}
+				else if (mode == App.MODE_NOTION)
+				{
+					this.appIcon.style.backgroundImage = 'url(' + IMAGE_PATH + '/notion-logo-white.svg)';
+					this.appIcon.style.backgroundSize = '70% 70%';
+				}				
 				else if (mode == App.MODE_TRELLO)
 				{
 					this.appIcon.style.backgroundImage = 'url(' + IMAGE_PATH + '/trello-logo-white-orange.svg)';
@@ -6925,6 +6994,7 @@ App.prototype.updateUserElement = function()
 		(this.dropbox == null || this.dropbox.getUser() == null) &&
 		(this.gitHub == null || this.gitHub.getUser() == null) &&
 		(this.gitLab == null || this.gitLab.getUser() == null) &&
+		(this.notion == null || this.notion.getUser() == null) &&
 		(this.trello == null || !this.trello.isAuthorized())) //TODO Trello no user issue
 	{
 		if (this.userElement != null)
@@ -7303,6 +7373,37 @@ App.prototype.updateUserElement = function()
 							}
 						}), mxResources.get('gitlab'));
 					}
+
+					if (this.notion != null)
+					{
+						addUser(this.notion.getUser(), IMAGE_PATH + '/notion-logo.svg', mxUtils.bind(this, function()
+						{
+							var file = this.getCurrentFile();
+
+							if (file != null && file.constructor == NotionFile)
+							{
+								var doLogout = mxUtils.bind(this, function()
+								{
+									this.notion.logout();
+									window.location.hash = '';
+								});
+
+								if (!file.isModified())
+								{
+									doLogout();
+								}
+								else
+								{
+									this.confirm(mxResources.get('allChangesLost'), null, doLogout,
+										mxResources.get('cancel'), mxResources.get('discardChanges'));
+								}
+							}
+							else
+							{
+								this.notion.logout();
+							}
+						}), mxResources.get('notion'));
+					}
 					
 					//TODO We have no user info from Trello, how we can create a user?
 					if (this.trello != null)
@@ -7425,6 +7526,10 @@ App.prototype.updateUserElement = function()
 		{
 			user = this.gitLab.getUser();
 		}
+		else if (this.notion != null && this.notion.getUser() != null)
+		{
+			user = this.notion.getUser();
+		}
 		//TODO Trello no user issue
 		
 		if (user != null)

+ 3 - 0
src/main/webapp/js/diagramly/Devel.js

@@ -246,6 +246,9 @@ mxscript(drawDevUrl + 'js/diagramly/TrelloClient.js');
 mxscript(drawDevUrl + 'js/diagramly/GitLabFile.js');
 mxscript(drawDevUrl + 'js/diagramly/GitLabLibrary.js');
 mxscript(drawDevUrl + 'js/diagramly/GitLabClient.js');
+mxscript(drawDevUrl + 'js/diagramly/NotionFile.js');
+mxscript(drawDevUrl + 'js/diagramly/NotionLibrary.js');
+mxscript(drawDevUrl + 'js/diagramly/NotionClient.js');
 
 mxscript(drawDevUrl + 'js/diagramly/App.js');
 mxscript(drawDevUrl + 'js/diagramly/Menus.js');

+ 60 - 2
src/main/webapp/js/diagramly/Dialogs.js

@@ -18,7 +18,7 @@ var StorageDialog = function(editorUi, fn, rowLimit)
 	buttons.style.borderWidth = '1px 0px 1px 0px';
 	buttons.style.padding = '10px 0px 20px 0px';
 	
-	var count = 0;
+	var count = 0, totalBtns = 0;
 	var container = document.createElement('div');
 	container.style.paddingTop = '2px';
 	buttons.appendChild(container);
@@ -27,10 +27,12 @@ var StorageDialog = function(editorUi, fn, rowLimit)
 	
 	function addLogo(img, title, mode, clientName, labels, clientFn)
 	{
+		totalBtns++;
+		
 		if (++count > rowLimit)
 		{
 			mxUtils.br(container);
-			count = 0;
+			count = 1;
 		}
 		
 		var button = document.createElement('a');
@@ -233,6 +235,11 @@ var StorageDialog = function(editorUi, fn, rowLimit)
 		{
 			addLogo(IMAGE_PATH + '/gitlab-logo.svg', mxResources.get('gitlab'), App.MODE_GITLAB, 'gitLab');
 		}
+		
+		if (totalBtns < 6 && editorUi.notion != null)
+		{
+			addLogo(IMAGE_PATH + '/notion-logo.svg', mxResources.get('notion'), App.MODE_NOTION, 'notion');
+		}
 	};
 	
 	div.appendChild(buttons);
@@ -337,6 +344,11 @@ var SplashDialog = function(editorUi)
 		logo.src = IMAGE_PATH + '/gitlab-logo.svg';
 		service = mxResources.get('gitlab');
 	}
+	else if (editorUi.mode == App.MODE_NOTION)
+	{
+		logo.src = IMAGE_PATH + '/notion-logo.svg';
+		service = mxResources.get('notion');
+	}
 	else if (editorUi.mode == App.MODE_BROWSER)
 	{
 		logo.src = IMAGE_PATH + '/osa_database.png';
@@ -460,6 +472,10 @@ var SplashDialog = function(editorUi)
 	{
 		storage = mxResources.get('gitlab');
 	}
+	else if (editorUi.mode == App.MODE_NOTION)
+	{
+		storage = mxResources.get('notion');
+	}
 	else if (editorUi.mode == App.MODE_TRELLO)
 	{
 		storage = mxResources.get('trello');
@@ -598,6 +614,13 @@ var SplashDialog = function(editorUi)
 				editorUi.openLink(DRAWIO_GITLAB_URL + '/users/sign_out');
 			});
 		}
+		else if (editorUi.mode == App.MODE_NOTION && editorUi.notion != null)
+		{
+			addLogout(function()
+			{
+				editorUi.notion.logout();
+			});
+		}
 		else if (editorUi.mode == App.MODE_TRELLO && editorUi.trello != null)
 		{
 			if (editorUi.trello.isAuthorized())
@@ -2416,6 +2439,10 @@ var NewDialog = function(editorUi, compact, showName, callback, createOnly, canc
 	{
 		logo.src = IMAGE_PATH + '/gitlab-logo.svg';
 	}
+	else if (editorUi.mode == App.MODE_NOTION)
+	{
+		logo.src = IMAGE_PATH + '/notion-logo.svg';
+	}
 	else if (editorUi.mode == App.MODE_TRELLO)
 	{
 		logo.src = IMAGE_PATH + '/trello-logo.svg';
@@ -2462,6 +2489,10 @@ var NewDialog = function(editorUi, compact, showName, callback, createOnly, canc
 	{
 		ext = editorUi.gitLab.extension;
 	}
+	else if (editorUi.mode == App.MODE_NOTION && editorUi.notion != null)
+	{
+		ext = editorUi.notion.extension;
+	}
 	else if (editorUi.mode == App.MODE_TRELLO && editorUi.trello != null)
 	{
 		ext = editorUi.trello.extension;
@@ -4084,6 +4115,16 @@ var CreateDialog = function(editorUi, title, createFn, cancelFn, dlgTitle, btnLa
 			addLogo(IMAGE_PATH + '/gitlab-logo.svg', mxResources.get('gitlab'), App.MODE_GITLAB, 'gitLab');
 		}
 
+		if (editorUi.notion != null)
+		{
+			var notionOption = document.createElement('option');
+			notionOption.setAttribute('value', App.MODE_NOTION);
+			mxUtils.write(notionOption, mxResources.get('notion'));
+			serviceSelect.appendChild(notionOption);
+
+			addLogo(IMAGE_PATH + '/notion-logo.svg', mxResources.get('notion'), App.MODE_NOTION, 'notion');
+		}
+		
 		if (typeof window.TrelloClient === 'function')
 		{
 			var trelloOption = document.createElement('option');
@@ -4153,6 +4194,10 @@ var CreateDialog = function(editorUi, title, createFn, cancelFn, dlgTitle, btnLa
 				{
 					ext = editorUi.gitLab.extension;
 				}
+				else if (newMode == App.MODE_NOTION)
+				{
+					ext = editorUi.notion.extension;
+				}
 				else if (newMode == App.MODE_TRELLO)
 				{
 					ext = editorUi.trello.extension;
@@ -5187,6 +5232,8 @@ var LinkDialog = function(editorUi, initialValue, btnLabel, fn, showPages, showN
 			});
 		});
 	}
+
+	//TODO should Notion support this?
 	//TODO should Trello support this?
 	
 	mxEvent.addListener(linkInput, 'keypress', function(e)
@@ -7623,6 +7670,12 @@ var AuthDialog = function(editorUi, peer, showRememberOption, fn)
 		img.src = IMAGE_PATH + '/gitlab-logo.svg';
 		img.style.width = '32px';
 	}
+	else if (peer == editorUi.notion)
+	{
+		service = mxResources.get('notion');
+		img.src = IMAGE_PATH + '/notion-logo-white.svg';
+		img.style.width = '32px';
+	}
 	else if (peer == editorUi.trello)
 	{
 		service = mxResources.get('trello');
@@ -11348,6 +11401,11 @@ var BtnDialog = function(editorUi, peer, btnLbl, fn)
 		service = mxResources.get('gitlab');
 		img.src = IMAGE_PATH + '/gitlab-logo.svg';
 	}
+	else if (peer == editorUi.notion)
+	{
+		service = mxResources.get('notion');
+		img.src = IMAGE_PATH + '/notion-logo.svg';
+	}
 	else if (peer == editorUi.trello)
 	{
 		service = mxResources.get('trello');

+ 5 - 0
src/main/webapp/js/diagramly/EditorUi.js

@@ -12964,6 +12964,11 @@
 			serviceCount++
 		}
 		
+		if (this.notion != null)
+		{
+			serviceCount++
+		}
+		
 		if (allowBrowser && isLocalStorage && urlParams['browser'] == '1')
 		{
 			serviceCount++

+ 36 - 0
src/main/webapp/js/diagramly/Menus.js

@@ -2407,6 +2407,15 @@
 				}, parent);
 			}
 
+			if (editorUi.notion != null)
+			{
+				menu.addSeparator(parent);
+				menu.addItem(mxResources.get('notion') + '...', null, function()
+				{
+					pickFileFromService(editorUi.notion);
+				}, parent);
+			}
+
 			if (editorUi.trello != null)
 			{
 				menu.addItem(mxResources.get('trello') + '...', null, function()
@@ -3020,6 +3029,15 @@
 				}, parent);
 			}
 
+			if (editorUi.notion != null)
+			{
+				menu.addSeparator(parent);
+				menu.addItem(mxResources.get('notion') + '...', null, function()
+				{
+					editorUi.pickFile(App.MODE_NOTION);
+				}, parent);
+			}
+
 			if (editorUi.trello != null)
 			{
 				menu.addItem(mxResources.get('trello') + '...', null, function()
@@ -3151,6 +3169,15 @@
 					}, parent);
 				}
 				
+				if (editorUi.notion != null)
+				{
+					menu.addSeparator(parent);
+					menu.addItem(mxResources.get('notion') + '...', null, function()
+					{
+						editorUi.showLibraryDialog(null, null, null, null, App.MODE_NOTION);
+					}, parent);
+				}
+
 				if (editorUi.trello != null)
 				{
 					menu.addItem(mxResources.get('trello') + '...', null, function()
@@ -3253,6 +3280,15 @@
 					}, parent);
 				}
 				
+				if (editorUi.notion != null)
+				{
+					menu.addSeparator(parent);
+					menu.addItem(mxResources.get('notion') + '...', null, function()
+					{
+						editorUi.pickLibrary(App.MODE_NOTION);
+					}, parent);
+				}
+
 				if (editorUi.trello != null)
 				{
 					menu.addItem(mxResources.get('trello') + '...', null, function()

+ 804 - 0
src/main/webapp/js/diagramly/NotionClient.js

@@ -0,0 +1,804 @@
+/**
+ * Copyright (c) 2006-2021, JGraph Ltd
+ * Copyright (c) 2006-2021, draw.io AG
+ */
+
+//Add a closure to hide the class private variables without changing the code a lot
+(function ()
+{
+
+var _token = null;
+
+window.NotionClient = function(editorUi)
+{
+	DrawioClient.call(this, editorUi, 'notionAuthInfo');
+};
+
+// Extends DrawioClient
+mxUtils.extend(NotionClient, DrawioClient);
+
+/**
+ *
+ */
+NotionClient.prototype.extension = '.drawio';
+
+/**
+ * 
+ */
+NotionClient.prototype.baseUrl = window.NOTION_API_URL || ((window.location.hostname == 'test.draw.io') ?
+						'https://notion.jgraph.workers.dev/api' : '/notion-api');
+
+NotionClient.prototype.getTitle = function (obj)
+{
+	if (typeof obj.title === 'string')
+	{
+		return obj.title;
+	}
+	else
+	{
+		var title = [];
+		
+		for (var i = 0; i < obj.title.length; i++)
+		{
+			title.push(obj.title[i].text.content)
+		}
+		
+		return title.join(' ');
+	}
+};
+	
+NotionClient.prototype.authenticate = function(success, error, failOnAuth)
+{
+	var acceptAuthResponse = true;
+
+	var errFn = mxUtils.bind(this, function()
+	{
+		if (acceptAuthResponse)
+		{
+			acceptAuthResponse = false;
+			error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
+			{
+				this.ui.hideDialog();
+				auth();
+			})});
+		}
+	});
+	
+	var auth = mxUtils.bind(this, function()
+	{
+		acceptAuthResponse = true;
+		
+		this.ui.showAuthDialog(this, true, mxUtils.bind(this, function(remember, authSuccess)
+		{
+			var tokenDlg = new FilenameDialog(this.ui, '',
+				mxResources.get('ok'), mxUtils.bind(this, function(token)
+				{
+					//check token is valid
+					_token = token;
+					
+					//TODO use any simpler request if one becomes available
+					this.executeRequest('/v1/databases', null, 'GET', mxUtils.bind(this, function()
+					{
+						this.executeRequest('/setToken', null, 'GET', mxUtils.bind(this, function()
+						{
+							acceptAuthResponse = false;
+							
+							if (remember)
+							{
+								_token = null;
+							}
+							
+							if (authSuccess != null)
+							{
+								authSuccess();
+							}
+							
+							success();
+						}), errFn, failOnAuth);
+					}), errFn, failOnAuth);
+				}), mxResources.get('notionToken'), function(token)
+				{
+					return token != null && token.length > 0;
+				}, null, 'https://developers.notion.com/docs/getting-started#step-1-create-an-integration');
+				
+			this.ui.showDialog(tokenDlg.container, 300, 80, true, true);
+			tokenDlg.init();
+		}), errFn);
+	});
+	
+	auth();
+};
+
+/**
+ * Checks if the client is authorized and calls the next step.
+ */
+NotionClient.prototype.executeRequest = function(url, data, method, success, error, failOnAuth)
+{
+	var doExecute = mxUtils.bind(this, function()
+	{
+		var acceptResponse = true;
+		
+		var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
+		{
+			acceptResponse = false;
+			error({code: App.ERROR_TIMEOUT, retry: doExecute});
+		}), this.ui.timeout);
+
+		var req = new mxXmlRequest(this.baseUrl + url, data, method);
+		
+		req.withCredentials = true;
+		
+		req.setRequestHeaders = mxUtils.bind(this, function(request, params)
+		{
+			if (_token != null)
+			{
+				request.setRequestHeader('Authorization', 'Bearer ' + _token);
+			}
+			
+			request.setRequestHeader('Notion-Version', '2021-05-13');
+			request.setRequestHeader('Content-Type', 'application/json');
+		});
+		
+		req.send(mxUtils.bind(this, function(req)
+		{
+			window.clearTimeout(timeoutThread);
+			
+			if (acceptResponse)
+			{
+				// 404 (file not found) is a valid response for checkExists
+				if (req.getStatus() >= 200 && req.getStatus() <= 299)
+				{
+					if (this.user == null)
+					{
+						this.setUser(new DrawioUser('notion', null, 'Notion'));
+					}
+					
+					success(JSON.parse(req.getText()));
+				}
+				else if (!failOnAuth && (req.getStatus() === 401 || req.getStatus() === 400))
+				{
+					this.setUser(null);
+					failOnAuth = true;
+					//Authorize again using the refresh token
+					this.authenticate(function()
+					{
+						doExecute();
+					}, error, failOnAuth);
+				}
+				else
+				{
+					error(this.parseRequestText(req));
+				}
+			}
+		}), mxUtils.bind(this, function(err)
+		{
+			window.clearTimeout(timeoutThread);
+				    	
+			if (acceptResponse)
+			{
+				error(err);
+			}
+		}));
+	});
+	
+	doExecute();
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.getLibrary = function(id, success, error)
+{
+	this.getFile(id, success, error, false, true);
+};
+
+/**
+ *
+ */
+NotionClient.prototype.getFile = function(id, success, error, denyConvert, asLibrary)
+{
+	asLibrary = (asLibrary != null) ? asLibrary : false;
+	
+	this.executeRequest('/v1/pages/' + encodeURIComponent(id), null, 'GET', mxUtils.bind(this, function(fileInfo)
+	{
+		try
+		{
+			var xmlParts = fileInfo.properties.xml.rich_text, xml = '';
+			var fileName = this.getTitle(fileInfo.properties.Name);
+			
+			for (var i = 0; i < xmlParts.length; i++)
+			{
+				xml += xmlParts[i].text.content;
+			}
+			
+			var meta = {id: id, name: fileName};
+			
+			if (asLibrary)
+			{
+				success(new NotionLibrary(this.ui, xml, meta));
+			}
+			else
+			{
+				success(new NotionFile(this.ui, xml, meta));
+			}
+		}
+		catch(e)
+		{
+			if (error != null)
+			{
+				error(e);
+			}
+			else
+			{
+				throw e;
+			}
+		}
+	}), error);
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.insertLibrary = function(filename, data, success, error, folderId)
+{
+	this.insertFile(filename, data, success, error, true, folderId);
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.insertFile = function(filename, data, success, error, asLibrary, folderId)
+{
+	asLibrary = (asLibrary != null) ? asLibrary : false;
+	
+	this.checkExists(folderId, filename, true, mxUtils.bind(this, function(checked, currentId)
+	{
+		if (checked)
+		{
+			this.writeFile(currentId? '/v1/pages/' + encodeURIComponent(currentId) : '/v1/pages', 
+					currentId? null : folderId, filename, data, currentId? 'PATCH' : 'POST',
+					mxUtils.bind(this, function(fileInfo)
+			{
+				var meta = {id: fileInfo.id, name: this.getTitle(fileInfo.properties.Name)};
+			
+				if (asLibrary)
+				{
+					success(new NotionLibrary(this.ui, data, meta));
+				}
+				else
+				{
+					success(new NotionFile(this.ui, data, meta));
+				}
+			}), error);
+		}
+		else
+		{
+			error();
+		}
+	}))
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.checkExists = function(parentId, filename, askReplace, fn)
+{
+	this.executeRequest('/v1/databases/' + encodeURIComponent(parentId) + '/query', JSON.stringify({
+		filter: {
+	        property: 'Name',
+			text: {
+			    equals: filename
+			}
+		}
+	}), 'POST', mxUtils.bind(this, function(resp)
+	{
+		if (resp.results.length == 0)
+		{
+			fn(true);
+		}
+		else
+		{
+			if (askReplace)
+			{
+				this.ui.spinner.stop();
+				
+				this.ui.confirm(mxResources.get('replaceIt', [filename]), function()
+				{
+					fn(true, resp.results[0].id);
+				}, function()
+				{
+					fn(false);
+				});
+			}
+			else
+			{
+				this.ui.spinner.stop();
+				
+				this.ui.showError(mxResources.get('error'), mxResources.get('fileExists'), mxResources.get('ok'), function()
+				{
+					fn(false);						
+				});
+			}
+		}
+	}), function(req)
+	{
+		fn(false);
+	});
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.saveFile = function(file, success, error)
+{
+	try
+	{
+		var data = file.getData();
+		
+		this.writeFile('/v1/pages/' + file.getId(), null, file.getTitle(), data, 
+				'PATCH', mxUtils.bind(this, function(resp)
+				{
+					success(resp, data);
+				}), error);
+	}
+	catch (e)
+	{
+		error(e);
+	}
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.writeFile = function(url, folderId, filename, data, method, success, error)
+{
+	try
+	{
+		if (url != null && data != null)
+		{
+			//Notion has a limit on rich-text pages of 200KB
+			if (data.length > 200000 /*200KB*/)
+			{
+				error({message: mxResources.get('drawingTooLarge') + ' (' +
+					this.ui.formatFileSize(data.length) + ' / 200 KB)'});
+				
+				return;
+			}
+			
+			var richTxt = []
+			var parts = Math.ceil(data.length / 2000);
+			
+			for (var i = 0; i < parts; i++)
+			{
+				richTxt.push({
+					text: {
+                    	content: data.substr(i * 2000, 2000)
+	                }
+				});
+			}
+			
+			var reqBody = {
+				properties: {
+					Name: {
+	                	title: [{
+							text: {
+								content: filename
+							}
+	                    }]
+	                },
+					xml: {
+	                    rich_text: richTxt
+	                }
+	  			}
+			};
+			
+			if (folderId)
+			{
+				reqBody['parent'] = { database_id: folderId };
+			}
+			
+			this.executeRequest(url, JSON.stringify(reqBody), method, success, error);
+		}
+		else
+		{
+			error({message: mxResources.get('unknownError')});
+		}
+	}
+	catch (e)
+	{
+		error(e);
+	}
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.parseRequestText = function(req)
+{
+	var result = {message: mxResources.get('unknownError')};
+	
+	try
+	{
+		result = JSON.parse(req.getText());
+	}
+	catch (e)
+	{
+		// ignore
+	}
+	
+	return result;
+};
+
+/**
+ * Checks if the client is authorized and calls the next step.
+ */
+NotionClient.prototype.pickLibrary = function(fn)
+{
+	this.pickFile(fn);
+};
+
+/**
+ * Checks if the client is authorized and calls the next step.
+ */
+NotionClient.prototype.pickFolder = function(fn)
+{
+	this.showNotionDialog(false, fn);
+};
+
+NotionClient.prototype.pickFile = function(fn)
+{
+	fn = (fn != null) ? fn : mxUtils.bind(this, function(id)
+	{
+		this.ui.loadFile('N' + encodeURIComponent(id));
+	});
+	
+	this.showNotionDialog(true, fn);
+};
+
+/**
+ * 
+ */
+NotionClient.prototype.showNotionDialog = function(showFiles, fn)
+{
+	var itemId, itemName;
+	
+	var content = document.createElement('div');
+	content.style.whiteSpace = 'nowrap';
+	content.style.overflow = 'hidden';
+	content.style.height = '304px';
+
+	var hd = document.createElement('h3');
+	mxUtils.write(hd, mxResources.get((showFiles) ? 'officeSelDiag' : 'selectDB'));
+	hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:12px';
+	content.appendChild(hd);
+
+	var div = document.createElement('div');
+	div.style.whiteSpace = 'nowrap';
+	div.style.border = '1px solid lightgray';
+	div.style.boxSizing = 'border-box';
+	div.style.padding = '4px';
+	div.style.overflow = 'auto';
+	div.style.lineHeight = '1.2em';
+	div.style.height = '274px';
+	content.appendChild(div);
+	
+	var listItem = document.createElement('div');
+	listItem.style.textOverflow = 'ellipsis';
+	listItem.style.boxSizing = 'border-box';
+	listItem.style.overflow = 'hidden';
+	listItem.style.padding = '4px';
+	listItem.style.width = '100%';
+	
+	var dlg = new CustomDialog(this.ui, content, mxUtils.bind(this, function()
+	{
+		fn(itemId);
+	}));
+	this.ui.showDialog(dlg.container, 420, 360, true, true);
+	
+	if (showFiles)
+	{
+		dlg.okButton.parentNode.removeChild(dlg.okButton);
+	}
+	
+	var createLink = mxUtils.bind(this, function(label, exec, padding, underline)
+	{
+		var link = document.createElement('a');
+		link.setAttribute('title', label);
+		link.style.cursor = 'pointer';
+		mxUtils.write(link,  label);
+		mxEvent.addListener(link, 'click', exec);
+		
+		if (underline)
+		{
+			link.style.textDecoration = 'underline';
+		}
+		
+		if (padding != null)
+		{
+			var temp = listItem.cloneNode();
+			temp.style.padding = padding;
+			temp.appendChild(link);
+			
+			link = temp;
+		}
+		
+		return link;
+	});
+	
+	var updatePathInfo = mxUtils.bind(this, function()
+	{
+		var dbInfo = document.createElement('div');
+		dbInfo.style.marginBottom = '8px';
+		
+		dbInfo.appendChild(createLink(itemName, mxUtils.bind(this, function()
+		{
+			itemId = null;
+			selectDB();
+		}), null, true));
+		
+		div.appendChild(dbInfo);
+	});
+	
+	var error = mxUtils.bind(this, function(err)
+	{
+		// Pass a dummy notFoundMessage to bypass special handling 
+		this.ui.handleError(err, null, mxUtils.bind(this, function()
+		{
+			this.ui.spinner.stop();
+			
+			if (this.getUser() != null)
+			{
+				itemId = null;
+				
+				selectDB();
+			}
+			else
+			{
+				this.ui.hideDialog();
+			}
+		}), null, {});
+	});
+	
+	// Adds paging for DBs and diagrams (DB pages)
+	var nextPageDiv = null;
+	var scrollFn = null;
+	var pageSize = 100;
+
+	var selectFile = mxUtils.bind(this, function(nextCursor)
+	{
+		if (nextCursor == null)
+		{
+			div.innerHTML = '';
+		}
+		
+		this.ui.spinner.spin(div, mxResources.get('loading'));
+		dlg.okButton.removeAttribute('disabled');
+		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+			scrollFn = null;
+		}
+		
+		if (nextPageDiv != null && nextPageDiv.parentNode != null)
+		{
+			nextPageDiv.parentNode.removeChild(nextPageDiv);
+		}
+		
+		nextPageDiv = document.createElement('a');
+		nextPageDiv.style.display = 'block';
+		nextPageDiv.style.cursor = 'pointer';
+		mxUtils.write(nextPageDiv, mxResources.get('more') + '...');
+		
+		var nextPage = mxUtils.bind(this, function()
+		{
+			selectFile(nextCursor);
+		});
+		
+		mxEvent.addListener(nextPageDiv, 'click', nextPage);
+		
+		var reqBody = {
+			page_size: pageSize
+		};
+		
+		if (nextCursor != null)
+		{
+			reqBody.start_cursor = nextCursor;
+		}
+		
+		this.executeRequest('/v1/databases/' + encodeURIComponent(itemId) + '/query', 
+			JSON.stringify(reqBody), 'POST', mxUtils.bind(this, function(resp)
+		{
+			this.ui.spinner.stop();
+			
+			if (nextCursor == null)
+			{
+				updatePathInfo();
+				
+				div.appendChild(createLink('../ [Up]', mxUtils.bind(this, function()
+				{
+					itemId = null;
+					selectDB();
+				}), '4px'));
+			}
+
+			var files = resp.results;
+			
+			if (files == null || files.length == 0)
+			{
+				mxUtils.write(div, mxResources.get('noFiles'));
+			}
+			else
+			{
+				var gray = true;
+				
+				for (var i = 0; i < files.length; i++)
+				{
+					(mxUtils.bind(this, function(file, idx)
+					{
+						var temp = listItem.cloneNode();
+						temp.style.backgroundColor = (gray) ?
+							((Editor.isDarkMode()) ? '#000000' : '#eeeeee') : '';
+						gray = !gray;
+
+						var typeImg = document.createElement('img');
+						typeImg.src = IMAGE_PATH + '/file.png';
+						typeImg.setAttribute('align', 'absmiddle');
+						typeImg.style.marginRight = '4px';
+						typeImg.style.marginTop = '-4px';
+						typeImg.width = 20;
+						temp.appendChild(typeImg);
+						
+						temp.appendChild(createLink(this.getTitle(file.properties.Name), mxUtils.bind(this, function()
+						{
+							this.ui.hideDialog();
+							fn(file.id);
+						})));
+						
+						div.appendChild(temp);
+					}))(files[i], i);
+				}
+				
+				if (resp.has_more)
+				{
+					nextCursor = resp.next_cursor;
+					
+					div.appendChild(nextPageDiv);
+					
+					scrollFn = function()
+					{
+						if (div.scrollTop >= div.scrollHeight - div.offsetHeight)
+						{
+							nextPage();
+						}
+					};
+					
+					mxEvent.addListener(div, 'scroll', scrollFn);
+				}
+			}
+		}), error, true);
+	});
+
+	var selectDB = mxUtils.bind(this, function(nextCursor)
+	{
+		if (nextCursor == null)
+		{
+			div.innerHTML = '';
+		}
+		
+		dlg.okButton.setAttribute('disabled', 'disabled');
+		this.ui.spinner.spin(div, mxResources.get('loading'));
+		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+		}
+		
+		if (nextPageDiv != null && nextPageDiv.parentNode != null)
+		{
+			nextPageDiv.parentNode.removeChild(nextPageDiv);
+		}
+		
+		nextPageDiv = document.createElement('a');
+		nextPageDiv.style.display = 'block';
+		nextPageDiv.style.cursor = 'pointer';
+		mxUtils.write(nextPageDiv, mxResources.get('more') + '...');
+		
+		var nextPage = mxUtils.bind(this, function()
+		{
+			selectDB(nextCursor);
+		});
+		
+		mxEvent.addListener(nextPageDiv, 'click', nextPage);
+
+		this.executeRequest('/v1/databases?page_size=' + pageSize + (nextCursor != null? '&start_cursor=' + nextCursor : ''), 
+			null, 'GET', mxUtils.bind(this, function(resp)
+		{
+			this.ui.spinner.stop();
+			var dbs = resp.results;
+			var count = 0;
+			
+			if (dbs == null || dbs.length == 0)
+			{
+				mxUtils.write(div, mxResources.get('noDBs'));
+			}
+			else
+			{
+				for (var i = 0; i < dbs.length; i++)
+				{
+					if (!dbs[i].properties.Name || dbs[i].properties.Name.type != 'title' 
+							|| !dbs[i].properties.xml || dbs[i].properties.xml.type != 'rich_text') continue;
+					
+					(mxUtils.bind(this, function(db, idx)
+					{
+						var temp = listItem.cloneNode();
+						temp.style.backgroundColor = (idx % 2 == 0) ?
+							((Editor.isDarkMode()) ? '#000000' : '#eeeeee') : '';
+						
+						temp.appendChild(createLink(this.getTitle(db), mxUtils.bind(this, function()
+						{
+							itemId = db.id;
+							itemName = this.getTitle(db);
+	
+							if (showFiles)
+							{
+								selectFile();
+							}
+							else
+							{
+								this.ui.hideDialog();
+								fn(itemId);
+							}
+						})));
+						
+						div.appendChild(temp);
+					}))(dbs[i], i);
+					
+					count++;
+				}
+			}
+
+			if (resp.has_more)
+			{
+				nextCursor = resp.next_cursor;
+				
+				if (count == 0)
+				{
+					nextPage();
+					return;
+				}
+				
+				div.appendChild(nextPageDiv);
+				
+				scrollFn = function()
+				{
+					if (div.scrollTop >= div.scrollHeight - div.offsetHeight)
+					{
+						nextPage();
+					}
+				};
+				
+				mxEvent.addListener(div, 'scroll', scrollFn);
+			}
+			else if (count == 0 && div.innerHTML == '')
+			{
+				mxUtils.write(div, mxResources.get('noDBs'));
+			}
+		}), error);
+	});
+	
+	selectDB();
+};
+
+/**
+ *
+ */
+NotionClient.prototype.logout = function()
+{
+	//Send to server to clear token cookie
+	this.executeRequest('/removeToken', null, 'GET', function(){}, function(){});
+	this.setUser(null);
+	_token = null;
+};
+
+})();

+ 202 - 0
src/main/webapp/js/diagramly/NotionFile.js

@@ -0,0 +1,202 @@
+/**
+ * Copyright (c) 2006-2021, JGraph Ltd
+ * Copyright (c) 2006-2021, Gaudenz Alder
+ */
+NotionFile = function(ui, data, meta)
+{
+	DrawioFile.call(this, ui, data);
+	
+	this.meta = meta;
+	this.peer = this.ui.notion;
+};
+
+//Extends mxEventSource
+mxUtils.extend(NotionFile, DrawioFile);
+
+/**
+ * 
+ */
+NotionFile.prototype.getId = function()
+{
+	return this.meta.id;
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.getHash = function()
+{
+	return encodeURIComponent('N' + this.getId());
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.getMode = function()
+{
+	return App.MODE_NOTION;
+};
+
+/**
+ * Overridden to enable the autosave option in the document properties dialog.
+ */
+NotionFile.prototype.isAutosave = function()
+{
+	return false;
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.getTitle = function()
+{
+	return this.meta.name;
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.isRenamable = function()
+{
+	return false;
+};
+
+
+/**
+ * 
+ */
+NotionFile.prototype.isCompressedStorage = function()
+{
+	return true;
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.save = function(revision, success, error, unloading, overwrite)
+{
+	this.doSave(this.getTitle(), success, error, unloading, overwrite);
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.saveAs = function(title, success, error)
+{
+	this.doSave(title, success, error);
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.doSave = function(title, success, error, unloading, overwrite)
+{
+	// Forces update of data for new extensions
+	var prev = this.meta.name;
+	this.meta.name = title;
+	
+	DrawioFile.prototype.save.apply(this, [null, mxUtils.bind(this, function()
+	{
+		this.meta.name = prev;
+		this.saveFile(title, false, success, error, unloading, overwrite);
+	}), error, unloading, overwrite]);
+};
+
+/**
+ * 
+ */
+NotionFile.prototype.saveFile = function(title, revision, success, error, unloading, overwrite)
+{
+	if (!this.isEditable())
+	{
+		if (success != null)
+		{
+			success();
+		}
+	}
+	else if (!this.savingFile)
+	{
+		if (this.getTitle() == title)
+		{
+			try
+			{
+				// Sets shadow modified state during save
+				this.savingFileTime = new Date();
+				this.setShadowModified(false);
+				this.savingFile = true;
+					
+				var savedData = this.data;
+
+				this.peer.saveFile(this, mxUtils.bind(this, function()
+				{
+					// Checks for changes during save
+					this.setModified(this.getShadowModified());
+					this.savingFile = false;
+					
+					if (success != null)
+					{
+						success();
+					}
+				}),
+				mxUtils.bind(this, function(err)
+				{
+					this.savingFile = false;
+
+					if (error != null)
+					{
+						error(err);
+					}
+				}), overwrite);
+			}
+			catch (e)
+			{
+				this.savingFile = false;
+				
+				if (error != null)
+				{
+					error(e);
+				}
+				else
+				{
+					throw e;
+				}
+			}
+		}
+		else
+		{
+			// Sets shadow modified state during save
+			this.savingFileTime = new Date();
+			this.setShadowModified(false);
+			this.savingFile = true;
+			
+			this.ui.pickFolder(this.getMode(), mxUtils.bind(this, function(folderId)
+			{
+				this.peer.insertFile(title, this.getData(), mxUtils.bind(this, function(file)
+				{
+					// Checks for changes during save
+					this.setModified(this.getShadowModified());
+					this.savingFile = false;
+					
+					if (success != null)
+					{
+						success();
+					}
+					
+					this.ui.fileLoaded(file);
+				}), mxUtils.bind(this, function()
+				{
+					this.savingFile = false;
+					
+					if (error != null)
+					{
+						error();
+					}
+				}), false, folderId);
+			}));
+		}
+	}
+	else if (error != null)
+	{
+		error({code: App.ERROR_BUSY});
+	}
+};

+ 28 - 0
src/main/webapp/js/diagramly/NotionLibrary.js

@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2006-2021, JGraph Ltd
+ * Copyright (c) 2006-2021, Gaudenz Alder
+ */
+NotionLibrary = function(ui, data, meta)
+{
+	NotionFile.call(this, ui, data, meta);
+};
+
+//Extends mxEventSource
+mxUtils.extend(NotionLibrary, NotionFile);
+
+/**
+ * Overridden to avoid updating data with current file.
+ */
+NotionLibrary.prototype.doSave = function(title, success, error)
+{
+	this.saveFile(title, false, success, error);
+};
+
+/**
+ * Returns the location as a new object.
+ * @type mx.Point
+ */
+NotionLibrary.prototype.open = function()
+{
+	// Do nothing - this should never be called
+};

文件差异内容过多而无法显示
+ 40 - 40
src/main/webapp/js/viewer-static.min.js


文件差异内容过多而无法显示
+ 40 - 40
src/main/webapp/js/viewer.min.js


文件差异内容过多而无法显示
+ 1 - 1
src/main/webapp/mxgraph/mxClient.js


+ 1 - 0
src/main/webapp/plugins/googledrive.js

@@ -7,6 +7,7 @@ Draw.loadPlugin(function(ui)
 	ui.dropbox = null;
 	ui.gitLab = null;
 	ui.gitHub = null;
+	ui.notion = null;
 	ui.trello = null;
 
 	window.OneDriveClient = null;

+ 1 - 0
src/main/webapp/resources/dia.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_am.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ar.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_bg.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_bn.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_bs.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ca.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 137 - 136
src/main/webapp/resources/dia_cs.txt

@@ -1014,140 +1014,141 @@ pageIdsExpTrg=Export target
 confALucidDiagImgImported={2} diagram "{1}" image extracted successfully
 confASavingLucidDiagImgFailed=Extracting {2} diagram "{1}" image failed
 confGetInfoFailed=Fetching file info from {1} failed.
-confCheckCacheFailed=Cannot get cached file info.
-confReadFileErr=Cannot read "{1}" file from {2}.
-confSaveCacheFailed=Unexpected error. Cannot save cached file
-orgChartType=Org Chart Type
-linear=Linear
-hanger2=Hanger 2
-hanger4=Hanger 4
-fishbone1=Fishbone 1
-fishbone2=Fishbone 2
-1ColumnLeft=Single Column Left
-1ColumnRight=Single Column Right
-smart=Smart
-parentChildSpacing=Parent Child Spacing
-siblingSpacing=Sibling Spacing
-confNoPermErr=Sorry, you don't have enough permissions to view this embedded diagram from page {1}
-copyAsImage=Copy as Image
-lucidImport=Lucidchart Import
-lucidImportInst1=Click the "Start Import" button to import all Lucidchart diagrams.
-installFirst=Please install {1} first
-drawioChromeExt=draw.io Chrome Extension
-loginFirstThen=Please login to {1} first, then {2}
-errFetchDocList=Error: Couldn't fetch documents list
-builtinPlugins=Built-in Plugins
-extPlugins=External Plugins
-backupFound=Backup file found
-chromeOnly=This feature only works in Google Chrome
-msgDeleted=This message has been deleted
-confAErrFetchDrawList=Error fetching diagrams list. Some diagrams are skipped.
-confAErrCheckDrawDiag=Cannot check diagram {1}
-confAErrFetchPageList=Error fetching pages list
-confADiagImportIncom={1} diagram "{2}" is imported partially and may have missing shapes
-invalidSel=Invalid selection
-diagNameEmptyErr=Diagram name cannot be empty
-openDiagram=Open Diagram
-newDiagram=New diagram
-editable=Editable
-confAReimportStarted=Re-import {1} diagrams started...
-spaceFilter=Filter by spaces
-curViewState=Current Viewer State
-pageLayers=Page and Layers
-customize=Customize
-firstPage=First Page (All Layers)
-curEditorState=Current Editor State
-noAnchorsFound=No anchors found 
-attachment=Attachment
-curDiagram=Current Diagram
-recentDiags=Recent Diagrams
-csvImport=CSV Import
-chooseFile=Choose a file...
-choose=Choose
-gdriveFname=Google Drive filename
-widthOfViewer=Width of the viewer (px)
-heightOfViewer=Height of the viewer (px)
-autoSetViewerSize=Automatically set the size of the viewer
-thumbnail=Thumbnail
-prevInDraw=Preview in draw.io
-onedriveFname=OneDrive filename
-diagFname=Diagram filename
-diagUrl=Diagram URL
-showDiag=Show Diagram
-diagPreview=Diagram Preview
-csvFileUrl=CSV File URL
-generate=Generate
-selectDiag2Insert=Please select a diagram to insert it.
-errShowingDiag=Unexpected error. Cannot show diagram
-noRecentDiags=No recent diagrams found
-fetchingRecentFailed=Failed to fetch recent diagrams
-useSrch2FindDiags=Use the search box to find draw.io diagrams
-cantReadChckPerms=Cannot read the specified diagram. Please check you have read permission on that file.
-cantFetchChckPerms=Cannot fetch diagram info. Please check you have read permission on that file.
-searchFailed=Searching failed. Please try again later.
-plsTypeStr=Please type a search string.
-unsupportedFileChckUrl=Unsupported file. Please check the specified URL
-diagNotFoundChckUrl=Diagram not found or cannot be accessed. Please check the specified URL
-csvNotFoundChckUrl=CSV file not found or cannot be accessed. Please check the specified URL
-cantReadUpload=Cannot read the uploaded diagram
-select=Select
-errCantGetIdType=Unexpected Error: Cannot get content id or type.
-errGAuthWinBlocked=Error: Google Authentication window blocked
-authDrawAccess=Authorize draw.io to access {1}
-connTimeout=The connection has timed out
-errAuthSrvc=Error authenticating to {1}
-plsSelectFile=Please select a file
-mustBgtZ={1} must be greater than zero
-cantLoadPrev=Cannot load file preview.
-errAccessFile=Error: Access Denied. You do not have permission to access "{1}".
-noPrevAvail=No preview is available.
-personalAccNotSup=Personal accounts are not supported.
-errSavingTryLater=Error occured during saving, please try again later.
-plsEnterFld=Please enter {1}
-invalidDiagUrl=Invalid Diagram URL
-unsupportedVsdx=Unsupported vsdx file
-unsupportedImg=Unsupported image file
-unsupportedFormat=Unsupported file format
-plsSelectSingleFile=Please select a single file only
-attCorrupt=Attachment file "'{1}" is corrupted
-loadAttFailed=Failed to load attachment "{1}"
-embedDrawDiag=Embed draw.io Diagram
-addDiagram=Add Diagram
-embedDiagram=Embed Diagram
-editOwningPg=Edit owning page
-deepIndexing=Deep Indexing (Index diagrams that aren't used in any page also)
-confADeepIndexStarted=Deep Indexing Started
-confADeepIndexDone=Deep Indexing Done
-officeNoDiagramsSelected=No diagrams found in the selection
-officeNoDiagramsInDoc=No diagrams found in the document
-officeNotSupported=This feature is not supported in this host application
-someImagesFailed={1} out of {2} failed due to the following errors
-importingNoUsedDiagrams=Importing {1} Diagrams not used in pages
-importingDrafts=Importing {1} Diagrams in drafts
-processingDrafts=Processing drafts
-updatingDrafts=Updating drafts
-updateDrafts=Update drafts
-notifications=Notifications
-drawioImp=draw.io Import
-confALibsImp=Importing draw.io Libraries
-confALibsImpFailed=Importing {1} library failed
-contributors=Contributors
-drawDiagrams=draw.io Diagrams
-errFileNotFoundOrNoPer=Error: Access Denied. File not found or you do not have permission to access "{1}" on {2}.
-confACheckPagesWEmbed=Checking pages having embedded draw.io diagrams.
-confADelBrokenEmbedDiagLnk=Removing broken embedded diagram links
-replaceWith=Replace with
-replaceAll=Replace All
-confASkipDiagModified=Skipped "{1}" as it was modified after initial import
-replFind=Replace/Find
-matchesRepl={1} matches replaced
-draftErrDataLoss=An error occurred while reading the draft file. The diagram cannot be edited now to prevent any possible data loss. Please try again later or contact support.
+confCheckCacheFailed=Nedaří se získat informace o souboru v mezipaměti.
+confReadFileErr=Nedaří se číst soubor „{1}“ z {2}.
+confSaveCacheFailed=Neočekávaná chyba: Soubor z mezipaměti se nedaří uložit
+orgChartType=Typ organizačního diagramu
+linear=Lineární
+hanger2=Věšák 2
+hanger4=Věšák 4
+fishbone1=Rybí kost 1
+fishbone2=Rybí kost 2
+1ColumnLeft=Jeden sloupec vlevo
+1ColumnRight=Jeden sloupec vpravo
+smart=Inteligentní
+parentChildSpacing=Rozestupy mezi nadřazeným a podřízeným prvkem
+siblingSpacing=Rozestupy mezi prvky na stejné úrovni
+confNoPermErr=Je nám líto, ale nemáte dostatečné oprávnění pro zobrazení tohoto vestavěného diagramu ze stránky {1}
+copyAsImage=Zkopírovat jako obrázek
+lucidImport=Import Lucidchart
+lucidImportInst1=Pokud chcete importovat všechny Lucidchart diagramy, klikněte na tlačítko „Zahájit import“.
+installFirst=Nejprve nainstalujte {1}
+drawioChromeExt=Rozšíření draw.io pro prohlížeč Chrome
+loginFirstThen=Nejprve se přihlašte do {1}, pak {2}
+errFetchDocList=Chyba: nedaří se získat seznam dokumentů
+builtinPlugins=Vestavěné zásuvné moduly
+extPlugins=Externí zásuvné moduly
+backupFound=Nalezen soubor se zálohou
+chromeOnly=Tato funkce funguje pouze v prohlížeči Google Chrome
+msgDeleted=Tato zpráva byla smazána.
+confAErrFetchDrawList=Chyba při získávání seznamu diagramů. Některé diagramy byly přeskočeny.
+confAErrCheckDrawDiag=Nedaří se zkontrolovat diagram {1}
+confAErrFetchPageList=Chyba při získávání seznamu stránek
+confADiagImportIncom={1} diagram „{2}“ je importován jen z části a některé tvary mohou chybět
+invalidSel=Neplatný výběr
+diagNameEmptyErr=Je třeba vyplnit název diagramu
+openDiagram=Otevřít diagram
+newDiagram=Nový diagram
+editable=Upravitelné
+confAReimportStarted=Opětovný import {1} diagramů zahájen…
+spaceFilter=Filtrovat podle mezer
+curViewState=Stav stávajícího prohlížeče
+pageLayers=Stránka a vrstvy
+customize=Přizpůsobit
+firstPage=První stránka (všechny vrstvy)
+curEditorState=Stávající stav editoru
+noAnchorsFound=Nenalezeny žádné kotvy
+attachment=Příloha
+curDiagram=Stávající diagram
+recentDiags=Nedávno otevřené diagramy
+csvImport=Import CSV
+chooseFile=Zvolte soubor…
+choose=Zvolit
+gdriveFname=Název souboru na službě Google Drive
+widthOfViewer=Šířka prohlížeče (pixely)
+heightOfViewer=Výška prohlížeče (pixely)
+autoSetViewerSize=Automaticky nastavit velikost prohlížeče
+thumbnail=Náhled
+prevInDraw=Zobrazit náhled v draw.io
+onedriveFname=Název souboru na službě OneDrive
+diagFname=Název souboru s diagramem
+diagUrl=URL adresa diagramu
+showDiag=Zobrazit diagram
+diagPreview=Náhled diagramu
+csvFileUrl=URL adresa CSV souboru
+generate=Vytvořit
+selectDiag2Insert=Vyberte diagram, který chcete vložit.
+errShowingDiag=Neočekávaná chyba. Diagram nelze zobrazit
+noRecentDiags=Nenalezeny žádné nedávno otevřené diagramy
+fetchingRecentFailed=Nepodařilo se načíst seznam nedávno otevřených diagramů
+useSrch2FindDiags=Pokud chcete hledat draw.io diagramy, použitje k tomu kolonku pro vyhledávání
+cantReadChckPerms=Nedaří se číst zadaný diagram. Zkontrolujte, zda máte oprávnění ke čtení tohoto souboru.
+cantFetchChckPerms=Nedaří se získat informace o diagramu. Zkontrolujte, zda máte oprávnění ke čtení tohoto souboru.
+searchFailed=Hledání se nezdařilo. Prosíme zkuste to znovu později.
+plsTypeStr=Zadejte hledaný řetězec.
+unsupportedFileChckUrl=Nepodporovaný soubor. Zkontrolujte zadanou URL adresu
+diagNotFoundChckUrl=Diagram nenalezen nebo k němu nelze přistupovat. Zkontrolujte zadanou URL adresu
+csvNotFoundChckUrl=CSV soubor nenalezen nebo k němu nelze přistupovat. Zkontrolujte zadanou URL adresu
+cantReadUpload=Nedaří se načíst nahraný diagram
+select=Vybrat
+errCantGetIdType=Neočekávaná chyba: Nedaří se získat identif. nebo typ obsahu
+errGAuthWinBlocked=Chyba: Okno ověřování Google blokováno
+authDrawAccess=Pověřit draw.io přistupovat k {1}
+connTimeout=Překročen časový limit pro spojení
+errAuthSrvc=Chyba při ověřování se vůči {1}
+plsSelectFile=Vyberte soubor
+mustBgtZ=Je třeba, aby {1} bylo vyšší než nula
+cantLoadPrev=Nedaří se načíst náhled souboru.
+errAccessFile=Chyba: Přístup odepřen. Nemáte oprávnění pro přístup k „{1}“.
+noPrevAvail=Není k dispozici žádný náhled.
+personalAccNotSup=Osobní účty nejsou podporovány.
+errSavingTryLater=Při ukládání došlo k chybě, prosíme zkuste to znovu později.
+plsEnterFld=Zadejte {1}
+invalidDiagUrl=Neplatná URL adresa diagramu
+unsupportedVsdx=Nepodporovaný vsdx soubor
+unsupportedImg=Nepodporovaný formát obrázku
+unsupportedFormat=Nepodporovaný formát souboru
+plsSelectSingleFile=Vyberte pouze jeden soubor
+attCorrupt=Soubor přílohy „{1}“ je poškozený
+loadAttFailed=Nepodařilo se načíst přílohu „{1}“
+embedDrawDiag=Vestavět diagram draw.io
+addDiagram=Přidat diagram
+embedDiagram=Vestavěný diagram
+editOwningPg=Upravit stránku, která vlastní
+deepIndexing=Vytváření podrobného rejstříku (také vytvářet rejstříky diagramů, které nejsou použity na žádné stránce
+confADeepIndexStarted=Vytváření podrobného rejstříku zahájeno
+confADeepIndexDone=Vytváření podrobného rejstříku dokončeno
+officeNoDiagramsSelected=Ve výběru nenalezeny žádné diagramy
+officeNoDiagramsInDoc=V dokumentu nenalezeny žádné diagramy
+officeNotSupported=Tato funkce není v této hostitelské aplikaci podporována
+someImagesFailed={1} ze {2} se nezdařilo kvůli následujícím chybám
+importingNoUsedDiagrams=Importují se {1} diagramy nepoužité ve stránkách
+importingDrafts=Importují se {1} diagramy v náčrtech
+processingDrafts=Zpracovávání náčrtů
+updatingDrafts=Aktualizují se náčrty
+updateDrafts=Aktualizovat náčrty
+notifications=Upozornění
+drawioImp=Import draw.io
+confALibsImp=Importují se knihovny draw.io
+confALibsImpFailed=Import knihovny {1} se nezdařil
+contributors=Podíl na vývoji
+drawDiagrams=draw.io diagramy
+errFileNotFoundOrNoPer=Chyba: Přístup odepřen. Soubor nenalezen nebo nemáte oprávnění pro přístup k „{1}“ na {2}.
+confACheckPagesWEmbed=Zjišťování stránek, které mají vestavěné diagramy draw.io.
+confADelBrokenEmbedDiagLnk=Oebírají se nefunční vestavěné odkazy diagramu
+replaceWith=Nahradit čím
+replaceAll=Nahradit vše
+confASkipDiagModified=Přeskočeno „{1}“ protože bylo po počátečním importu upraveno
+replFind=Nahradit/Najít
+matchesRepl={1} shod nahrazeno
+draftErrDataLoss=Došlo k chybě při čtení souboru s náčrtem. Diagram nyní nelze upravovat, aby se zabránílo možným ztrátám dat. Prosíme zkuste to znovu později nebo se obraťte na podporu.
 ibm=IBM
-linkToDiagramHint=Add a link to this diagram. The diagram can only be edited from the page that owns it.
-linkToDiagram=Link to Diagram
-changedBy=Changed By
-lastModifiedOn=Last modified on
-searchResults=Search Results
-showAllTemps=Show all templates
-notionToken=Notion Token
-selectDB=Select Database
+linkToDiagramHint=Přidat odkaz na tento diagram. Diagram je možné upravovat pouze ze stránky, která ho vlastní.
+linkToDiagram=Odkaz na diagram
+changedBy=Změnil(a)
+lastModifiedOn=Naposledy změněno v
+searchResults=Výsledky hledání
+showAllTemps=Zobrazit všechny šablony
+notionToken=Token pojmu
+selectDB=Vybrat databázi
+noDBs=Žádné databáze

+ 1 - 0
src/main/webapp/resources/dia_da.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_de.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_el.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_eo.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_es.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_et.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_eu.txt

@@ -1151,3 +1151,4 @@ searchResults=Bilatu emaitzak
 showAllTemps=Erakutsi txantiloi guztiak
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_fa.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_fi.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_fil.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_fr.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_gl.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_gu.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_he.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_hi.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_hr.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_hu.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_i18n.txt

@@ -1151,3 +1151,4 @@ searchResults=searchResults
 showAllTemps=showAllTemps
 notionToken=notionToken
 selectDB=selectDB
+noDBs=noDBs

+ 1 - 0
src/main/webapp/resources/dia_id.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_it.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ja.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_kn.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ko.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_lt.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_lv.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ml.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_mr.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ms.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_my.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_nl.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_no.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_pl.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_pt-br.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_pt.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ro.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ru.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_si.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_sk.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_sl.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_sr.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_sv.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_sw.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_ta.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_te.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_th.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_tr.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_uk.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_vi.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_zh-tw.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

+ 1 - 0
src/main/webapp/resources/dia_zh.txt

@@ -1151,3 +1151,4 @@ searchResults=Search Results
 showAllTemps=Show all templates
 notionToken=Notion Token
 selectDB=Select Database
+noDBs=No Databases

文件差异内容过多而无法显示
+ 1 - 1
src/main/webapp/service-worker.js


文件差异内容过多而无法显示
+ 1 - 1
src/main/webapp/service-worker.js.map