Browse Source

Sandstorm updates

David Benson 9 years ago
parent
commit
47e26e5d94

+ 6 - 0
etc/sandstorm/ChangeLog

@@ -1,3 +1,9 @@
+09-SEP-2016: 5.6.0.3
+
+- Updates to draw.io 5.6.0.3
+- Fixes Saving... status
+- Adds read-only permission option
+
 28-AUG-2015: 5.0.2.3
 
 - Updates to draw.io 5.0.2.3

+ 1 - 1
etc/sandstorm/Makefile

@@ -6,7 +6,7 @@ SANDSTORM_CAPNP_DIR=/opt/sandstorm/latest/usr/include
 .PHONEY: all clean dev
 
 package.spk: server sandstorm-pkgdef.capnp empty
-	spk pack --keyring="../drawio.key" package.spk
+	spk pack --keyring="drawio.key" package.spk
 
 dev: server sandstorm-pkgdef.capnp empty
 	spk dev

+ 2 - 58
etc/sandstorm/build.sh

@@ -1,62 +1,6 @@
 #! /bin/bash
 #
-# Copyright (c) 2014, JGraph Ltd
-
-# https://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
+# Copyright (c) 2016, JGraph Ltd
 BUILD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-BUILDDIR="build"
-
-echo
-echo "Creating staging"
-
 cd $BUILD
-rm -rf $BUILDDIR
-mkdir $BUILDDIR
-cp -v $BUILD/Makefile $BUILD/$BUILDDIR/
-cp -v $BUILD/sandstorm-pkgdef.capnp $BUILD/$BUILDDIR/
-cp -v $BUILD/server.c++ $BUILD/$BUILDDIR/
-
-cd build
-mkdir client
-cd client
-cp -v ../../ssindex.html .
-cp -v ../../ChangeLog .
-cp -v ../../pgp-keyring .
-cp -v ../../pgp-signature .
-cp -v ../../description.md .
-cp -v ../../shortDesc.txt .
-cp -v ../../../../war/export.html .
-cp -v ../../../../war/favicon.ico .
-cp -v ../../../../war/open.html .
-cp -v ../../../../war/stencils.xml .
-cp -v ../../../../war/search.xml .
-cp -v ../../../../war/shapeeditor.html .
-mkdir -p images
-cp -rf ../../../../war/images/* images/
-cp -v ../../images/drawio448.png images/
-mkdir -p img
-cp -rf ../../../../war/img/* img/
-mkdir -p js
-cp -rf ../../../../war/js/* js/
-mkdir -p mxgraph
-cp -rf ../../../../war/mxgraph/* mxgraph/
-mkdir -p plugins
-cp -rf ../../../../war/plugins/* plugins/
-mkdir -p resources
-cp -rf ../../../../war/resources/* resources/
-mkdir -p shapes
-cp -rf ../../../../war/shapes/* shapes/
-mkdir -p stencils
-cp -rf ../../../../war/stencils/* stencils/
-mkdir -p styles
-cp -rf ../../../../war/styles/* styles/
-mkdir -p templates
-cp -rf ../../../../war/templates/* templates/
-
-echo "Creating file list"
-cd -
-find . -printf "%p\n" | cut -c 3- >  sandstorm-files.list
-
-echo "Creating sandstorm package"
-make
-echo "Distributable file is build/package.spk"
+make server

+ 2 - 2
etc/sandstorm/description.md

@@ -1,5 +1,5 @@
-**The web's most popular diagram editor in your Sandstorm, completely free**
+**The web's most popular open source diagram editor in your Sandstorm**
 
 draw.io is a leading web based diagramming application designed to be simple to use, yet satisfy the power users.
 
-This is the offical Sandstorm port of draw.io based on the [online version of draw.io](https://www.draw.io?splash=0).
+This is the official Sandstorm port of draw.io based on the [online version of draw.io](https://www.draw.io?splash=0).

BIN
etc/sandstorm/pgp-keyring


+ 0 - 0
etc/sandstorm/pgp-signature


+ 28 - 10
etc/sandstorm/sandstorm-pkgdef.capnp

@@ -3,6 +3,8 @@
 # one generated using the `capnp id` command.
 
 using Spk = import "/sandstorm/package.capnp";
+using Grain = import "/sandstorm/grain.capnp";
+
 # This imports:
 #   $SANDSTORM_HOME/latest/usr/include/sandstorm/package.capnp
 # Check out that file to see the full, documented package definition format.
@@ -11,7 +13,7 @@ const pkgdef :Spk.PackageDefinition = (
   # The package definition. Note that the spk tool looks specifically for the
   # "pkgdef" constant.
 
-  id = "nfqhx83vvzm80edpgkpax8mhqp176qj2vwg67rgq5e3kjc5r4cyh",
+  id = "a3w50h1435gsxczugm16q0amwkqm9f4crykzea53sv61pt7phk8h",
   # The app ID is actually the public key used to sign the app package.
   # All packages with the same ID are versions of the same app.
   #
@@ -23,11 +25,11 @@ const pkgdef :Spk.PackageDefinition = (
     # This manifest is included in your app package to tell Sandstorm
     # about your app.
 
-    appVersion = 1,  # Increment this for every release.
+    appVersion = 2,  # Increment this for every release.
     
     appTitle = (defaultText = "draw.io"),
     
-    appMarketingVersion = (defaultText = "5.0.2.3"),
+    appMarketingVersion = (defaultText = "5.6.0.3"),
 
     actions = [
       # Define your "new document" handlers here.
@@ -53,23 +55,26 @@ const pkgdef :Spk.PackageDefinition = (
       ),
 
       website = "https://www.draw.io/",
+      codeUrl = "https://github.com/jgraph/draw.io",
+      license = (openSource = gpl3),
       categories = [office, productivity],
 
       author = (
+        upstreamAuthor = "JGraph",
         contactEmail = "support@draw.io",
-        pgpSignature = embed "client/pgp-signature",
+        pgpSignature = embed "pgp-signature",
       ),
-      pgpKeyring = embed "client/pgp-keyring",
+      pgpKeyring = embed "pgp-keyring",
 
-      description = (defaultText = embed "client/description.md"),
+      description = (defaultText = embed "description.md"),
       
-      shortDescription = (defaultText = embed "client/shortDesc.txt"),
+      shortDescription = (defaultText = embed "shortDesc.txt"),
 
       screenshots = [
         (width = 448, height = 243, png = embed "client/images/drawio448.png")
       ],
 
-      changeLog = (defaultText = embed "client/ChangeLog"),
+      changeLog = (defaultText = embed "ChangeLog"),
     )
   ),
 
@@ -82,8 +87,7 @@ const pkgdef :Spk.PackageDefinition = (
       ( packagePath = "client", sourcePath = "client" ),
       # Map client directory at "/client".
 
-      ( sourcePath = "empty" )
-      # Make sure / is mapped to work around Sandstorm bug (temporary).
+      ( sourcePath = "." )
     ]
   ),
 
@@ -92,6 +96,20 @@ const pkgdef :Spk.PackageDefinition = (
   # "spk dev".
 );
 
+const appIndexViewInfo :Grain.UiView.ViewInfo = (
+  permissions = [(name = "write", title = (defaultText = "write"),
+                  description = (defaultText = "allows editing diagrams")),
+                 (name = "read", title = (defaultText = "read"),
+                  description = (defaultText = "allows viewing diagrams"))],
+  roles = [(title = (defaultText = "editor"),
+            permissions = [true, true],
+            verbPhrase = (defaultText = "can edit"),
+            default = true),
+           (title = (defaultText = "viewer"),
+            permissions = [false, true],
+            verbPhrase = (defaultText = "can view"))]
+);
+
 const myCommand :Spk.Manifest.Command = (
   # Here we define the command used to start up your server.
   argv = ["/server"],

+ 23 - 13
etc/sandstorm/server.c++

@@ -190,6 +190,13 @@ public:
       // Sandstorm in general, and if they attacker already has write access to upload the
       // malicious content, they have little to gain from hijacking another session.)
       return readFile(path, context, "application/octet-stream");
+    } else if (path == ".can-write") {
+      // Fetch "/.can-write" to determine if the user has write permission, so you can show them
+      // a different UI if not.
+      auto response = context.getResults().initContent();
+      response.setMimeType("text/plain");
+      response.getBody().setBytes(kj::str(canWrite).asBytes());
+      return kj::READY_NOW;
     } else if (path == "" || path.endsWith("/")) {
       // A directory. Serve "index.html".
       return readFile(kj::str("client/", path, "ssindex.html"), context, "text/html; charset=UTF-8");
@@ -232,10 +239,9 @@ public:
           .write(data.begin(), data.size());
 
       KJ_SYSCALL(rename(tempPath.cStr(), path.cStr()));
+      context.getResults().initNoContent();
     }
 
-    context.getResults().initNoContent();
-
     return kj::READY_NOW;
   }
 
@@ -334,19 +340,23 @@ public:
   kj::Promise<void> getViewInfo(GetViewInfoContext context) override {
     auto viewInfo = context.initResults();
 
-    // Define a "write" permission. People who don't have this will get read-only access.
-    //
-    // Currently, Sandstorm does not support assigning permissions to individuals. There are only
-    // three distinguishable permission levels:
-    // - The owner has all permissions.
-    // - People who know the grain's secret URL (e.g. because the owner shared it with them) can
-    //   open the grain but have no permissions.
-    // - Everyone else cannot even open the grain.
-    //
-    // Thus, only the grain owner will get our "write" permission, but someday it may be possible
-    // for the owner to assign varying permissions to individual people.
+    // Define a "write" permission, and then define roles "editor" and "viewer" where only "editor"
+	// has the "write" permission. This will allow people to share read-only.
     auto perms = viewInfo.initPermissions(1);
     perms[0].setName("write");
+    auto write = perms[0];
+    write.setName("write");
+    write.initTitle().setDefaultText("write");
+
+    auto roles = viewInfo.initRoles(2);
+    auto editor = roles[0];
+    editor.initTitle().setDefaultText("editor");
+    editor.initVerbPhrase().setDefaultText("can edit");
+    editor.initPermissions(1).set(0, true);   // has "write" permission
+    auto viewer = roles[1];
+    viewer.initTitle().setDefaultText("viewer");
+    viewer.initVerbPhrase().setDefaultText("can view");
+	viewer.initPermissions(1).set(0, false); // does not have "write" permission
 
     return kj::READY_NOW;
   }

+ 209 - 380
etc/sandstorm/ssindex.html

@@ -17,34 +17,89 @@
     <meta name="apple-mobile-web-app-capable" content="yes">
     <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
     <link rel="canonical" href="https://www.draw.io">
+    <script type="text/javascript">
+		/**
+		 * URL Parameters and protocol description are here:
+		 *
+		 * https://support.draw.io/pages/viewpage.action?pageId=12878136
+		 *
+		 * Parameters for developers:
+		 *
+		 * - dev=1: For developers only
+		 * - test=1: For developers only
+		 * - drawdev=1: For developers only
+		 * - export=URL for export: For developers only
+		 * - pages=1: For developers only
+		 * - page=n: For developers only
+		 * - ignoremime=1: For developers only (see DriveClient.js). Use Cmd-S to override mime.
+		 * - createindex=1: For depelopers only (see etc/build/README)
+		 * - filesupport=0: For developers only (see Editor.js in core)
+		 * - savesidebar=1: For developers only (see Sidebar.js)
+		 * - pages=1: For developers only (see Pages.js)
+		 * - lic=email: For developers only (see LicenseServlet.java)
+		 * --
+		 * - networkshapes=1: For testing network shapes (temporary)
+		 */
+		var urlParams = (function()
+		{
+			var result = new Object();
+			var params = window.location.search.slice(1).split('&');
+			
+			for (var i = 0; i < params.length; i++)
+			{
+				idx = params[i].indexOf('=');
+				
+				if (idx > 0)
+				{
+					result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
+				}
+			}
+			
+			return result;
+		})();
+	</script>
+    <link rel="alternate" type="application/rss+xml" title="RSS Feed for draw.io" href="http://blog.draw.io/feed/">
+    <link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/plgmlhohecdddhbmmkncjdmlhcmaachm">
+	<link rel="apple-touch-icon" sizes="57x57" href="images/apple-touch-icon-57x57.png">
+	<link rel="apple-touch-icon" sizes="60x60" href="images/apple-touch-icon-60x60.png">
+	<link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
+	<link rel="apple-touch-icon" sizes="76x76" href="images/apple-touch-icon-76x76.png">
+	<link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
+	<link rel="apple-touch-icon" sizes="120x120" href="images/apple-touch-icon-120x120.png">
+	<link rel="apple-touch-icon" sizes="144x144" href="images/apple-touch-icon-144x144.png">
+	<link rel="apple-touch-icon" sizes="152x152" href="images/apple-touch-icon-152x152.png">
+	<link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon-180x180.png">
+	<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32">
+	<link rel="icon" type="image/png" href="images/favicon-194x194.png" sizes="194x194">
+	<link rel="icon" type="image/png" href="images/favicon-96x96.png" sizes="96x96">
+	<link rel="icon" type="image/png" href="images/android-chrome-192x192.png" sizes="192x192">
+	<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16">
+	<link rel="manifest" href="images/manifest.json">
+	<link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#f18808">
+	<meta name="msapplication-TileColor" content="#da532c">
+	<meta name="msapplication-TileImage" content="images/mstile-144x144.png">
+	<meta name="msapplication-config" content="images/browserconfig.xml">
+	<meta name="theme-color" content="#f18808">
+    <link rel="apple-touch-startup-image" href="images/logo-flat.png">
+    <link rel="stylesheet" type="text/css" href="styles/grapheditor.css">
 	<style type="text/css">
-		body { _overflow: hidden; }
+		body { overflow:hidden; }
 		.geSidebarContainer .geTitle { color:#505050; }
 		.geSidebarContainer .geTitle input {
 			font-size:8pt;
 			color:#606060;
 		}
 		.geBlock {
-			z-index: -3;
+			z-index:-3;
 			margin:100px;
 			margin-top:40px;
 			margin-bottom:30px;
 			padding:20px;
-			border-radius:6px;
-			-webkit-box-shadow:0px 0px 4px 4px #e5e5e5;
-			-moz-box-shadow:0px 0px 4px 4px #e5e5e5;
-			box-shadow:0px 0px 4px 4px #e5e5e5;
-			_filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, Color='#e5e5e5', Positive='true');
-			border:1px solid gray;
-			background-color:white;
 		}
 		.geBlock h1, .geBlock h2 {
 			margin-top:0px;
 			padding-top:0px;
 		}
-		.geEditor {
-			background-color:whiteSmoke;
-		}
 		.geEditor ::-webkit-scrollbar {
 		    width:12px;
 		    height:12px;
@@ -72,8 +127,44 @@
 			cursor:pointer;
 			margin:5px;
 		}
-		.geFooterContainer {
-			z-index:100;
+		.geFooterContainer div.geSocialFooter a {
+			display:inline;
+			padding:0px;
+		}
+		.geFooterContainer div.geSocialFooter a img {
+			margin-top:10px;
+			opacity:0.8;
+		}
+		.geFooterContainer div.geSocialFooter a img:hover {
+			opacity:1;
+		}
+		#geFooterItem1 {
+			background-color: #cdcdcd;
+		}
+		#geFooterItem1:hover {
+			background-color: #b0b0b0;
+		}		
+		.geFooterContainer>div>img {
+			opacity:0.5;
+			background:#e5e5e5;
+			border:1px solid transparent;
+			cusor:pointer;
+			margin-top:3px;
+			margin-right:6px;
+			position:absolute;
+			right:4px;
+			top:12px;
+			padding:1px;
+			cursor:pointer;
+		}
+		.geFooterContainer>div>img:hover {
+			opacity: 1;
+		}
+		.geBlink {
+		  animation: geBlinker 1s linear infinite;
+		}
+		@keyframes geBlinker {  
+		  50% { opacity: 0.0; }
 		}
 	</style>
 	<!-- Workaround for binary XHR in IE 9/10, see App.loadUrl -->
@@ -90,131 +181,45 @@
 		</script>
 	<!--<![endif]-->
 	<script type="text/javascript">
-	/**
-	 * Parses URL parameters. Supported parameters are (precedence in given order):
-	 * 
-	 * - lang=xy: Specifies the language of the user interface
-	 * - touch=1: Enables a touch-style user interface
-	 * - libs=key1;key2;...;keyN: Specifies the libraries
-	 * - clibs=key1;key2;key;...;keyN: Specifies custom libraries
-	 * - picker=0: Disables Google picker
-	 * - picker=1: Enables Google picker
-	 * - storage=device: Adds device storage option for touch devices
-	 * - mode=google/dropobox/device/browser: Switch to given mode
-	 * - chrome=0: Chromeless read-only viewer with no analytics
-	 * - nav=1: Enables folding in chromeless mode
-	 * - https=0: Disables/enables SSL
-	 * - save=local/remote: Uses/disables saving files locally (local is default)
-	 * - stealth=1: Runs in offline mode but without storing the app in local cache
-	 * - math=0: Disables MathJax support
-	 * - drive=0: Simulates www.draw.io (using old app ID)
-	 * - url=url: Opens diagram from URL (URL should be encoded)
-	 * - analytics=0: Disables Google Analytics
-	 * - plugins=0: Do not load Plugins
-	 * - p=id1;id2;...;idN: Select plugins to load
-	 * - gapi=0: Do not load Google APIs
-	 * - db=0: Do not load Dropbox APIs
-	 * - od=0: Do not load OneDrive APIs
-	 * - local=1: Uses device mode only.
-	 * - format=0: Disables the format panel.
-	 * - photos=1: Adds the photos scopes in Drive.
-	 * - thumb=0: Disables creation of thumbnails in Drive.
-	 * - client=1: Runs in client mode. This will run a normal UI and send a "ready"
-	 *   message to the opener or parent when the page is loaded. After receiving
-	 *   a message with XML or compressed XML, a local file is created. The file is then
-	 *   set to modified so the connection to the window can be closed after sending the
-	 *   initial XML. The XML will not be loaded again after a refresh. The window will
-	 *   stay empty in this case.
-	 * - embed=1: Runs in embedded mode. This will send a "ready" message to the
-	 *   opener or parent when the page is loaded. After receiving the ready message
-	 *   the data can be sent as XML or compressed XML. It will send back XML or an empty
-	 *   string if apply or cancel are pressed, respectively.
-	 * - spin=1: Shows a spinner while waiting for the diagram data in embed mode.
-	 * - modified=0: Disables update of the modified state in embed mode after save is pressed.
-	 *   And enables update of the status message after changes. If 0 is used, the status bar
-	 *	 is cleared after changes. Else the value of this is used as the resource key.
-	 * - saveAndExit: Displays an additional save and exit button (see below).
-	 * - proto=json: Uses JSON for message passing in embed and client mode. The protocol is
-	 *   as follows: When the client is ready, it sends {event: 'init'} and expects {action: 'load',
-	 *	 xml: '...'}. An optional autosave: 1 can be specified in this message to enabled autosave.
-	 *   If autosave is enabled the editor will send the current XML in an {event: 'autosave'...}.
-	 *   The XML in the load message is displayed and an {event: 'load'...} is returned with some
-	 *	 data about the size of the graph. Optionally, instead of xml, xmlpng can be used in the
-	 *	 load message with a base64 encoded PNG with embedded XML. (The XML in this message can
-	 *	 be any supported XML represenation of the graph including SVG with embedded XML.)
-	 * 	 If save is clicked, the same message as load is sent but with an additional event: 'save'
-	 *	 and xml: '...' containing the XML of the diagram. If save and exit is clicked the message
-	 *	 has an additional exit: true.
-	 *	 If exit is clicked, an {event: 'exit', modified: boolean} is sent.
-	 *	 {action: 'dialog', title: '...', message: '...', button: '...', modified: bool} can be sent
-	 *	 at any time to display a dialog in the editor window. To translate the dialog, titleKey,
-	 *	 messageKey and buttonKey can be used instead.
-	 *	 {action: 'status', message: '...', modified: bool} can be sent at any time to display a message
-	 *	 in the status bar. The optional modified flag is used to update the modified state. Instead of
-	 *	 message, messageKey can be used to specify a resource key for the message.
-	 *	 {action: 'export', format: '...'} can be sent at any time to return {event: 'export',
-	 *	 format: '...', data: '...', xml: '...'} with the data and XML used for the given format.
-	 *	 Supported formats are html, svg (default), xmlsvg (SVG with embeddded XML), png and xmlpng
-	 *	 (PNG with embedded XML). For png and xmlpng, an additional spin (or spinKey) parameter
-	 *	 can be used to enable a spinner and specify a message (or message key) while the image
-	 *	 is being generated. An optional xml parameter can be used to specify the XML of the
-	 *	 diagram to be exported for all supported formats. An optional embedImages: false can be
-	 *   specified for the svg and xmlsvg formats to disable embedded images in the SVG output.
-	 * - returnbounds=1: Returns a JSON structure with the graph bounds in embed and client
-	 *   mode. This message is dispatched immediately after receiving the graph XML.
-	 * - ui=atlas: Uses the atlas theme for the UI
-	 * - ready=message: The message to send in embed or client mode. Default is 'ready'. If
-	 *   JSON protocol is used then this is ignored.
-	 * - create=url/name: Creates new file from template URL. If the value is not an
-	 *   URL and is not empty the script will try to use window.opener[url]. In embed
-	 *   mode, window.opener[name] will be used to get the initial XML. Note that this
-	 *   needs same origin policy in the opener/parent for reading the variable.
-	 * - title=title: New file title (used with create)
-	 * - offline=1: Shortcut for db=0&gapi=0&math=0&picker=0&analytics=0
-	 *   and disables all remote operations and features, such as i18n (english only),
-	 *   remote images, google/dropbox integration, plugins and bitmap and PDF export.
-	 * - demo=1: Shortcut for db=0&gapi=0&math=0&picker=0&analytics=0, disables the
-	 *   splash screen and creates an empty, local file.
-	 * - splash=0: Bypasses the splash screen and starts with an empty file.
-	 * - rev=id: Selects a specific revision of a Google Drive file.
-	 * - pv=0: Sets default pageVisible to false.
-	 * - sb=0: Starts with scrollbars disabled.
-	 * --
-	 * - dev=1: For developers only
-	 * - test=1: For developers only
-	 * - drawdev=1: For developers only
-	 * - export=URL for export: For developers only
-	 * - ignoremime=1: For developers only (see DriveClient.js). Use Cmd-S to override mime.
-	 * - createindex=1: For depelopers only (see etc/build/README)
-	 * - filesupport=0: For developers only (see Editor.js in core)
-	 * - savesidebar=1: For developers only (see Sidebar.js)
-	 * --
-	 * - networkshapes=1: For testing network shapes (temporary)
-	 */
-		var urlParams = (function()
+		/**
+		 * Synchronously adds scripts to the page.
+		 */
+		function mxscript(src, onLoad, id, dataAppKey)
 		{
-			var result = new Object();
-			var params = window.location.search.slice(1).split('&');
-			
-			for (var i = 0; i < params.length; i++)
+			if (onLoad != null)
 			{
-				idx = params[i].indexOf('=');
+				var s = document.createElement('script');
+				s.setAttribute('type', 'text/javascript');
+				s.setAttribute('src', src);
+				var r = false;
 				
-				if (idx > 0)
+				if (id != null)
 				{
-					result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
+					s.setAttribute('id', id);
+				}
+				
+				if (dataAppKey != null)
+				{
+					s.setAttribute('data-app-key', dataAppKey);
 				}
+				
+				s.onload = s.onreadystatechange = function()
+				{
+					if (!r && (!this.readyState || this.readyState == 'complete'))
+					{
+			      		r = true;
+			      		onLoad();
+					}
+			  	};
+			  	
+			  	var t = document.getElementsByTagName('script')[0];
+			  	t.parentNode.insertBefore(s, t);
+			}
+			else
+			{
+				document.write('<script src="' + src + '"' + ((id != null) ? ' id="' + id +'" ' : '') +
+					((dataAppKey != null) ? ' data-app-key="' + dataAppKey +'" ' : '') + '></scr' + 'ipt>');
 			}
-			
-			return result;
-		})();
-
-		/**
-		 * Synchronously adds scripts to the page.
-		 */
-		function mxscript(src)
-		{
-			document.write('<script src="'+src+'"></scr' + 'ipt>');
 		};
 
 		/**
@@ -227,7 +232,6 @@
 		};
 
 		// Checks for local storage and SVG support
-		var isSvgBrowser = true;
 		var isLocalStorage = false; // No local storage in sandstorm because of subdomains - typeof(localStorage) != 'undefined';
 
 		var t0 = new Date();
@@ -250,128 +254,8 @@
 		var IMAGE_PATH = 'images';
 		// Path for images inside the diagram
 		var GRAPH_IMAGE_PATH = 'img';
-		var ICONFINDER_PATH = (navigator.userAgent.indexOf('MSIE') >= 0) ? 'iconfinder' : 'https://www.draw.io/iconfinder';
-		var STYLE_PATH = 'styles';
-		var CSS_PATH = 'styles';
-		var OPEN_FORM = 'open.html';
-		var TEMPLATE_PATH = '/templates';
-		
-		// Directory for i18 files and basename for main i18n file
-		var RESOURCES_PATH = 'resources';
-		var RESOURCE_BASE = RESOURCES_PATH + '/dia';
-
-		/**
-		 * Global function for loading local files via servlet
-		 */
-		function setCurrentXml(data, filename)
-		{
-			if (window.parent != null && window.parent.openFile != null)
-			{
-				window.parent.openFile.setData(data, filename);
-			}
-		};
-
-		/**
-		 * Returns the global language
-		 */
-		function getLanguage() 
-		{
-			var lang = (urlParams['offline'] == '1') ? 'en' : urlParams['lang'];
-			
-			// Known issue: No JSON object at this point in quirks in IE8
-			if (lang == null && typeof(JSON) != 'undefined')
-			{
-				// Cannot use mxSettings here
-				if (isLocalStorage) 
-				{
-					try
-					{
-						var value = localStorage.getItem('.drawio-config');
-						
-						if (value != null)
-						{
-							lang = JSON.parse(value).language || null;
-						}
-					}
-					catch (e)
-					{
-						// cookies are disabled, attempts to use local storage will cause
-						// a DOM error at a minimum on Chrome
-						isLocalStorage = false;
-					}
-				}
-			}
-			
-			return lang;
-		};
-		
-		// Sets the base path, the UI language via URL param and configures the
-		// supported languages to avoid 404s. The loading of all core language
-		// resources is disabled as all required resources are in grapheditor.
-		// properties. Note that in this example the loading of two resource
-		// files (the special bundle and the default bundle) is disabled to
-		// save a GET request. This requires that all resources be present in
-		// the special bundle.
-		var mxLoadResources = false;
-		var mxLanguage = getLanguage();
-		
-		// Add new languages here. First entry is translated to [Automatic]
-		// in the menu defintion in Diagramly.js.
-		var mxLanguageMap = {'i18n': '', 'id' : 'Bahasa Indonesia', 'ms' : 'Bahasa Melayu', 'bs' : 'Bosanski', 'ca' : 'Català', 'cs' : 'Čeština', 'da' : 'Dansk', 'de' : 'Deutsch', 'et' : 'Eesti', 'en' : 'English', 'es' : 'Español', 
-				'fil' : 'Filipino', 'fr' : 'Français', 'it' : 'Italiano', 'hu' : 'Magyar', 'nl' : 'Nederlands', 'no' : 'Norsk', 
-				'pl' : 'Polski', 'pt-br' : 'Português (Brasil)', 'pt' : 'Português (Portugal)', 'ro' : 'Română', 'fi' : 'Suomi', 'sv' : 'Svenska', 'vi' : 'Tiếng Việt', 'tr' : 'Türkçe',
-				'el' : 'Ελληνικά', 'ru' : 'Русский', 'sr' : 'Српски', 'uk' : 'Українська', 'he' : 'עברית',
-				'ar' : 'العربية', 'th' : 'ไทย', 'ko' : '한국어', 'ja' : '日本語', 'zh' : '中文(中国)',  'zh-tw' : '中文(台灣)'};
-
-		var geBasePath = 'js';
-		var mxBasePath = 'mxgraph';
-		var mxLanguages = [];
-		
-		// Populates the list of supported special language bundles
-		for (var lang in mxLanguageMap)
-		{
-			// Empty means default (ie. browser language), "en" means English (default for unsupported languages)
-			// Since "en" uses no extension this must not be added to the array of supported language bundles.
-			if (lang != 'en')
-			{
-				mxLanguages.push(lang);
-			}
-		}
+		ICONFINDER_PATH = (navigator.userAgent.indexOf('MSIE') >= 0) ? 'iconfinder' : 'https://www.draw.io/iconfinder';
 
-		/**
-		 * Returns the global UI setting before runngin static draw.io code
-		 */
-		var uiTheme = (function() 
-		{
-			var ui = urlParams['ui'];
-			
-			// Known issue: No JSON object at this point in quirks in IE8
-			if (ui == null && typeof(JSON) != 'undefined')
-			{
-				// Cannot use mxSettings here
-				if (isLocalStorage) 
-				{
-					try
-					{
-						var value = localStorage.getItem('.drawio-config');
-						
-						if (value != null)
-						{
-							ui = JSON.parse(value).ui || null;
-						}
-					}
-					catch (e)
-					{
-						// cookies are disabled, attempts to use local storage will cause
-						// a DOM error at a minimum on Chrome
-						isLocalStorage = false;
-					}
-				}
-			}
-			
-			return ui;
-		})();
-		
 		// Used to request grapheditor/mxgraph sources in dev mode
 		var mxDevUrl = document.location.protocol + '//devhost.jgraph.com/mxgraph2';
 
@@ -401,6 +285,8 @@
 		// Changes paths for local development environment
 		if (urlParams['dev'] == '1')
 		{
+			// Used to request grapheditor/mxgraph sources in dev mode
+			var mxDevUrl = document.location.protocol + '//devhost.jgraph.com/mxgraph2';
 			geBasePath = mxDevUrl + '/javascript/examples/grapheditor/www/js';
 			mxBasePath = mxDevUrl + '/javascript/src';
 			mxscript(mxBasePath + '/js/mxClient.js');
@@ -443,13 +329,6 @@
 </head>
 <body class="geEditor">
 <div id="geInfo">
-	<div class="geBlock" style="text-align:center;">
-		<h1>Flowchart Maker and Online Diagram Software</h1>
-		<p>
-			draw.io (formerly Diagramly) is free online diagram software. You can use it as a flowchart maker, network diagram software, to create UML online, as an ER diagram tool, 
-			to design database schema, to build BPMN online, as a circuit diagram maker, and more.
-		</p>
-	</div>
 	<div class="geBlock" style="text-align:center;">
 		<h3 id="geStatus">Loading... Please ensure JavaScript is enabled</h2>
 	</div>
@@ -466,8 +345,22 @@
 		{
 			if (req.getStatus() == 200)
 			{
-				this.spinner.stop();
-				this.fileLoaded(new SandstormFile(this, req.getText(), null));
+				mxUtils.get('.can-write', mxUtils.bind(this, function(req2)
+				{
+					var ssFile = new SandstormFile(this, req.getText(), null);
+
+					if (req2.getText() == "false")
+					{
+						ssFile.editable = false;
+					}
+					
+					this.spinner.stop();
+					this.fileLoaded(ssFile);
+					
+				}), mxUtils.bind(this, function()
+				{
+					// No action, only failed to read the write status
+				}));
 			}
 			else if (req.getStatus() == 404)
 			{
@@ -491,7 +384,7 @@
 
 	App.prototype.createFile = function(title, data, libs, mode, done)
 	{
-		data = (data != null) ? data : '';
+		data = (data != null) ? data : this.emptyDiagramXml;
 		
 		var error = mxUtils.bind(this, function(resp)
 		{
@@ -519,6 +412,7 @@
 		DrawioFile.call(this, ui, data);
 		
 		this.title = title;
+		this.editable = true;
 	};
 
 	mxUtils.extend(SandstormFile, DrawioFile);
@@ -526,7 +420,8 @@
 	SandstormFile.prototype.isAutosave = function()	{ return true; };
 	SandstormFile.prototype.getMode = function() { return App.MODE_DEVICE; };
 	SandstormFile.prototype.getTitle = function() { return this.title; };
-	SandstormFile.prototype.isRenamable = function() { return true; };
+	SandstormFile.prototype.isRenamable = function() { return this.editable; };
+	SandstormFile.prototype.isEditable = function() { return this.editable; };
 
 	/**
 	 *
@@ -557,10 +452,10 @@
 
 		request.send(mxUtils.bind(this, function(req)
 		{
-			
+			this.ui.editor.setStatus(mxResources.get('allChangesSaved'));
 		}), mxUtils.bind(this, function()
 		{
-			
+			this.ui.editor.setStatus(mxResources.get('unsavedChanges'));
 		}));
 		
 		this.contentChanged();
@@ -578,126 +473,60 @@
 
 	//////////////// End custom menu actions //////////////////
 
-	/**
-	 * Color dialog - Do not add to app.min.js due to path issues!
-	 */
-	if (urlParams['chrome'] != '0')
-	{
-		mxscript('js/jscolor/jscolor.js');
-	}
-
-	if (uiTheme == 'atlas')
-	{
-		mxClient.link('stylesheet', 'styles/atlas.css');
-	}
-
-	/**
-	 * Asynchronous MathJax extension.
-	 */
-/* 	if (urlParams['math'] != '0')
-	{
-		var mathJaxQueue = [];
-		
-		// Disables global typesetting and messages on startup, adds queue for
-		// asynchronous rendering while MathJax is loading
-		window.MathJax =
-		{
-			skipStartupTypeset: true,
-			showMathMenu: false,
-			messageStyle: 'none',
-			AuthorInit: function ()
-			{
-				MathJax.Hub.Register.StartupHook('Begin', function()
-				{
-					for (var i = 0; i < mathJaxQueue.length; i++)
-					{
-						MathJax.Hub.Queue(['Typeset', MathJax.Hub, mathJaxQueue[i]]);
-					}
-				});
-		    }
-		};
-
-		// Adds global enqueue method for async rendering
-		window.MathJaxRender = function(container)
-		{
-			// Initial rendering when MathJax finished loading
-			if (typeof(MathJax) !== 'undefined' && typeof(MathJax.Hub) !== 'undefined')
-			{
-				MathJax.Hub.Queue(['Typeset', MathJax.Hub, container]);
-			}
-			else
-			{
-				mathJaxQueue.push(container);
-			}
-		}
-		
-		mxscript('js/mathjax/MathJax.js?config=TeX-MML-AM_HTMLorMML');
-	} */
-	
-	/**
-	 * For developers only
-	 */
-	if (urlParams['chrome'] != '0' && (urlParams['test'] == '1' || urlParams['dev'] == '1'))
-	{
-		mxLog.show();
-		mxLog.debug('Started in ' + (new Date().getTime() - t0.getTime()) + 'ms');
-		mxLog.debug('Export:', EXPORT_URL);
-		mxLog.debug('Development mode:', (urlParams['dev'] == '1') ? 'active' : 'inactive');
-		mxLog.debug('Test mode:', (urlParams['test'] == '1') ? 'active' : 'inactive');
-	}
-
 	/**
 	 * Main
 	 */
-	var ui = new App(new Editor());
-	App.prototype.enableLogging = false;
-	
-	ui.actions.get('save').visible = false;
-	ui.actions.get('exit').visible = false;
-	ui.actions.get('feedback').visible = false;
+ 	App.prototype.enableLogging = false;
 	
-	// Changes support link in Help menu
-	ui.actions.addAction('support...', function()
+	App.main(function(ui)
 	{
-		window.open('https://support.draw.io/display/DFS/draw.io+for+Sandstorm+Home');
+	 	ui.actions.get('save').visible = false;
+	 	ui.actions.get('exit').visible = false;
+	 	ui.actions.get('feedback').visible = false;
+		
+	 	// Changes support link in Help menu
+	 	ui.actions.addAction('support...', function()
+	 	{
+	 		window.open('https://support.draw.io/display/DFS/draw.io+for+Sandstorm+Home');
+	 	});
 	});
 
-	EditorUi.prototype.isExportToCanvas = function()
-	{
-		// LATER: Fix security error caused by foreignObjects in Safari for toDataUri (tainted canvas)
-		return false;
-	};
-
-	// Loads and executes the plugins
-	if (urlParams['offline'] != '1')
-	{
-		var plugins = mxSettings.getPlugins();
-
-		if (plugins != null && plugins.length > 0 && urlParams['plugins'] != '0')
-		{
-			// Global entry point for plugins is Draw.loadPlugin. This is the only
-			// long-term supported solution for access to the EditorUi instance.
-			window.Draw = new Object();
-			window.Draw.loadPlugin = function(callback)
-			{
-				callback(ui);
-			};
+// 	EditorUi.prototype.isExportToCanvas = function()
+// 	{
+// 		// LATER: Fix security error caused by foreignObjects in Safari for toDataUri (tainted canvas)
+// 		return false;
+// 	};
+
+// 	// Loads and executes the plugins
+// 	if (urlParams['offline'] != '1')
+// 	{
+// 		var plugins = mxSettings.getPlugins();
+
+// 		if (plugins != null && plugins.length > 0 && urlParams['plugins'] != '0')
+// 		{
+// 			// Global entry point for plugins is Draw.loadPlugin. This is the only
+// 			// long-term supported solution for access to the EditorUi instance.
+// 			window.Draw = new Object();
+// 			window.Draw.loadPlugin = function(callback)
+// 			{
+// 				callback(ui);
+// 			};
 			
-			if (plugins.length == 1 && (plugins[0].charAt(0) == '/' ||
-				plugins[0].indexOf(window.location.protocol + '//' + window.location.host) == 0))
-			{
-				mxscript(plugins[0]);
-			}
-			// Loads plugins asynchronously
-			else if (mxUtils.confirm(mxResources.get('pluginWarning', [plugins.join('\n')]).replace(/\\n/g, '\n')))
-			{
-				for (var i = 0; i < plugins.length; i++)
-				{
-					mxscript(plugins[i]);
-				}
-			}
-		}
-	}
+// 			if (plugins.length == 1 && (plugins[0].charAt(0) == '/' ||
+// 				plugins[0].indexOf(window.location.protocol + '//' + window.location.host) == 0))
+// 			{
+// 				mxscript(plugins[0]);
+// 			}
+// 			// Loads plugins asynchronously
+// 			else if (mxUtils.confirm(mxResources.get('pluginWarning', [plugins.join('\n')]).replace(/\\n/g, '\n')))
+// 			{
+// 				for (var i = 0; i < plugins.length; i++)
+// 				{
+// 					mxscript(plugins[i]);
+// 				}
+// 			}
+// 		}
+// 	}
 })();
 </script>
 </body>

+ 44 - 0
etc/sandstorm/stage.sh

@@ -0,0 +1,44 @@
+#! /bin/bash
+#
+# Copyright (c) 2016, JGraph Ltd
+
+# https://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
+mkdir -p build/.sandstorm/client
+cp -v ssindex.html build/.sandstorm/client
+cp -v sandstorm-pkgdef.capnp build/.sandstorm
+cp -v ChangeLog build/.sandstorm
+cp -v pgp-keyring build/.sandstorm
+cp -v pgp-signature build/.sandstorm
+cp -v description.md build/.sandstorm
+cp -v shortDesc.txt build/.sandstorm
+cp -v Makefile build/.sandstorm
+cp -v server.c++ build/.sandstorm
+cp -v ../../war/export.html build/.sandstorm/client
+cp -v ../../war/favicon.ico build/.sandstorm/client
+cp -v ../../war/open.html build/.sandstorm/client
+cp -v ../../war/stencils.xml build/.sandstorm/client
+cp -v ../../war/search.xml build/.sandstorm/client
+mkdir -p  build/.sandstorm/client/images
+cp -rf ../../war/images/* build/.sandstorm/client/images/
+cp -v images/drawio448.png build/.sandstorm/client/images/
+mkdir -p build/.sandstorm/client/img
+cp -rf ../../war/img/* build/.sandstorm/client/img/
+mkdir -p build/.sandstorm/client/js
+cp -rf ../../war/js/* build/.sandstorm/client/js/
+mkdir -p build/.sandstorm/client/mxgraph
+cp -rf ../../war/mxgraph/* build/.sandstorm/client/mxgraph/
+mkdir -p build/.sandstorm/client/plugins
+cp -rf ../../war/plugins/* build/.sandstorm/client/plugins/
+mkdir -p build/.sandstorm/client/resources
+cp -rf ../../war/resources/* build/.sandstorm/client/resources/
+mkdir -p build/.sandstorm/client/shapes
+cp -rf ../../war/shapes/* build/.sandstorm/client/shapes/
+mkdir -p build/.sandstorm/client/stencils
+cp -rf ../../war/stencils/* build/.sandstorm/client/stencils/
+mkdir -p build/.sandstorm/client/styles
+cp -rf ../../war/styles/* build/.sandstorm/client/styles/
+mkdir -p build/.sandstorm/client/templates
+cp -rf ../../war/templates/* build/.sandstorm/client/templates/
+echo "Creating file list"
+cd build/.sandstorm
+gfind . -type f -printf "%p\n" | cut -c 3- >  sandstorm-files.list