Bug 372914 - implement an "orion.page.content" service extension
diff --git a/bundles/org.eclipse.orion.client.core/web/content/content.css b/bundles/org.eclipse.orion.client.core/web/content/content.css
new file mode 100644
index 0000000..4c0c9d2
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.core/web/content/content.css
@@ -0,0 +1,15 @@
+@import "../org.dojotoolkit/dojo/resources/dojo.css";
+
+@import "../org.dojotoolkit/dijit/themes/nihilo/nihilo.css";
+
+@import "../org.dojotoolkit/dijit/themes/nihilo/layout/BorderContainer.css";
+
+@import "../org.dojotoolkit/dijit/themes/nihilo/form/Common.css";
+
+@import "../org.dojotoolkit/dijit/themes/nihilo/form/Button.css";
+
+@import "../css/ide.css";
+
+@import "../css/breadcrumbs.css";
+
+@import "../css/commands.css";
\ No newline at end of file
diff --git a/bundles/org.eclipse.orion.client.core/web/content/content.html b/bundles/org.eclipse.orion.client.core/web/content/content.html
new file mode 100644
index 0000000..d2fa375
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.core/web/content/content.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+	<head>
+		<meta charset="UTF-8">
+		<title>Plugin Content</title>
+		<link rel="stylesheet" type="text/css" href="content.css" />
+    	<script type="text/javascript" src="../requirejs/require.js"></script>
+		<script type="text/javascript">
+		require({
+			  baseUrl: '..',
+			  packages: [
+			    {
+			      name: 'dojo',
+			      location: 'org.dojotoolkit/dojo',
+			      main: 'lib/main-browser',
+			      lib: '.'
+			    },
+			    {
+			      name: 'dijit',
+			      location: 'org.dojotoolkit/dijit',
+			      main: 'lib/main',
+			      lib: '.'
+			    },
+			    {
+			      name: 'dojox',
+			      location: 'org.dojotoolkit/dojox',
+			      main: 'lib/main',
+			      lib: '.'
+			    }		    
+			  ],
+			  paths: {
+				  text: 'requirejs/text',
+				  i18n: 'requirejs/i18n'	    
+			  }
+			});
+		
+		require(["./content"]);
+		</script>
+	</head>
+   <body class="nihilo" style="width: 100%; height: 100%; visibility:hidden;">
+	<div id="orion.delegatedContent" class="orionPage" dojoType="dijit.layout.BorderContainer" design="headline" gutters="false">
+		<div class="banner" id="banner" dojoType="dijit.layout.ContentPane" region="top">
+		</div>
+		<div id="centerPane" dojoType="dijit.layout.BorderContainer" gutters="false" region="center" design="headline" liveSplitters="false" splitter="false">
+			<div class="toolbar" id="pageToolbar" dojoType="dijit.layout.ContentPane" splitter="false" region="top">
+			</div>
+			<div id="delegatedContent" dojoType="dijit.layout.ContentPane" splitter="false"region="center">
+				<div>Loading content from plugin...</div>
+			</div>
+		</div>
+		<div class="footer" id="footer" dojoType="dijit.layout.ContentPane" region="bottom" splitter="false">
+		</div>
+	</div>
+	
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.orion.client.core/web/content/content.js b/bundles/org.eclipse.orion.client.core/web/content/content.js
new file mode 100644
index 0000000..2c39264
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.core/web/content/content.js
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define dojo dijit orion window widgets*/
+/*jslint browser:true*/
+
+/*
+ * Glue code for content.html
+ */
+
+define(['require', 'dojo', 'orion/bootstrap', 'orion/status', 'orion/progress', 'orion/commands', 'orion/fileClient', 'orion/operationsClient',
+	        'orion/searchClient', 'orion/dialogs', 'orion/globalCommands', 'orion/breadcrumbs', 'orion/URITemplate', 'orion/PageUtil', 
+	        'dojo/parser', 'dojo/hash', 'dojo/date/locale', 'dijit/layout/BorderContainer', 'dijit/layout/ContentPane', 'orion/widgets/NewSiteDialog'], 
+			function(require, dojo, mBootstrap, mStatus, mProgress, mCommands, mFileClient, mOperationsClient, mSearchClient, mDialogs, 
+			mGlobalCommands, mBreadcrumbs, URITemplate, PageUtil) {
+
+	dojo.addOnLoad(function() {
+		mBootstrap.startup().then(function(core) {
+			var serviceRegistry = core.serviceRegistry;
+			var preferences = core.preferences;
+			// Register services
+			var dialogService = new mDialogs.DialogService(serviceRegistry);
+			var operationsClient = new mOperationsClient.OperationsClient(serviceRegistry);
+			var statusService = new mStatus.StatusReportingService(serviceRegistry, operationsClient, "statusPane", "notifications", "notificationArea");
+			var progressService = new mProgress.ProgressService(serviceRegistry, operationsClient);
+			var commandService = new mCommands.CommandService({serviceRegistry: serviceRegistry});
+			var fileClient = new mFileClient.FileClient(serviceRegistry);
+			var searcher = new mSearchClient.Searcher({serviceRegistry: serviceRegistry, commandService: commandService, fileService: fileClient});
+			
+			function loadContent() {
+				var foundContent = false;
+				var params = PageUtil.matchResourceParameters(window.location.href);
+				var nonHash = window.location.href.split('#')[0];
+				// TODO: should not be necessary, see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=373450
+				var hostName = nonHash.substring(0, nonHash.length - window.location.pathname.length);
+				var locationObject = {OrionHome: hostName, Location: params.resource};
+				if (params.contentProvider) {
+					// Note that the shape of the "orion.page.content" extension is not in any shape or form that could be considered final.
+					// We've included it to enable experimentation. Please provide feedback on IRC or bugzilla.
+			
+					// The shape of the extension is:
+					// info - information about the extension (object)
+					//		required attribute: name - the name to be used in the page title and orion page heading
+					//		required attribute: id - the id of the content contribution
+					//		required attribute: uriTemplate - a uriTemplate that expands to the URL of the content to be placed in a content iframe
+					//		optional attribute: saveToken - if specified, this token (or array of tokens) should be used to find a content URL provided inside a save URL
+					//		optional attribute: saveTokenTerminator - if specified this terminator (or array of terminators) should be used to find the 
+					//			end of a content URL provided in a save URL
+					var contentProviders = serviceRegistry.getServiceReferences("orion.page.content");
+					for (var i=0; i<contentProviders.length; i++) {
+						// Exclude any navigation commands themselves, since we are the navigator.
+						var id = contentProviders[i].getProperty("id");
+						if (id === params.contentProvider) {
+							var impl = serviceRegistry.getService(contentProviders[i]);
+							var info = {};
+							var propertyNames = contentProviders[i].getPropertyNames();
+							for (var j = 0; j < propertyNames.length; j++) {
+								info[propertyNames[j]] = contentProviders[i].getProperty(propertyNames[j]);
+							}
+							foundContent = true;
+							if (info.saveToken) {
+								// we need to set up a SaveURL for the iframe to use.
+								locationObject.SaveURL = hostName+"/content/saveHook.html#" + params.resource + ",contentProvider=" + params.contentProvider + ",";
+							}
+							var uriTemplate = new URITemplate(info.uriTemplate);
+							var href = uriTemplate.expand(locationObject);
+							dojo.place('<iframe id="' + id + '" type="text/html" width="100%" height="100%" frameborder="0" src="'+ href + '"></iframe>', "delegatedContent", "only");
+							// this is ripe for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349531
+							document.title = info.name;
+							fileClient.read(locationObject.Location, true).then(function(metadata) {
+								dojo.empty("location");
+								if (metadata) {
+									mGlobalCommands.setPageTarget(metadata, serviceRegistry, commandService);
+									searcher.setLocationByMetaData(metadata, {index: "first"});
+									var root = fileClient.fileServiceName(metadata.Location);
+									new mBreadcrumbs.BreadCrumbs({
+										container: "location", 
+										resource: metadata,
+										firstSegmentName: root
+									});
+								}
+							});
+							break;
+						}
+					}
+				}
+				if (!foundContent) {
+					dojo.place("<div>Plugin content could not be found</div>", "delegatedContent", "only");
+				}
+			}
+			
+			dojo.subscribe("/dojo/hashchange", this, function() {
+				loadContent();
+			});
+			loadContent();
+			mGlobalCommands.generateBanner("banner", serviceRegistry, commandService, preferences, searcher);
+			document.body.style.visibility = "visible";
+			dojo.parser.parse();
+
+
+		});
+	});
+});
\ No newline at end of file
diff --git a/bundles/org.eclipse.orion.client.core/web/content/saveHook.html b/bundles/org.eclipse.orion.client.core/web/content/saveHook.html
new file mode 100644
index 0000000..7433d33
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.core/web/content/saveHook.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+	<head>
+		<meta charset="UTF-8">
+		<title>Plugin Content</title>
+		<link rel="stylesheet" type="text/css" href="content.css" />
+    	<script type="text/javascript" src="../requirejs/require.js"></script>
+		<script type="text/javascript">
+		require({
+			  baseUrl: '..',
+			  packages: [
+			    {
+			      name: 'dojo',
+			      location: 'org.dojotoolkit/dojo',
+			      main: 'lib/main-browser',
+			      lib: '.'
+			    },
+			    {
+			      name: 'dijit',
+			      location: 'org.dojotoolkit/dijit',
+			      main: 'lib/main',
+			      lib: '.'
+			    },
+			    {
+			      name: 'dojox',
+			      location: 'org.dojotoolkit/dojox',
+			      main: 'lib/main',
+			      lib: '.'
+			    }		    
+			  ],
+			  paths: {
+				  text: 'requirejs/text',
+				  i18n: 'requirejs/i18n'	    
+			  }
+			});
+		
+		require(["./saveHook"]);
+		</script>
+	</head>
+   <body class="nihilo" style="width: 100%; height: 100%; visibility:hidden;">
+	<div id="orion.saveRequest" style="margin: 8px;">
+		<div>Handling save request</div>
+	</div>
+	
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.orion.client.core/web/content/saveHook.js b/bundles/org.eclipse.orion.client.core/web/content/saveHook.js
new file mode 100644
index 0000000..53ef285
--- /dev/null
+++ b/bundles/org.eclipse.orion.client.core/web/content/saveHook.js
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * @license
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made 
+ * available under the terms of the Eclipse Public License v1.0 
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*global define dojo dijit orion window widgets*/
+/*jslint browser:true*/
+
+/*
+ * Glue code for content.html
+ */
+
+define(['require', 'dojo', 'orion/bootstrap', 'orion/status', 'orion/progress', 'orion/commands', 'orion/fileClient', 'orion/operationsClient',
+	        'orion/searchClient', 'orion/dialogs', 'orion/globalCommands', 'orion/breadcrumbs', 'orion/URITemplate', 'orion/PageUtil', 
+	        'dojo/parser', 'dojo/hash', 'dojo/date/locale', 'dijit/layout/BorderContainer', 'dijit/layout/ContentPane', 'orion/widgets/NewSiteDialog'], 
+			function(require, dojo, mBootstrap, mStatus, mProgress, mCommands, mFileClient, mOperationsClient, mSearchClient, mDialogs, 
+			mGlobalCommands, mBreadcrumbs, URITemplate, PageUtil) {
+
+	dojo.addOnLoad(function() {
+		mBootstrap.startup().then(function(core) {
+			var serviceRegistry = core.serviceRegistry;
+			var preferences = core.preferences;
+			// Register services
+			var dialogService = new mDialogs.DialogService(serviceRegistry);
+			var operationsClient = new mOperationsClient.OperationsClient(serviceRegistry);
+			var statusService = new mStatus.StatusReportingService(serviceRegistry, operationsClient, "statusPane", "notifications", "notificationArea");
+			var progressService = new mProgress.ProgressService(serviceRegistry, operationsClient);
+			var commandService = new mCommands.CommandService({serviceRegistry: serviceRegistry});
+			var fileClient = new mFileClient.FileClient(serviceRegistry);
+			var searcher = new mSearchClient.Searcher({serviceRegistry: serviceRegistry, commandService: commandService, fileService: fileClient});
+			
+			// parse the URL to determine what should be saved.
+			var params = PageUtil.matchResourceParameters(window.location.href);
+			if (params.contentProvider) {
+				// Note that the shape of the "orion.page.content" extension is not in any shape or form that could be considered final.
+				// We've included it to enable experimentation. Please provide feedback on IRC or bugzilla.
+				var contentProviders = serviceRegistry.getServiceReferences("orion.page.content");
+				for (var i=0; i<contentProviders.length; i++) {
+					// Exclude any navigation commands themselves, since we are the navigator.
+					var id = contentProviders[i].getProperty("id");
+					if (id === params.contentProvider) {
+						var impl = serviceRegistry.getService(contentProviders[i]);
+						var info = {};
+						var propertyNames = contentProviders[i].getPropertyNames();
+						for (var j = 0; j < propertyNames.length; j++) {
+							info[propertyNames[j]] = contentProviders[i].getProperty(propertyNames[j]);
+						}
+						if (info.saveToken) {
+							// save tokens would typically have special characters such as '?' or '&' in them so we can't use
+							// the URI template to parse them.  Not sure how we could best express this.  For now we have the plugin
+							// specify a token that signifies the start of the URl and possible terminators
+							var tokens = dojo.isArray(info.saveToken) ? info.saveToken : [info.saveToken];
+							var parameterStart = dojo.hash().indexOf(",");
+							if (parameterStart >= 0) {
+								var parameterString = dojo.hash().substring(parameterStart);
+								for (var i=0; i<tokens.length; i++) {
+									var index = parameterString.indexOf(info.saveToken[i]);
+									if (index >= 0) {
+										var contentURL = parameterString.substring(index+info.saveToken[i].length);
+										if (info.saveTokenTerminator) {
+											var terminators = dojo.isArray(info.saveTokenTerminator) ? info.saveTokenTerminator : [info.saveTokenTerminator];
+											for (var j=0; j<terminators.length; j++) {
+												var ending = contentURL.indexOf(terminators[j]);
+												if (ending >= 0) {
+													contentURL = contentURL.substring(0, ending);
+													break;
+												}
+											}
+										}
+										if (contentURL && contentURL.length > 0) {
+											dojo.place("<p>Content plugin <b>" + info.name + "</b> has saved data at <a href='" + contentURL + "'>" + contentURL + "</a>." +
+											"<p>It is cool that we know this, but now we need a way to read the blob at this URL and save back to fileClient.  See <a href='https://bugs.eclipse.org/bugs/show_bug.cgi?id=373443'>Bug 373443</a></p>" +
+											"<p>Once we are able to save the data, not clear where you would go from this page.  You could use the breadcrumb, main nav links, related pages, browser history," +
+											" etc., to get back to your next task.</p>" +
+											"<p>This area probably just tells you that the save was successful and then lets you click a link to keep editing?..ie..reload the page to return to the original editor.</p>", 
+											"orion.saveRequest" ,"only");
+										}
+										break;
+									}
+								}
+							}
+						}
+						break;
+					}
+				}
+			}
+			document.body.style.visibility = "visible";
+			dojo.parser.parse();
+		});
+	});
+});
\ No newline at end of file
diff --git a/bundles/org.eclipse.orion.client.core/web/orion/extensionCommands.js b/bundles/org.eclipse.orion.client.core/web/orion/extensionCommands.js
index 1bfd9cb..78cf80f 100644
--- a/bundles/org.eclipse.orion.client.core/web/orion/extensionCommands.js
+++ b/bundles/org.eclipse.orion.client.core/web/orion/extensionCommands.js
@@ -101,6 +101,10 @@
 		

 		var editors = getEditors(), defaultEditor = getDefaultEditor(serviceRegistry);

 		var fileCommands = [];

+		// TODO: should not be necessary to do this here, see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=373450

+		var nonHash = window.location.href.split('#')[0];

+		var hostName = nonHash.substring(0, nonHash.length - window.location.pathname.length);

+

 		for (var i=0; i < editors.length; i++) {

 			var editor = editors[i];

 			var isDefaultEditor = (defaultEditor && defaultEditor.editor === editor.id);

@@ -125,8 +129,14 @@
 						forceSingleItem: true,

 						isEditor: (isDefaultEditor ? "default": "editor") // Distinguishes from a normal fileCommand

 					};

+

 				// Pretend that this is a real service

-				var fakeService = { run: dojo.hitch(uriTemplate, uriTemplate.expand) };

+				var fakeService = { 

+					run: dojo.hitch(uriTemplate, function(data) {

+						data.OrionHome = hostName;  //  https://bugs.eclipse.org/bugs/show_bug.cgi?id=373450

+						return window.decodeURIComponent(this.expand(data));

+					})

+				};

 				fileCommands.push({properties: properties, service: fakeService});

 			}

 		}