diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls
index 9f7724cae233674fc63335ad063636d9b015b00e..8d7784125bad3226f67a0cf35266de503b5a48cb 100644
Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls differ
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/plugin.properties
new file mode 100644
index 0000000000000000000000000000000000000000..76d5aa52bab6f89a78a2e2bd11542e728466b26d
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/plugin.properties
@@ -0,0 +1,2 @@
+class = ch.ethz.sis.openbis.generic.server.asapi.v3.helper.service.JythonBasedCustomASServiceExecutor
+script-path = publication-api.py
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc5c7e99fe2eb0518d370c82b61ccc04bee4cc2c
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py
@@ -0,0 +1,93 @@
+import traceback
+
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset import DataSetKind
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create import DataSetCreation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions import DataSetFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id import DataSetPermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.id import DataStorePermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype import EntityKind
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id import EntityTypePermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id import ExperimentIdentifier
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create import SampleCreation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id import SpacePermId
+from ch.systemsx.cisd.common.logging import LogCategory
+from ch.systemsx.cisd.openbis.generic.client.web.client.exception import UserFailureException
+from java.util import ArrayList
+from org.apache.log4j import Logger
+
+operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.publication-api.py')
+
+
+def process(context, parameters):
+    method = parameters.get('method')
+
+    try:
+        if method == 'insertPublication':
+            result = insertPublication(context, parameters)
+        else:
+            raise UserFailureException('Unknown method: "%s"' % str(method if method is not None else 'None'))
+    except Exception as e:
+        operationLog.error('Exception at: ' + traceback.format_exc())
+        operationLog.error('Exception: ' + str(e))
+        result = {
+            'status': 'FAILED',
+            'error': str(e)
+        }
+    return result
+
+
+def insertPublication(context, parameters):
+    transaction = context.applicationService
+    sessionToken = transaction.loginAsSystem()
+
+    v3 = context.applicationService
+
+    name = parameters.get('name')
+    if name is None:
+        raise UserFailureException('name parameter missing')
+
+    sampleId = createPublicationSample(parameters, sessionToken, v3).get(0)
+    createDataSet(parameters, sessionToken, v3, sampleId)
+
+    return {
+        'status': 'OK',
+    }
+
+
+def createDataSet(parameters, sessionToken, v3, sampleId):
+    openBISRelatedIdentifiers = parameters.get('openBISRelatedIdentifiers').split(',')
+    identifiers = ArrayList(len(openBISRelatedIdentifiers))
+    for identifier in openBISRelatedIdentifiers:
+        identifiers.add(DataSetPermId(identifier))
+
+    dataSetIds = v3.getDataSets(sessionToken, identifiers, DataSetFetchOptions()).keys()
+    operationLog.debug('Found %d data sets.' % len(dataSetIds))
+
+    dataSetCreation = DataSetCreation()
+    dataSetCreation.setAutoGeneratedCode(True)
+    dataSetCreation.setTypeId(EntityTypePermId('PUBLICATION_DATA', EntityKind.DATA_SET))
+    dataSetCreation.setSampleId(sampleId)
+    dataSetCreation.setDataSetKind(DataSetKind.CONTAINER)
+    dataSetCreation.setComponentIds(dataSetIds)
+    dataSetCreation.setDataStoreId(DataStorePermId('STANDARD'))
+    v3.createDataSets(sessionToken, [dataSetCreation])
+
+
+def createPublicationSample(parameters, sessionToken, v3):
+    publicationOrganization = parameters.get('publicationOrganization')
+    publicationType = parameters.get('publicationType')  # The only valid value for now is "Public Repository"
+    publicationDescription = parameters.get('publicationDescription')  # Can be empty
+    publicationURL = parameters.get('publicationURL')
+    publicationIdentifier = parameters.get('publicationIdentifier')
+
+    sampleCreation = SampleCreation()
+    sampleCreation.setTypeId(EntityTypePermId('PUBLICATION'))
+    sampleCreation.setExperimentId(ExperimentIdentifier('/PUBLICATIONS/PUBLIC_REPOSITORIES/PUBLICATIONS_COLLECTION'))
+    sampleCreation.setSpaceId(SpacePermId('PUBLICATIONS'))
+    sampleCreation.setProperty('$NAME', 'TEST NAME')
+    sampleCreation.setProperty('$PUBLICATION.ORGANIZATION', publicationOrganization)
+    sampleCreation.setProperty('$PUBLICATION.TYPE', publicationType)
+    sampleCreation.setProperty('$PUBLICATION.IDENTIFIER', publicationIdentifier)
+    sampleCreation.setProperty('$PUBLICATION.URL', publicationURL)
+    sampleCreation.setProperty('$PUBLICATION.DESCRIPTION', publicationDescription)
+    return v3.createSamples(sessionToken, [sampleCreation])
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
index 18631b75318635184942854a3e1f2056b39e984f..b9f816840a1cef0a7b3966db54cb871d96a7c496 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
@@ -307,12 +307,58 @@ function ServerFacade(openbisServer) {
 	//
 	// Research collection export
 	//
-	this.exportRc = function(entities, includeRoot, metadataOnly, callbackFunction) {
+	this.exportRc = function(entities, includeRoot, metadataOnly, submissionUrl, submissionType, userInformation, callbackFunction) {
+		this.asyncExportRc({
+			"method": "exportAll",
+			"includeRoot": includeRoot,
+			"entities": entities,
+			"metadataOnly": metadataOnly,
+			"submissionUrl": submissionUrl,
+			"submissionType": submissionType,
+            "userInformation": userInformation,
+			"originUrl": window.location.origin,
+			"sessionToken": this.openbisServer.getSession(),
+		}, callbackFunction, "rc-exports-api");
+	};
+
+	this.asyncExportRc = function(parameters, callbackFunction, serviceId) {
+		require(["as/dto/service/execute/ExecuteAggregationServiceOperation",
+				"as/dto/operation/AsynchronousOperationExecutionOptions", "as/dto/service/id/DssServicePermId",
+				"as/dto/datastore/id/DataStorePermId", "as/dto/service/execute/AggregationServiceExecutionOptions"],
+			function(ExecuteAggregationServiceOperation, AsynchronousOperationExecutionOptions, DssServicePermId, DataStorePermId,
+					 AggregationServiceExecutionOptions) {
+				var dataStoreId = new DataStorePermId("STANDARD");
+				var dssServicePermId = new DssServicePermId(serviceId, dataStoreId);
+				var options = new AggregationServiceExecutionOptions();
+
+				options.withParameter("sessionToken", parameters["sessionToken"]);
+
+				options.withParameter("entities", parameters["entities"]);
+				options.withParameter("includeRoot", parameters["includeRoot"]);
+				options.withParameter("metadataOnly", parameters["metadataOnly"]);
+				options.withParameter("method", parameters["method"]);
+				options.withParameter("originUrl", parameters["originUrl"]);
+				options.withParameter("submissionType", parameters["submissionType"]);
+				options.withParameter("submissionUrl", parameters["submissionUrl"]);
+				options.withParameter("entities", parameters["entities"]);
+				options.withParameter("userId", parameters["userInformation"]["id"]);
+				options.withParameter("userEmail", parameters["userInformation"]["email"]);
+				options.withParameter("userFirstName", parameters["userInformation"]["firstName"]);
+				options.withParameter("userLastName", parameters["userInformation"]["lastName"]);
+
+				var operation = new ExecuteAggregationServiceOperation(dssServicePermId, options);
+				mainController.openbisV3.executeOperations([operation], new AsynchronousOperationExecutionOptions()).done(function(results) {
+					callbackFunction(results.executionId.permId);
+				});
+			});
+	};
+
+	//
+	// Gets submission types
+	//
+	this.listSubmissionTypes = function(callbackFunction) {
 		this.customELNApi({
-			"method" : "exportAll",
-			"includeRoot" : includeRoot,
-			"entities" : entities,
-			"metadataOnly" : metadataOnly,
+			"method": "getSubmissionTypes",
 		}, callbackFunction, "rc-exports-api");
 	};
 	
@@ -671,7 +717,6 @@ function ServerFacade(openbisServer) {
     this._callPasswordResetService = function(parameters, callbackFunction) {
         var _this = this;
         this.getOpenbisV3(function(openbisV3) {
-
             openbisV3.loginAsAnonymousUser().done(function(sessionToken) {
                 _this.openbisServer._internal.sessionToken = sessionToken;
 
@@ -694,6 +739,7 @@ function ServerFacade(openbisServer) {
     }
 
  	this.customELNApi = function(parameters, callbackFunction, service) {
+		var _this = this;
  		if(!service) {
  			service = "eln-lims-api";
  		}
@@ -705,24 +751,28 @@ function ServerFacade(openbisServer) {
  		
  		var dataStoreCode = profile.getDefaultDataStoreCode();
  		this.openbisServer.createReportFromAggregationService(dataStoreCode, service, parameters, function(data) {
- 			var error = null;
- 			var result = {};
- 			if(data.error) { //Error Case 1
- 				error = data.error.message;
- 			} else if (data.result.columns[1].title === "Error") { //Error Case 2
- 				error = data.result.rows[0][1].value;
- 			} else if (data.result.columns[0].title === "STATUS" && data.result.rows[0][0].value === "OK") { //Success Case
- 				result.message = data.result.rows[0][1].value;
- 				result.data = data.result.rows[0][2].value;
- 				if(result.data) {
- 					result.data = JSON.parse(result.data);
- 				}
- 			} else {
- 				error = "Unknown Error.";
- 			}
- 			callbackFunction(error, result);
+			_this.customELNApiCallbackHandler(data, callbackFunction);
  		});
-	}
+	};
+
+	this.customELNApiCallbackHandler = function(data, callbackFunction) {
+		var error = null;
+		var result = {};
+		if (data && data.error) { //Error Case 1
+			error = data.error.message;
+		} else if (data && data.result.columns[1].title === "Error") { //Error Case 2
+			error = data.result.rows[0][1].value;
+		} else if (data && data.result.columns[0].title === "STATUS" && data.result.rows[0][0].value === "OK") { //Success Case
+			result.message = data.result.rows[0][1].value;
+			result.data = data.result.rows[0][2].value;
+			if(result.data) {
+				result.data = JSON.parse(result.data);
+			}
+		} else {
+			error = "Unknown Error.";
+		}
+		callbackFunction(error, result);
+	};
 
 	this.customELNASAPI = function(parameters, callbackFunction) {
 		this.customASService(parameters, callbackFunction, "as-eln-lims-api");
@@ -2575,10 +2625,10 @@ function ServerFacade(openbisServer) {
 
 	this.getSessionInformation = function(callbackFunction) {
 		mainController.openbisV3.getSessionInformation().done(function(sessionInfo) {
-                callbackFunction(sessionInfo);
+			callbackFunction(sessionInfo);
         }).fail(function(result) {
-				Util.showFailedServerCallError(result);
-				callbackFunction(false);
+			Util.showFailedServerCallError(result);
+			callbackFunction(false);
 		});
 	}
 
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js
index e501e670936df304c7e03f164f910f7fbb453845..9658d70d42f076133974dd2075ce5654b55951de 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js
@@ -322,25 +322,28 @@ var FormUtil = new function() {
 		Select2Manager.add($dropdown);
 		return $dropdown;
 	}
-	
-	this.getPlainDropdown = function(mapVals, placeHolder) {
 
-		var $component = $("<select>", {class : 'form-control'});
-		if(placeHolder) {
-			$component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text(placeHolder));
-		}
-		for(var mIdx = 0; mIdx < mapVals.length; mIdx++) {
+	this.setValuesToComponent = function ($component, mapVals) {
+		for (var mIdx = 0; mIdx < mapVals.length; mIdx++) {
 			var $option = $("<option>").attr('value', mapVals[mIdx].value).text(mapVals[mIdx].label);
-			if(mapVals[mIdx].disabled) {
+			if (mapVals[mIdx].disabled) {
 				$option.attr('disabled', '');
 			}
-			if(mapVals[mIdx].selected) {
+			if (mapVals[mIdx].selected) {
 				$option.attr('selected', '');
 			}
 			$component.append($option);
 		}
+	};
+
+	this.getPlainDropdown = function(mapVals, placeHolder) {
+		var $component = $("<select>", {class : 'form-control'});
+		if (placeHolder) {
+			$component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text(placeHolder));
+		}
+		this.setValuesToComponent($component, mapVals);
 		return $component;
-	}
+	};
 	
 	this.getDataSetsDropDown = function(code, dataSetTypes) {
 		var $component = $("<select>", { class : 'form-control ' });
@@ -350,11 +353,11 @@ var FormUtil = new function() {
 		
 		$component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text('Select a dataset type'));
 		
-		for(var i = 0; i < dataSetTypes.length; i++) {
+		for (var i = 0; i < dataSetTypes.length; i++) {
 			var datasetType = dataSetTypes[i];
 			var label = Util.getDisplayNameFromCode(datasetType.code);
 			var description = Util.getEmptyIfNull(datasetType.description);
-			if(description !== "") {
+			if (description !== "") {
 				label += " (" + description + ")";
 			}
 			
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js
index f09bd1a1cf9355bcbac788de1f49c8f923ad59f8..7bbbd3de8e2eb73f3e5c18fc7f186fdeb7dd50fb 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js
@@ -39,7 +39,7 @@ function ExportTreeView(exportTreeController, exportTreeModel) {
                 'onClick': '$("form[name=\'exportTreeForm\']").submit()' });
         $header.append($exportButton);
 
-		var $infoBox = FormUtil.getInfoBox("You can select any parts of the accesible openBIS structure to export:", [
+		var $infoBox = FormUtil.getInfoBox("You can select any parts of the accessible openBIS structure to export:", [
 		                                   "If you select a tree node and do not expand it, everything below this node will be exported by default.",
 		                                   "To export selectively only parts of a tree, open the nodes and select what to export."
 		]);
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js
index 5424e879bc64f561f63f1b8fed3ff7a6ba0d4f61..514b6a15d2dcc03336b6c499ac7ad13528fbe454 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js
@@ -15,7 +15,6 @@
  */
 
 function ResearchCollectionExportController(parentController) {
-    var parentController = parentController;
     var researchCollectionExportModel = new ResearchCollectionExportModel();
     var researchCollectionExportView = new ResearchCollectionExportView(this, researchCollectionExportModel);
 
@@ -23,27 +22,106 @@ function ResearchCollectionExportController(parentController) {
         researchCollectionExportView.repaint(views);
     };
 
+    this.initialiseSubmissionTypesDropdown = function(callback) {
+        Util.blockUI();
+        mainController.serverFacade.listSubmissionTypes(function(error, result) {
+            Util.unblockUI();
+            if (error) {
+                Util.showError(error);
+            } else {
+                researchCollectionExportModel.submissionTypes = result.data.map(function (resultItem) {
+                    return {
+                        value: resultItem.url,
+                        label: resultItem.title
+                    };
+                });
+                researchCollectionExportView.refreshSubmissionTypeDropdown();
+            }
+        });
+    };
+
     this.exportSelected = function() {
+        var _this = this;
         var selectedNodes = $(researchCollectionExportModel.tree).fancytree('getTree').getSelectedNodes();
 
+        var selectedOption = researchCollectionExportView.$submissionTypeDropdown.find(":selected");
+        var submissionUrl = selectedOption.val();
+        var submissionType = selectedOption.text();
+
         var toExport = [];
         for (var eIdx = 0; eIdx < selectedNodes.length; eIdx++) {
             var node = selectedNodes[eIdx];
-            toExport.push({ type: node.data.entityType, permId : node.key, expand : !node.expanded });
+            toExport.push({type: node.data.entityType, permId: node.key, expand: !node.expanded});
         }
 
         if (toExport.length === 0) {
-            Util.showInfo("First select something to export.");
+            Util.showInfo('First select something to export.');
+        } else if (!submissionUrl) {
+            Util.showInfo('First select submission type.');
+        } else {
+            Util.blockUI();
+            this.getUserInformation(function(userInformation) {
+                mainController.serverFacade.exportRc(toExport, true, false, submissionUrl, submissionType, userInformation,
+                        function(operationExecutionPermId) {
+                            _this.waitForOpExecutionResponse(operationExecutionPermId, function(error, result) {
+                                Util.unblockUI();
+                                if (result && result.data && result.data.url) {
+                                    var win = window.open(result.data.url, '_blank');
+                                    win.focus();
+                                    mainController.refreshView();
+                                } else {
+                                    if (error) {
+                                        Util.showError(error);
+                                    } else {
+                                        Util.showError('Returned result format is not correct.');
+                                    }
+                                }
+                            });
+                        });
+            });
         }
+    };
 
-        Util.blockUI();
-        mainController.serverFacade.exportRc(toExport, true, false, function(error, result) {
-            if (error) {
-                Util.showError(error);
-            } else {
-                Util.showSuccess("Export is being processed. If you logout the process will stop.", function() { Util.unblockUI(); });
-                mainController.refreshView();
-            }
+    this.waitForOpExecutionResponse = function(operationExecutionPermIdString, callbackFunction) {
+        var _this = this;
+        require(["as/dto/operation/id/OperationExecutionPermId",
+                "as/dto/operation/fetchoptions/OperationExecutionFetchOptions"],
+            function(OperationExecutionPermId, OperationExecutionFetchOptions) {
+                var operationExecutionPermId = new OperationExecutionPermId(operationExecutionPermIdString);
+                var fetchOptions = new OperationExecutionFetchOptions();
+                var fetchOptionsDetails = fetchOptions.withDetails();
+                fetchOptionsDetails.withResults();
+                fetchOptionsDetails.withError();
+                mainController.openbisV3.getOperationExecutions([operationExecutionPermId], fetchOptions).done(function(results) {
+                    var result = results[operationExecutionPermIdString];
+                    var v2Result = null;
+                    if (result && result.details && result.details.results) {
+                        v2Result = result.details.results[0];
+                    }
+
+                    if (result && result.state === 'FINISHED') {
+                        mainController.serverFacade.customELNApiCallbackHandler(v2Result, callbackFunction);
+                    } else if (!result || result.state === 'FAILED') {
+                        mainController.serverFacade.customELNApiCallbackHandler(v2Result, callbackFunction);
+                    } else {
+                        setTimeout(function() {
+                            _this.waitForOpExecutionResponse(operationExecutionPermIdString, callbackFunction);
+                        }, 3000);
+                    }
+                });
+            });
+    };
+
+    this.getUserInformation = function(callback) {
+        var userId = mainController.serverFacade.getUserId();
+        mainController.serverFacade.getSessionInformation(function(sessionInfo) {
+            var userInformation = {
+                firstName: sessionInfo.person.firstName,
+                lastName: sessionInfo.person.lastName,
+                email: sessionInfo.person.email,
+                id: userId,
+            };
+            callback(userInformation);
         });
-    }
+    };
 }
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js
index 3f99119599869a116cc69f7df2461f6beb366c98..7099e83d87552bbebcdff0b2788f00a34d18d6e1 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js
@@ -15,5 +15,9 @@
  */
 
 function ResearchCollectionExportModel() {
+    this.submissionTypes = [];
 
+    this.addSubmissionType = function(submissionType) {
+        submissionTypes.push(submissionType);
+    };
 }
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js
index a4ce7b4d9ac9a3de31dbb603a650cd00df06f1f6..7945f200cb3b4bdf5591f784dbeff1a6335ce1f0 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js
@@ -15,38 +15,38 @@
  */
 
 function ResearchCollectionExportView(researchCollectionExportController, researchCollectionExportModel) {
-    var researchCollectionExportController = researchCollectionExportController;
-    var researchCollectionExportModel = researchCollectionExportModel;
     var exportTreeView = new ExportTreeView(researchCollectionExportController, researchCollectionExportModel);
 
     this.repaint = function(views) {
+        researchCollectionExportController.initialiseSubmissionTypesDropdown();
+
         var $header = views.header;
         var $container = views.content;
 
         var $form = $("<div>");
-
         var $formColumn = $("<form>", {
             'name': 'rcExportForm',
-            'role' : "form",
-            'action' : 'javascript:void(0);',
-            'onsubmit' : 'mainController.currentView.exportSelected();'
+            'role': 'form',
+            'action': 'javascript:void(0);',
+            'onsubmit': 'mainController.currentView.exportSelected();'
         });
-
         $form.append($formColumn);
 
-        var $infoBox = FormUtil.getInfoBox("You can select any parts of the accesible openBIS structure to export:", [
-            "If you select a tree node and do not expand it, everything below this node will be exported by default.",
-            "To export selectively only parts of a tree, open the nodes and select what to export."
+        var $infoBox = FormUtil.getInfoBox('You can select any parts of the accessible openBIS structure to export:', [
+            'If you select a tree node and do not expand it, everything below this node will be exported by default.',
+            'To export selectively only parts of a tree, open the nodes and select what to export.'
         ]);
-        $infoBox.css("border", "none");
+        $infoBox.css('border', 'none');
         $container.append($infoBox);
 
-        var $tree = $("<div>", { "id" : "exportsTree" });
-        $formColumn.append($("<br>"));
+        var $tree = $('<div>', { 'id' : 'exportsTree' });
+        $formColumn.append($('<br>'));
         $formColumn.append(FormUtil.getBox().append($tree));
 
         $container.append($form);
 
+        this.paintSubmissionTypeDropdown($container);
+
         researchCollectionExportModel.tree = TreeUtil.getCompleteTree($tree);
 
         var $formTitle = $('<h2>').append('Research Collection Export Builder');
@@ -55,5 +55,21 @@ function ResearchCollectionExportView(researchCollectionExportController, resear
         var $exportButton = $('<input>', { 'type': 'submit', 'class': 'btn btn-primary', 'value': 'Export Selected',
                 'onClick': '$("form[name=\'rcExportForm\']").submit()'});
         $header.append($exportButton);
+    };
+
+    this.paintSubmissionTypeDropdown = function($container) {
+        this.$submissionTypeDropdown = this.getSubmissionTypeDropdown();
+        var entityTypeDropdownFormGroup = FormUtil.getFieldForComponentWithLabel(this.$submissionTypeDropdown, 'Submission Type', null, true);
+        entityTypeDropdownFormGroup.css('width', '50%');
+        $container.append(entityTypeDropdownFormGroup);
+    };
+
+    this.getSubmissionTypeDropdown = function() {
+        return FormUtil.getDropdown(researchCollectionExportModel.submissionTypes, 'Select a submission type');
+    };
+
+    this.refreshSubmissionTypeDropdown = function() {
+        FormUtil.setValuesToComponent(this.$submissionTypeDropdown, researchCollectionExportModel.submissionTypes);
     }
+
 }
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py
index 0180a55e830b6d3c02ed5b79582674cca31b961f..932c0fa302302c30bd0dc8174b5fd680c62a0707 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py
@@ -108,8 +108,7 @@ def strip_tags(html):
     return s.get_data()
 
 
-def displayResult(isOk, tableBuilder):
-    result = None;
+def displayResult(isOk, tableBuilder, result=None):
     if isOk:
         tableBuilder.addHeader("STATUS");
         tableBuilder.addHeader("MESSAGE");
@@ -167,11 +166,12 @@ def findEntitiesToExport(params):
     entities = params.get("entities");
 
     for entity in entities:
-        entityAsPythonMap = {"type": entity.get("type"), "permId": entity.get("permId"), "expand": entity.get("expand")};
-        addToExportWithoutRepeating(entitiesToExport, entityAsPythonMap);
+        entityAsPythonMap = { "type" : entity.get("type"), "permId" : entity.get("permId"), "expand" : entity.get("expand") };
+        entitiesToExport.append(entityAsPythonMap);
         if entity.get("expand"):
             entitiesToExpand.append(entityAsPythonMap);
 
+    operationLog.info("Found %d entities to expand." % len(entitiesToExpand))
     while entitiesToExpand:
         entityToExpand = entitiesToExpand.popleft();
         type = entityToExpand["type"];
@@ -183,7 +183,7 @@ def findEntitiesToExport(params):
             results = v3.searchSpaces(sessionToken, criteria, SpaceFetchOptions());
             operationLog.info("Found: " + str(results.getTotalCount()) + " spaces");
             for space in results.getObjects():
-                entityFound = {"type": "SPACE", "permId": space.getCode()};
+                entityFound = {"type": "SPACE", "permId": space.getCode(), "registrationDate": space.getRegistrationDate()};
                 addToExportWithoutRepeating(entitiesToExport, entityFound);
                 entitiesToExpand.append(entityFound);
         if type == "SPACE":
@@ -192,7 +192,7 @@ def findEntitiesToExport(params):
             results = v3.searchProjects(sessionToken, criteria, ProjectFetchOptions());
             operationLog.info("Found: " + str(results.getTotalCount()) + " projects");
             for project in results.getObjects():
-                entityFound = {"type": "PROJECT", "permId": project.getPermId().getPermId()};
+                entityFound = {"type": "PROJECT", "permId": project.getPermId().getPermId(), "registrationDate": project.getRegistrationDate()};
                 addToExportWithoutRepeating(entitiesToExport, entityFound);
                 entitiesToExpand.append(entityFound);
         if type == "PROJECT":
@@ -201,7 +201,8 @@ def findEntitiesToExport(params):
             results = v3.searchExperiments(sessionToken, criteria, ExperimentFetchOptions());
             operationLog.info("Found: " + str(results.getTotalCount()) + " experiments");
             for experiment in results.getObjects():
-                entityFound = {"type": "EXPERIMENT", "permId": experiment.getPermId().getPermId()};
+                entityFound = {"type": "EXPERIMENT", "permId": experiment.getPermId().getPermId(),
+                               "registrationDate": experiment.getRegistrationDate()};
                 addToExportWithoutRepeating(entitiesToExport, entityFound);
                 entitiesToExpand.append(entityFound);
         if type == "EXPERIMENT":
@@ -213,25 +214,29 @@ def findEntitiesToExport(params):
             dCriteria = DataSetSearchCriteria();
             dCriteria.withExperiment().withPermId().thatEquals(permId);
             dCriteria.withoutSample();
-            dResults = v3.searchDataSets(sessionToken, dCriteria, DataSetFetchOptions());
+            fetchOptions = DataSetFetchOptions()
+            fetchOptions.withDataStore()
+            dResults = v3.searchDataSets(sessionToken, dCriteria, fetchOptions);
             operationLog.info("Found: " + str(dResults.getTotalCount()) + " datasets");
             for dataset in dResults.getObjects():
-                entityFound = {"type": "DATASET", "permId": dataset.getPermId().getPermId()};
+                entityFound = {"type": "DATASET", "permId": dataset.getPermId().getPermId(), "registrationDate": dataset.getRegistrationDate()};
                 addToExportWithoutRepeating(entitiesToExport, entityFound);
                 entitiesToExpand.append(entityFound);
 
             operationLog.info("Found: " + str(results.getTotalCount()) + " samples");
             for sample in results.getObjects():
-                entityFound = {"type": "SAMPLE", "permId": sample.getPermId().getPermId()};
+                entityFound = {"type": "SAMPLE", "permId": sample.getPermId().getPermId(), "registrationDate": sample.getRegistrationDate()};
                 addToExportWithoutRepeating(entitiesToExport, entityFound);
                 entitiesToExpand.append(entityFound);
         if type == "SAMPLE":
             criteria = DataSetSearchCriteria();
             criteria.withSample().withPermId().thatEquals(permId);
-            results = v3.searchDataSets(sessionToken, criteria, DataSetFetchOptions());
+            fetchOptions = DataSetFetchOptions()
+            fetchOptions.withDataStore()
+            results = v3.searchDataSets(sessionToken, criteria, fetchOptions);
             operationLog.info("Found: " + str(results.getTotalCount()) + " datasets");
             for dataset in results.getObjects():
-                entityFound = {"type": "DATASET", "permId": dataset.getPermId().getPermId()};
+                entityFound = {"type": "DATASET", "permId": dataset.getPermId().getPermId(), "registrationDate": dataset.getRegistrationDate()};
                 addToExportWithoutRepeating(entitiesToExport, entityFound);
                 entitiesToExpand.append(entityFound);
         if type == "DATASET" and not metadataOnly:
@@ -254,19 +259,28 @@ def cleanUp(tempDirPath, tempZipFilePath):
 
 # Generates ZIP file and stores it in workspace
 def generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFilePath):
+    # Create Zip File
+    fos = FileOutputStream(tempZipFilePath);
+    zos = ZipOutputStream(fos);
+
+    generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath)
+
+    zos.close();
+    fos.close();
+
+
+def generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath):
     # Services used during the export process
     v3 = ServiceProvider.getV3ApplicationService();
     v3d = ServiceProvider.getApplicationContext().getBean(V3_DSS_BEAN);
     objectCache = {};
     objectMapper = GenericObjectMapper();
     objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
-
     # To avoid empty directories on the zip file, it makes the first found entity the base directory
     baseDirToCut = None;
+    fileMetadata = []
+    emptyZip = True
 
-    # Create Zip File
-    fos = FileOutputStream(tempZipFilePath);
-    zos = ZipOutputStream(fos);
     for entity in entities:
         type = entity["type"];
         permId = entity["permId"];
@@ -357,6 +371,7 @@ def generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFil
             rawFile.getParentFile().mkdirs();
             IOUtils.copyLarge(rawFileInputStream, FileOutputStream(rawFile));
             addToZipFile(filePath, rawFile, zos);
+            emptyZip = False
 
         # To avoid empty directories on the zip file, it makes the first found entity the base directory
         if not includeRoot:
@@ -374,20 +389,25 @@ def generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFil
         if entityObj is not None and entityFilePath is not None:
             # JSON
             entityJson = String(objectMapper.writeValueAsString(entityObj));
-            addFile(tempDirPath, entityFilePath, "json", entityJson.getBytes(), zos);
+            fileMetadatum = addFile(tempDirPath, entityFilePath, "json", entityJson.getBytes(), zos);
+            fileMetadata.append(fileMetadatum)
+            emptyZip = False
             # TEXT
             entityTXT = String(getTXT(entityObj, v3, sessionToken, False));
-            addFile(tempDirPath, entityFilePath, "txt", entityTXT.getBytes(), zos);
+            fileMetadatum = addFile(tempDirPath, entityFilePath, "txt", entityTXT.getBytes(), zos);
+            fileMetadata.append(fileMetadatum)
             # DOCX
             entityDOCX = getDOCX(entityObj, v3, sessionToken, False);
-            addFile(tempDirPath, entityFilePath, "docx", entityDOCX, zos);
+            fileMetadatum = addFile(tempDirPath, entityFilePath, "docx", entityDOCX, zos);
+            fileMetadata.append(fileMetadatum)
             # HTML
             entityHTML = getDOCX(entityObj, v3, sessionToken, True);
-            addFile(tempDirPath, entityFilePath, "html", entityHTML, zos);
+            fileMetadatum = addFile(tempDirPath, entityFilePath, "html", entityHTML, zos);
+            fileMetadata.append(fileMetadatum)
             operationLog.info("--> Entity type: " + type + " permId: " + permId + " post html.");
-
-    zos.close();
-    fos.close();
+    if emptyZip:
+        raise IOError('Nothing added to ZIP file.')
+    return fileMetadata
 
 
 def generateDownloadUrl(sessionToken, tempZipFileName, tempZipFilePath):
@@ -573,12 +593,25 @@ def getTXT(entityObj, v3, sessionToken, isRichText):
     return txtBuilder.toString();
 
 def addFile(tempDirPath, entityFilePath, extension, fileContent, zos):
-    entityFile = File(tempDirPath + entityFilePath + "." + extension);
+    entityFileNameWithExtension = entityFilePath + "." + extension
+    entityFile = File(tempDirPath + entityFileNameWithExtension);
     entityFile.getParentFile().mkdirs();
     IOUtils.write(fileContent, FileOutputStream(entityFile));
-    addToZipFile(entityFilePath + "." + extension, entityFile, zos);
+    addToZipFile(entityFileNameWithExtension, entityFile, zos);
     FileUtils.forceDelete(entityFile);
 
+    extensionToMimeType = {
+        'json': 'application/json',
+        'txt': 'text/plain',
+        'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+        'html': 'text/html',
+    }
+
+    return {
+        'fileName': entityFileNameWithExtension[1:],
+        'mimeType': extensionToMimeType.get(extension, 'application/octet-stream')
+    }
+
 def getFilePath(spaceCode, projCode, expCode, sampCode, dataCode):
     fileName = "";
     if spaceCode is not None:
@@ -594,18 +627,18 @@ def getFilePath(spaceCode, projCode, expCode, sampCode, dataCode):
     return fileName;
 
 def addToZipFile(path, file, zos):
-        fis = FileInputStream(file);
-        zipEntry = ZipEntry(path[1:]); # Making paths relative to make them compatible with Windows zip implementation
-        zos.putNextEntry(zipEntry);
-
-        bytes = jarray.zeros(1024, "b");
+    fis = FileInputStream(file);
+    zipEntry = ZipEntry(path[1:]); # Making paths relative to make them compatible with Windows zip implementation
+    zos.putNextEntry(zipEntry);
+
+    bytes = jarray.zeros(1024, "b");
+    length = fis.read(bytes);
+    while length >= 0:
+        zos.write(bytes, 0, length);
         length = fis.read(bytes);
-        while length >= 0:
-            zos.write(bytes, 0, length);
-            length = fis.read(bytes);
-        
-        zos.closeEntry();
-        fis.close();
+
+    zos.closeEntry();
+    fis.close();
 
 def getConfigurationProperty(transaction, propertyName):
     threadProperties = transaction.getGlobalState().getThreadParameters().getThreadProperties();
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties
index 16b4f674e2cf42167bc0e424c4c15e2059e1bf81..eabafa539e8a00741e999e6ce7b45bbb7eb62ed9 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties
@@ -4,5 +4,5 @@ script-path = rcExports.py
 limit-data-size-megabytes = 4000
 service-document-url=https://test.research-collection.ethz.ch/swordv2/servicedocument
 realm=SWORD2
-user=
-password=
\ No newline at end of file
+user=youruser
+password=yourpassword
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py
index dbc1a7a71e9494e28529b5e369140d0c6b9ba215..e354f38dcdd770f6a8b98b765556fc936ad5dfab 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py
@@ -1,103 +1,384 @@
 #
 # Copyright 2016 ETH Zuerich, Scientific IT Services
 #
-# Licensed under the Apache License, Version 2.0 (the "License");
+# Licensed under the Apache License, Version 2.0 (the 'License');
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
 #      http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
+# distributed under the License is distributed on an 'AS IS' BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-import threading
-import time
-import urllib2
+import json
+import os
+import traceback
 import xml.etree.ElementTree as ET
+
+import datetime
+import time
 from ch.systemsx.cisd.common.logging import LogCategory
-from ch.systemsx.cisd.openbis.dss.generic.shared import ServiceProvider
 from java.io import File
+from java.io import FileOutputStream
+from java.net import URI
+from java.nio.file import Paths
+from java.text import SimpleDateFormat
+from java.util import UUID
+from java.util.zip import ZipOutputStream
+from org.apache.commons.io import FileUtils
 from org.apache.log4j import Logger
+from org.eclipse.jetty.client import HttpClient
+from org.eclipse.jetty.client.util import BasicAuthentication
+from org.eclipse.jetty.http import HttpMethod
+from org.eclipse.jetty.util.ssl import SslContextFactory
 
-from exportsApi import generateZipFile, cleanUp, displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty
+from exportsApi import displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty, generateFilesInZip, addToZipFile, cleanUp
 
-operationLog = Logger.getLogger(str(LogCategory.OPERATION) + ".rcExports.py")
+operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.rcExports.py')
 
 
 def process(tr, params, tableBuilder):
-    method = params.get("method")
-    isOk = False
+    method = params.get('method')
 
     # Set user using the service
-
     tr.setUserId(userId)
-    if method == "exportAll":
-        isOk = expandAndExport(tr, params)
 
-    displayResult(isOk, tableBuilder)
+    if method == 'exportAll':
+        resultUrl = expandAndExport(tr, params)
+        displayResult(resultUrl is not None, tableBuilder, '{"url": "' + resultUrl + '"}' if resultUrl is not None else None)
+    elif method == 'getSubmissionTypes':
+        collectionUrls = getSubmissionTypes(tr)
+        displayResult(collectionUrls is not None, tableBuilder, collectionUrls)
 
 
-def expandAndExport(tr, params):
-    #Services used during the export process
-    # TO-DO Login on the services as ETL server but on behalf of the user that makes the call
-    sessionToken = params.get("sessionToken")
-    v3 = ServiceProvider.getV3ApplicationService()
+def getSubmissionTypes(tr):
+    url = getConfigurationProperty(tr, 'service-document-url')
+
+    httpClient = None
+    try:
+        httpClient = authenticateUserJava(url, tr)
+        httpClient.setFollowRedirects(True)
+        httpClient.start()
+
+        collections = fetchServiceDocument(url, httpClient)
+    finally:
+        if httpClient is not None:
+            httpClient.stop()
+    return collections
+
 
+def expandAndExport(tr, params):
     entitiesToExport = findEntitiesToExport(params)
     validateDataSize(entitiesToExport, tr)
 
-    includeRoot = params.get("includeRoot")
-
-    operationLog.info("Found " + str(len(entitiesToExport)) + " entities to export, export thread will start")
-    thread = threading.Thread(target=export, args=(tr, sessionToken, entitiesToExport, includeRoot))
-    thread.daemon = True
-    thread.start()
+    userInformation = {
+        'firstName': params.get('userFirstName'),
+        'lastName': params.get('userLastName'),
+        'email': params.get('userEmail'),
+    }
 
-    return True
+    operationLog.info('Found ' + str(len(entitiesToExport)) + ' entities to export')
+    return export(entities=entitiesToExport, tr=tr, params=params, userInformation=userInformation)
 
 
-def export(tr, sessionToken, entities, includeRoot):
+def export(entities, tr, params, userInformation):
     #Create temporal folder
-    tempDirName = "export_" + str(time.time())
-    tempDirPathFile = File.createTempFile(tempDirName, None)
-    tempDirPathFile.delete()
-    tempDirPathFile.mkdir()
-    tempDirPath = tempDirPathFile.getCanonicalPath()
-    tempZipFileName = tempDirName + ".zip"
-    tempZipFilePath = tempDirPath + ".zip"
+    timeNow = time.time()
+
+    exportDirName = 'export_' + str(timeNow)
+    exportDir = File.createTempFile(exportDirName, None)
+    exportDirPath = exportDir.getCanonicalPath()
+    exportDir.delete()
+    exportDir.mkdir()
+
+    contentZipFileName = 'content.zip'
+    contentDirName = 'content_' + str(timeNow)
+    contentDir = File.createTempFile(contentDirName, None, exportDir)
+    contentDirPath = contentDir.getCanonicalPath()
+    contentDir.delete()
+    contentDir.mkdir()
+
+    contentZipFilePath = exportDirPath + '/' + contentZipFileName
+
+    exportZipFilePath = exportDirPath + '.zip'
+    exportZipFileName = exportDirName + '.zip'
+
+    generateInternalZipFile(entities, params, contentDirPath, contentZipFilePath)
+    FileUtils.forceDelete(File(contentDirPath))
+
+    generateExternalZipFile(params=params, exportDirPath=exportDirPath, contentZipFilePath=contentZipFilePath, contentZipFileName=contentZipFileName,
+                            exportZipFileName=exportZipFilePath, userInformation=userInformation, entities=entities)
+    resultUrl = sendToDSpace(params=params, tr=tr, tempZipFileName=exportZipFileName, tempZipFilePath=exportZipFilePath)
+    cleanUp(exportDirPath, exportZipFilePath)
+    return resultUrl
 
-    generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFilePath)
 
-    #Send Email
-    sendToDSpace(tr, tempZipFileName, tempZipFilePath)
+def sendToDSpace(params, tr, tempZipFileName, tempZipFilePath):
+    depositUrl = str(params.get('submissionUrl'))
 
-    cleanUp(tempDirPath, tempZipFilePath)
-    return True
+    headers = {
+        'In-Progress': 'true',
+        'Content-Disposition': 'filename=' + tempZipFileName,
+        'Content-Type': 'application/zip',
+        'Content-Length': os.stat(tempZipFilePath).st_size,
+        'Content-Transfer-Encoding': 'binary',
+        'Packaging': 'http://purl.org/net/sword/package/METSDSpaceSIP',
+        'On-Behalf-Of': str(params.get('userId')),
+    }
 
+    httpClient = None
+    try:
+        httpClient = authenticateUserJava(depositUrl, tr)
 
-def sendToDSpace(tr, tempZipFileName, tempZipFilePath):
-    collectionUrls = fetchServiceDocument(tr)
-    print('URLs: %s' % collectionUrls)
-    print('URLs count: %s' % len(collectionUrls))
+        httpClient.setFollowRedirects(True)
+        httpClient.start()
 
-def fetchServiceDocument(tr):
+        request = httpClient.newRequest(depositUrl)
+        for key, value in headers.iteritems():
+            request.header(key, str(value))
+        response = request.method(HttpMethod.POST).file(Paths.get(tempZipFilePath), 'application/zip').send()
+        status = response.getStatus()
+        if status >= 300:
+            reason = response.getReason()
+            raise ValueError('Unsuccessful response from the server: %s %s' % (status, reason))
+
+        xmlText = response.getContentAsString().encode('utf-8')
+        xmlRoot = ET.fromstring(xmlText)
+        linkElement = xmlRoot.find('xmlns:link[@rel="alternate"]', namespaces=dict(xmlns='http://www.w3.org/2005/Atom'))
+        if linkElement is None:
+            raise ValueError('No redirection URL is found in the response.')
+
+        href = linkElement.attrib['href']
+
+        return href
+    except Exception as e:
+        operationLog.error('Exception at: ' + traceback.format_exc())
+        operationLog.error('Exception: ' + str(e))
+        raise e
+    finally:
+        if httpClient is not None:
+            httpClient.stop()
+
+
+def authenticateUserJava(url, tr):
+    sslContextFactory = SslContextFactory()
+    httpClient = HttpClient(sslContextFactory)
+    uri = URI(url)
     user = getConfigurationProperty(tr, 'user')
     password = getConfigurationProperty(tr, 'password')
     realm = getConfigurationProperty(tr, 'realm')
-    url = getConfigurationProperty(tr, 'service-document-url')
-    pm = urllib2.HTTPPasswordMgr()
-    pm.add_password(realm, url, user, password)
-    authHandler = urllib2.HTTPBasicAuthHandler(pm)
-    opener = urllib2.build_opener(authHandler)
-    urllib2.install_opener(opener)
-    response = urllib2.urlopen(url)
-    xmlText = response.read()
+    auth = httpClient.getAuthenticationStore()
+    auth.addAuthentication(BasicAuthentication(uri, realm, user, password))
+    return httpClient
+
+
+def fetchServiceDocument(url, httpClient):
+    response = httpClient.newRequest(url).method(HttpMethod.GET).send()
 
+    xmlText = response.getContentAsString().encode('utf-8')
     xmlRoot = ET.fromstring(xmlText)
     collections = xmlRoot.findall('./xmlns:workspace/xmlns:collection[@href]', namespaces=dict(xmlns='http://www.w3.org/2007/app'))
 
-    collectionUrls = map(lambda collection: collection.attrib['href'], collections)
-    return collectionUrls
+    def collectionToDictionaryMapper(collection):
+        return {
+            'title': collection.find('./atom:title', namespaces=dict(atom='http://www.w3.org/2005/Atom')).text,
+            'url': collection.attrib['href'],
+        }
+
+    return json.dumps(map(collectionToDictionaryMapper, collections))
+
+
+def generateInternalZipFile(entities, params, tempDirPath, tempZipFilePath):
+    # Generates ZIP file with selected item for export
+
+    sessionToken = params.get('sessionToken')
+    includeRoot = params.get('includeRoot')
+
+    fos = None
+    zos = None
+    try:
+        fos = FileOutputStream(tempZipFilePath)
+        zos = ZipOutputStream(fos)
+
+        fileMetadata = generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath)
+    finally:
+        if zos is not None:
+            zos.close()
+        if fos is not None:
+            fos.close()
+
+    return fileMetadata
+
+
+def generateExternalZipFile(params, exportDirPath, contentZipFilePath, contentZipFileName, exportZipFileName, userInformation, entities):
+    # Generates ZIP file which will go to the research collection server
+
+    originUrl=params.get('originUrl')
+    submissionType = str(params.get('submissionType'))
+
+    fileMetadata = [
+        {
+            'fileName': contentZipFileName,
+            'mimeType': 'application/zip'
+        }
+    ]
+
+    fos = None
+    zos = None
+    try:
+        fos = FileOutputStream(exportZipFileName)
+        zos = ZipOutputStream(fos)
+
+        addToZipFile(' ' + contentZipFileName, File(contentZipFilePath), zos)
+
+        generateXML(zipOutputStream=zos, fileMetadata=fileMetadata, exportDirPath=exportDirPath, submissionType=submissionType,
+                    userInformation=userInformation, entities=entities, originUrl=originUrl)
+    except Exception as e:
+        operationLog.error('Exception at: ' + traceback.format_exc())
+        operationLog.error('Exception: ' + str(e))
+        raise e
+    finally:
+        if zos is not None:
+            zos.close()
+        if fos is not None:
+            fos.close()
+
+
+def generateXML(zipOutputStream, fileMetadata, exportDirPath, submissionType, userInformation, entities, originUrl):
+    ns = {
+        'mets': 'http://www.loc.gov/METS/',
+        'xlink': 'http://www.w3.org/1999/xlink',
+        'dim': 'http://www.dspace.org/xmlns/dspace/dim'
+    }
+
+    entityPermIds = map(lambda entity: entity['permId'], entities)
+    permIdsStr = reduce(lambda str, permId: str + ',' + permId, entityPermIds)
+
+    withRegistrationDates = filter(lambda entity: 'registrationDate' in entity, entities)
+    registrationDates = map(lambda entity: entity['registrationDate'], withRegistrationDates)
+
+    if len(registrationDates) > 0:
+        minDateStr = javaDateToStr(min(registrationDates, key=lambda date: date.getTime()))
+        maxDateStr = javaDateToStr(max(registrationDates, key=lambda date: date.getTime()))
+    else:
+        minDateStr = None
+        maxDateStr = None
+
+    metsNS = ns['mets']
+    xlinkNS = ns['xlink']
+    dimNS = ns['dim']
+    ET.register_namespace('mets', metsNS)
+    ET.register_namespace('xlink', xlinkNS)
+    ET.register_namespace('dim', dimNS)
+
+    root = ET.Element(ET.QName(metsNS, 'METS'))
+    root.set('LABEL', 'DSpace Item')
+    root.set('ID', UUID.randomUUID().toString())
+
+    dmdSec = ET.SubElement(root, ET.QName(metsNS, 'dmdSec'))
+    dmdSec.set('GROUPID', 'group_dmd_0')
+    dmdSec.set('ID', 'dmd_1')
+
+    mdWrap = ET.SubElement(dmdSec, ET.QName(metsNS, 'mdWrap'))
+    mdWrap.set('MDTYPE', 'OTHER')
+    mdWrap.set('OTHERMDTYPE', 'DIM')
+
+    xmlData = ET.SubElement(mdWrap, ET.QName(metsNS, 'xmlData'))
+
+    dim = ET.SubElement(xmlData, ET.QName(dimNS, 'dim'))
+    dim.set('dspaceType', 'ITEM')
+
+    titleField = ET.SubElement(dim, ET.QName(dimNS, 'field'))
+    titleField.set('mdschema', 'dc')
+    titleField.set('element', 'title')
+    titleField.text = str(time.time())
+
+    typeField = ET.SubElement(dim, ET.QName(dimNS, 'field'))
+    typeField.set('mdschema', 'dc')
+    typeField.set('element', 'type')
+    typeField.text = submissionType
+
+    userIdField = ET.SubElement(dim, ET.QName(dimNS, 'field'))
+    userIdField.set('mdschema', 'ethz')
+    userIdField.set('element', 'identifier')
+    userIdField.set('qualifier', 'openbis')
+    userIdField.text = permIdsStr
+
+    userInfoField = ET.SubElement(dim, ET.QName(dimNS, 'field'))
+    userInfoField.set('mdschema', 'dc')
+    userInfoField.set('element', 'contributor')
+    userInfoField.set('qualifier', 'author')
+    userInfoField.text = userInformation['lastName'] + ', ' + userInformation['firstName']
+
+    publicationDateField = ET.SubElement(dim, ET.QName(dimNS, 'field'))
+    publicationDateField.set('mdschema', 'dc')
+    publicationDateField.set('element', 'date')
+    publicationDateField.set('qualifier', 'issued')
+    publicationDateField.text = datetime.date.today().strftime('%Y-%m-%d')
+
+    elnLimsURLPattern = '/openbis-test/webapp/eln-lims/?menuUniqueId=null&viewName='
+
+    for entity in entities:
+        type = entity['type']
+        viewName = ''
+        if type == 'SPACE':
+            viewName = 'showSpacePage'
+        if type == 'PROJECT':
+            viewName = 'showProjectPageFromPermId'
+        if type == 'EXPERIMENT':
+            viewName = 'showExperimentPageFromPermId'
+        if type == 'SAMPLE':
+            viewName = 'showViewSamplePageFromPermId'
+        if type == 'DATASET':
+            viewName = 'showViewDataSetPageFromPermId'
+        if type != 'FILE':
+            urlField = ET.SubElement(dim, ET.QName(dimNS, 'field'))
+            urlField.set('mdschema', 'ethz')
+            urlField.set('element', 'identifier')
+            urlField.set('qualifier', 'url')
+            urlField.text = originUrl + elnLimsURLPattern + viewName + '&viewData=' + entity['permId']
+
+    if minDateStr is not None and maxDateStr is not None:
+        creationDateField = ET.SubElement(dim, ET.QName(dimNS, 'field'))
+        creationDateField.set('mdschema', 'dc')
+        creationDateField.set('element', 'date')
+        creationDateField.set('qualifier', 'created')
+        creationDateField.text = minDateStr + '/' + maxDateStr if minDateStr != maxDateStr else minDateStr
+
+    fileSec = ET.SubElement(root, ET.QName(metsNS, 'fileSec'))
+    fileGrp = ET.SubElement(fileSec, ET.QName(metsNS, 'fileGrp'))
+    fileGrp.set('USE', 'CONTENT')
+
+    i = 0
+    for fileMetadatum in fileMetadata:
+        i += 1
+        file = ET.SubElement(fileGrp, ET.QName(metsNS, 'file'))
+        file.set('ID', 'file_' + str(i))
+        fLocat = ET.SubElement(file, ET.QName(metsNS, 'FLocat'))
+        fLocat.set('LOCTYPE', 'URL')
+        fLocat.set('MIMETYPE', fileMetadatum.get('mimeType'))
+        fLocat.set('RETENTIONPERIOD', '10 years')
+        fLocat.set(ET.QName(xlinkNS, 'href'), fileMetadatum.get('fileName'))
+
+    structMap = ET.SubElement(root, ET.QName(metsNS, 'structMap'))
+    structMap.set('LABEL', 'DSpace')
+    structMap.set('TYPE', 'LOGICAL')
+    div1 = ET.SubElement(structMap, ET.QName(metsNS, 'div'))
+    div1.set('DMDID', 'dmd_1')
+    div1.set('TYPE', 'DSpace Item')
+
+    xmlFileName = 'mets.xml'
+    xmlFilePath = exportDirPath + '/' + xmlFileName
+    ET.ElementTree(root).write(xmlFilePath)
+
+    xmlFile = File(xmlFilePath)
+    addToZipFile(' ' + xmlFileName, xmlFile, zipOutputStream)
+    # Space is added to the file name because the method chops out the first character
+
+
+def javaDateToStr(javaDate):
+    dateFormat = SimpleDateFormat('yyyy-MM-dd')
+    return dateFormat.format(javaDate)