diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
index ec7d923128220580157c45c72aecf5d42996f474..896db034e13267dbe25e8eb620be2e53c5fa8fee 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
@@ -1,1296 +1,1493 @@
-define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOpenbis, _, dtos) {
-
-	/*
-	 * These tests should be run against openBIS instance with screening sprint
-	 * server database version
-	 */
-
-	var testProtocol = window.location.protocol;
-	var testHost = window.location.hostname;
-	var testPort = window.location.port;
-	var testUrl = testProtocol + "//" + testHost + ":" + testPort;
-	var testApiUrl = testUrl + "/openbis/openbis/rmi-application-server-v3.json";
-
-	var testUserId = "openbis_test_js";
-	var testUserPassword = "password";
-
-	var Common = function(assert, openbis) {
-		this.assert = assert;
-
-		if (!openbis) {
-			openbis = defaultOpenbis;
-		}
-
-		this.SpaceCreation = dtos.SpaceCreation;
-		this.ProjectCreation = dtos.ProjectCreation;
-		this.ExperimentCreation = dtos.ExperimentCreation;
-		this.SampleCreation = dtos.SampleCreation;
-		this.MaterialCreation = dtos.MaterialCreation;
-		this.AttachmentCreation = dtos.AttachmentCreation;
-		this.VocabularyCreation = dtos.VocabularyCreation;
-		this.VocabularyTermCreation = dtos.VocabularyTermCreation;
-		this.TagCreation = dtos.TagCreation;
-		this.AuthorizationGroupCreation = dtos.AuthorizationGroupCreation;
-		this.RoleAssignmentCreation = dtos.RoleAssignmentCreation;
-		this.PersonCreation = dtos.PersonCreation;
-		this.Role = require('as/dto/roleassignment/Role');
-		this.RoleLevel = require('as/dto/roleassignment/RoleLevel');
-		this.DataType = require('as/dto/property/DataType');
-		this.SemanticAnnotationCreation = dtos.SemanticAnnotationCreation;
-		this.DataSetCreation = dtos.DataSetCreation;
-		this.FullDataSetCreation = dtos.FullDataSetCreation;
-		this.UploadedDataSetCreation = dtos.UploadedDataSetCreation;
-		this.DataSetFileCreation = dtos.DataSetFileCreation;
-		this.LinkedDataCreation = dtos.LinkedDataCreation;
-		this.ContentCopyCreation = dtos.ContentCopyCreation;
-		this.ExternalDmsCreation = dtos.ExternalDmsCreation;
-		this.ExternalDmsAddressType = require('as/dto/externaldms/ExternalDmsAddressType');
-		this.SpaceUpdate = dtos.SpaceUpdate;
-		this.ProjectUpdate = dtos.ProjectUpdate;
-		this.ExperimentTypeUpdate = dtos.ExperimentTypeUpdate;
-		this.ExperimentUpdate = dtos.ExperimentUpdate;
-		this.SampleTypeUpdate = dtos.SampleTypeUpdate;
-		this.SampleUpdate = dtos.SampleUpdate;
-		this.DataSetTypeUpdate = dtos.DataSetTypeUpdate;
-		this.DataSetUpdate = dtos.DataSetUpdate;
-		this.PhysicalDataUpdate = dtos.PhysicalDataUpdate;
-		this.LinkedDataUpdate = dtos.LinkedDataUpdate;
-		this.SemanticAnnotationUpdate = dtos.SemanticAnnotationUpdate;
-		this.ContentCopyListUpdateValue = dtos.ContentCopyListUpdateValue;
-		this.DataStorePermId = dtos.DataStorePermId;
-		this.MaterialTypeUpdate = dtos.MaterialTypeUpdate;
-		this.MaterialUpdate = dtos.MaterialUpdate;
-		this.VocabularyUpdate = dtos.VocabularyUpdate;
-		this.VocabularyTermUpdate = dtos.VocabularyTermUpdate;
-		this.ExternalDmsUpdate = dtos.ExternalDmsUpdate;
-		this.TagUpdate = dtos.TagUpdate;
-		this.AuthorizationGroupUpdate = dtos.AuthorizationGroupUpdate;
-		this.PersonUpdate = dtos.PersonUpdate;
-		this.SpaceDeletionOptions = dtos.SpaceDeletionOptions;
-		this.ProjectDeletionOptions = dtos.ProjectDeletionOptions;
-		this.ExperimentDeletionOptions = dtos.ExperimentDeletionOptions;
-		this.SampleDeletionOptions = dtos.SampleDeletionOptions;
-		this.DataSetDeletionOptions = dtos.DataSetDeletionOptions;
-		this.MaterialDeletionOptions = dtos.MaterialDeletionOptions;
-		this.VocabularyTermDeletionOptions = dtos.VocabularyTermDeletionOptions;
-		this.EntityTypeDeletionOptions = dtos.EntityTypeDeletionOptions;
-		this.ExternalDmsDeletionOptions = dtos.ExternalDmsDeletionOptions;
-		this.TagDeletionOptions = dtos.TagDeletionOptions;
-		this.AuthorizationGroupDeletionOptions = dtos.AuthorizationGroupDeletionOptions;
-		this.RoleAssignmentDeletionOptions = dtos.RoleAssignmentDeletionOptions;
-		this.SemanticAnnotationDeletionOptions = dtos.SemanticAnnotationDeletionOptions;
-		this.PersonPermId = dtos.PersonPermId;
-		this.Me = dtos.Me;
-		this.EntityTypePermId = dtos.EntityTypePermId;
-		this.SpacePermId = dtos.SpacePermId;
-		this.ProjectPermId = dtos.ProjectPermId;
-		this.ProjectIdentifier = dtos.ProjectIdentifier;
-		this.ExperimentPermId = dtos.ExperimentPermId;
-		this.ExperimentIdentifier = dtos.ExperimentIdentifier;
-		this.SamplePermId = dtos.SamplePermId;
-		this.SampleIdentifier = dtos.SampleIdentifier;
-		this.DataSetPermId = dtos.DataSetPermId;
-		this.FileFormatTypePermId = dtos.FileFormatTypePermId;
-		this.MaterialPermId = dtos.MaterialPermId;
-		this.ContentCopyPermId = dtos.ContentCopyPermId;
-		this.ExternalDmsPermId = dtos.ExternalDmsPermId;
-		this.VocabularyPermId = dtos.VocabularyPermId;
-		this.VocabularyTermPermId = dtos.VocabularyTermPermId;
-		this.AuthorizationGroupPermId = dtos.AuthorizationGroupPermId;
-		this.RoleAssignmentTechId = dtos.RoleAssignmentTechId;
-		this.TagPermId = dtos.TagPermId;
-		this.TagCode = dtos.TagCode;
-		this.SemanticAnnotationsPermId = dtos.SemanticAnnotationsPermId;
-		this.SpaceSearchCriteria = dtos.SpaceSearchCriteria;
-		this.ProjectSearchCriteria = dtos.ProjectSearchCriteria;
-		this.ExperimentSearchCriteria = dtos.ExperimentSearchCriteria;
-		this.ExperimentTypeSearchCriteria = dtos.ExperimentTypeSearchCriteria;
-		this.SampleSearchCriteria = dtos.SampleSearchCriteria;
-		this.SampleTypeSearchCriteria = dtos.SampleTypeSearchCriteria;
-		this.DataSetSearchCriteria = dtos.DataSetSearchCriteria;
-		this.DataSetTypeSearchCriteria = dtos.DataSetTypeSearchCriteria;
-		this.MaterialSearchCriteria = dtos.MaterialSearchCriteria;
-		this.MaterialTypeSearchCriteria = dtos.MaterialTypeSearchCriteria;
-		this.ExternalDmsSearchCriteria = dtos.ExternalDmsSearchCriteria;
-		this.VocabularySearchCriteria = dtos.VocabularySearchCriteria;
-		this.VocabularyTermSearchCriteria = dtos.VocabularyTermSearchCriteria;
-		this.DataSetFileSearchCriteria = dtos.DataSetFileSearchCriteria;
-		this.TagSearchCriteria = dtos.TagSearchCriteria;
-		this.AuthorizationGroupSearchCriteria = dtos.AuthorizationGroupSearchCriteria;
-		this.RoleAssignmentSearchCriteria = dtos.RoleAssignmentSearchCriteria;
-		this.PersonSearchCriteria = dtos.PersonSearchCriteria;
-		this.DataStoreSearchCriteria = dtos.DataStoreSearchCriteria;
-		this.PropertyTypeSearchCriteria = dtos.PropertyTypeSearchCriteria;
-		this.PropertyAssignmentSearchCriteria = dtos.PropertyAssignmentSearchCriteria;
-		this.SemanticAnnotationSearchCriteria = dtos.SemanticAnnotationSearchCriteria;
-		this.SpaceFetchOptions = dtos.SpaceFetchOptions;
-		this.ProjectFetchOptions = dtos.ProjectFetchOptions;
-		this.ExperimentFetchOptions = dtos.ExperimentFetchOptions;
-		this.ExperimentTypeFetchOptions = dtos.ExperimentTypeFetchOptions;
-		this.SampleFetchOptions = dtos.SampleFetchOptions;
-		this.SampleTypeFetchOptions = dtos.SampleTypeFetchOptions;
-		this.DataSetFetchOptions = dtos.DataSetFetchOptions;
-		this.DataSetTypeFetchOptions = dtos.DataSetTypeFetchOptions;
-		this.MaterialFetchOptions = dtos.MaterialFetchOptions;
-		this.MaterialTypeFetchOptions = dtos.MaterialTypeFetchOptions;
-		this.ExternalDmsFetchOptions = dtos.ExternalDmsFetchOptions;
-		this.VocabularyFetchOptions = dtos.VocabularyFetchOptions;
-		this.VocabularyTermFetchOptions = dtos.VocabularyTermFetchOptions;
-		this.TagFetchOptions = dtos.TagFetchOptions;
-		this.AuthorizationGroupFetchOptions = dtos.AuthorizationGroupFetchOptions;
-		this.RoleAssignmentFetchOptions = dtos.RoleAssignmentFetchOptions;
-		this.PersonFetchOptions = dtos.PersonFetchOptions;
-		this.PropertyTypeFetchOptions = dtos.PropertyTypeFetchOptions;
-		this.PropertyAssignmentFetchOptions = dtos.PropertyAssignmentFetchOptions;
-		this.SemanticAnnotationFetchOptions = dtos.SemanticAnnotationFetchOptions;
-		this.DeletionFetchOptions = dtos.DeletionFetchOptions;
-		this.DeletionSearchCriteria = dtos.DeletionSearchCriteria;
-		this.CustomASServiceSearchCriteria = dtos.CustomASServiceSearchCriteria;
-		this.CustomASServiceFetchOptions = dtos.CustomASServiceFetchOptions;
-		this.CustomASServiceCode = dtos.CustomASServiceCode;
-		this.CustomASServiceExecutionOptions = dtos.CustomASServiceExecutionOptions;
-		this.GlobalSearchCriteria = dtos.GlobalSearchCriteria;
-		this.GlobalSearchObjectFetchOptions = dtos.GlobalSearchObjectFetchOptions;
-		this.ObjectKindModificationSearchCriteria = dtos.ObjectKindModificationSearchCriteria;
-		this.ObjectKindModificationFetchOptions = dtos.ObjectKindModificationFetchOptions;
-		this.DataSetArchiveOptions = dtos.DataSetArchiveOptions;
-		this.DataSetUnarchiveOptions = dtos.DataSetUnarchiveOptions;
-		this.PropertyAssignmentCreation = dtos.PropertyAssignmentCreation;
-		this.PropertyTypePermId = dtos.PropertyTypePermId;
-		this.PropertyAssignmentPermId = dtos.PropertyAssignmentPermId;
-		this.PluginPermId = dtos.PluginPermId;
-		this.ExperimentTypeCreation = dtos.ExperimentTypeCreation;
-		this.SampleTypeCreation = dtos.SampleTypeCreation;
-		this.DataSetTypeCreation = dtos.DataSetTypeCreation;
-		this.MaterialTypeCreation = dtos.MaterialTypeCreation;
-		this.PropertyTypeCreation = dtos.PropertyTypeCreation;
-		this.PropertyTypeUpdate = dtos.PropertyTypeUpdate;
-		this.WebAppSettings = dtos.WebAppSettings;
-
-		// operations
-
-		this.GetSessionInformationOperation = dtos.GetSessionInformationOperation;
-		this.GetSpacesOperation = dtos.GetSpacesOperation;
-		this.GetProjectsOperation = dtos.GetProjectsOperation;
-		this.GetExperimentsOperation = dtos.GetExperimentsOperation;
-		this.GetSamplesOperation = dtos.GetSamplesOperation;
-		this.GetDataSetsOperation = dtos.GetDataSetsOperation;
-		this.GetMaterialsOperation = dtos.GetMaterialsOperation;
-		this.GetPropertyTypesOperation = dtos.GetPropertyTypesOperation;
-		this.GetVocabulariesOperation = dtos.GetVocabulariesOperation;
-		this.GetVocabularyTermsOperation = dtos.GetVocabularyTermsOperation;
-		this.GetTagsOperation = dtos.GetTagsOperation;
-		this.GetAuthorizationGroupsOperation = dtos.GetAuthorizationGroupsOperation;
-		this.GetRoleAssignmentsOperation = dtos.GetRoleAssignmentsOperation;
-		this.GetPersonsOperation = dtos.GetPersonsOperation;
-		this.GetExternalDmsOperation = dtos.GetExternalDmsOperation;
-		this.GetSemanticAnnotationsOperation = dtos.GetSemanticAnnotationsOperation;
-
-		this.CreateSpacesOperation = dtos.CreateSpacesOperation;
-		this.CreateProjectsOperation = dtos.CreateProjectsOperation;
-		this.CreateExperimentsOperation = dtos.CreateExperimentsOperation;
-		this.CreateExperimentTypesOperation = dtos.CreateExperimentTypesOperation;
-		this.CreateSamplesOperation = dtos.CreateSamplesOperation;
-		this.CreateSampleTypesOperation = dtos.CreateSampleTypesOperation;
-		this.CreateDataSetsOperation = dtos.CreateDataSetsOperation;
-		this.CreateDataSetTypesOperation = dtos.CreateDataSetTypesOperation;
-		this.CreateMaterialsOperation = dtos.CreateMaterialsOperation;
-		this.CreateMaterialTypesOperation = dtos.CreateMaterialTypesOperation;
-		this.CreatePropertyTypesOperation = dtos.CreatePropertyTypesOperation;
-		this.CreateVocabulariesOperation = dtos.CreateVocabulariesOperation;
-		this.CreateVocabularyTermsOperation = dtos.CreateVocabularyTermsOperation;
-		this.CreateTagsOperation = dtos.CreateTagsOperation;
-		this.CreateAuthorizationGroupsOperation = dtos.CreateAuthorizationGroupsOperation;
-		this.CreateRoleAssignmentsOperation = dtos.CreateRoleAssignmentsOperation;
-		this.CreatePersonsOperation = dtos.CreatePersonsOperation;
-		this.CreateSemanticAnnotationsOperation = dtos.CreateSemanticAnnotationsOperation;
-		this.CreateExternalDmsOperation = dtos.CreateExternalDmsOperation;
-
-		this.UpdateSpacesOperation = dtos.UpdateSpacesOperation;
-		this.UpdateProjectsOperation = dtos.UpdateProjectsOperation;
-		this.UpdateExperimentsOperation = dtos.UpdateExperimentsOperation;
-		this.UpdateExperimentTypesOperation = dtos.UpdateExperimentTypesOperation;
-		this.UpdateSamplesOperation = dtos.UpdateSamplesOperation;
-		this.UpdateSampleTypesOperation = dtos.UpdateSampleTypesOperation;
-		this.UpdateDataSetsOperation = dtos.UpdateDataSetsOperation;
-		this.UpdateDataSetTypesOperation = dtos.UpdateDataSetTypesOperation;
-		this.UpdateMaterialsOperation = dtos.UpdateMaterialsOperation;
-		this.UpdateMaterialTypesOperation = dtos.UpdateMaterialTypesOperation;
-		this.UpdatePropertyTypesOperation = dtos.UpdatePropertyTypesOperation;
-		this.UpdateVocabulariesOperation = dtos.UpdateVocabulariesOperation;
-		this.UpdateVocabularyTermsOperation = dtos.UpdateVocabularyTermsOperation;
-		this.UpdateExternalDmsOperation = dtos.UpdateExternalDmsOperation;
-		this.UpdateTagsOperation = dtos.UpdateTagsOperation;
-		this.UpdateAuthorizationGroupsOperation = dtos.UpdateAuthorizationGroupsOperation;
-		this.UpdatePersonsOperation = dtos.UpdatePersonsOperation;
-		this.UpdateOperationExecutionsOperation = dtos.UpdateOperationExecutionsOperation;
-		this.UpdateSemanticAnnotationsOperation = dtos.UpdateSemanticAnnotationsOperation;
-
-		this.GetSpacesOperation = dtos.GetSpacesOperation;
-		this.GetProjectsOperation = dtos.GetProjectsOperation;
-		this.GetExperimentsOperation = dtos.GetExperimentsOperation;
-		this.GetSamplesOperation = dtos.GetSamplesOperation;
-		this.GetDataSetsOperation = dtos.GetDataSetsOperation;
-		this.GetMaterialsOperation = dtos.GetMaterialsOperation;
-		this.GetVocabularyTermsOperation = dtos.GetVocabularyTermsOperation;
-		this.GetTagsOperation = dtos.GetTagsOperation;
-		this.GetSemanticAnnotationsOperation = dtos.GetSemanticAnnotationsOperation;
-		this.GetOperationExecutionsOperation = dtos.GetOperationExecutionsOperation;
-
-		this.SearchSpacesOperation = dtos.SearchSpacesOperation;
-		this.SearchProjectsOperation = dtos.SearchProjectsOperation;
-		this.SearchExperimentsOperation = dtos.SearchExperimentsOperation;
-		this.SearchExperimentTypesOperation = dtos.SearchExperimentTypesOperation;
-		this.SearchSamplesOperation = dtos.SearchSamplesOperation;
-		this.SearchSampleTypesOperation = dtos.SearchSampleTypesOperation;
-		this.SearchDataSetsOperation = dtos.SearchDataSetsOperation;
-		this.SearchDataSetTypesOperation = dtos.SearchDataSetTypesOperation;
-		this.SearchMaterialsOperation = dtos.SearchMaterialsOperation;
-		this.SearchMaterialTypesOperation = dtos.SearchMaterialTypesOperation;
-		this.SearchVocabulariesOperation = dtos.SearchVocabulariesOperation;
-		this.SearchVocabularyTermsOperation = dtos.SearchVocabularyTermsOperation;
-		this.SearchExternalDmsOperation = dtos.SearchExternalDmsOperation;
-		this.SearchTagsOperation = dtos.SearchTagsOperation;
-		this.SearchAuthorizationGroupsOperation = dtos.SearchAuthorizationGroupsOperation;
-		this.SearchRoleAssignmentsOperation = dtos.SearchRoleAssignmentsOperation;
-		this.SearchPersonsOperation = dtos.SearchPersonsOperation;
-		this.SearchCustomASServicesOperation = dtos.SearchCustomASServicesOperation;
-		this.SearchObjectKindModificationsOperation = dtos.SearchObjectKindModificationsOperation;
-		this.SearchGloballyOperation = dtos.SearchGloballyOperation;
-		this.SearchOperationExecutionsOperation = dtos.SearchOperationExecutionsOperation;
-		this.SearchDeletionsOperation = dtos.SearchDeletionsOperation;
-		this.SearchDataStoresOperation = dtos.SearchDataStoresOperation;
-		this.SearchPropertyTypesOperation = dtos.SearchPropertyTypesOperation;
-		this.SearchPropertyAssignmentsOperation = dtos.SearchPropertyAssignmentsOperation;
-		this.SearchSemanticAnnotationsOperation = dtos.SearchSemanticAnnotationsOperation;
-
-		this.DeleteSpacesOperation = dtos.DeleteSpacesOperation;
-		this.DeleteProjectsOperation = dtos.DeleteProjectsOperation;
-		this.DeleteExperimentsOperation = dtos.DeleteExperimentsOperation;
-		this.DeleteSamplesOperation = dtos.DeleteSamplesOperation;
-		this.DeleteDataSetsOperation = dtos.DeleteDataSetsOperation;
-		this.DeleteMaterialsOperation = dtos.DeleteMaterialsOperation;
-		this.DeleteExternalDmsOperation = dtos.DeleteExternalDmsOperation;
-		this.DeletePropertyTypesOperation = dtos.DeletePropertyTypesOperation;
-		this.DeleteVocabulariesOperation = dtos.DeleteVocabulariesOperation;
-		this.DeleteVocabularyTermsOperation = dtos.DeleteVocabularyTermsOperation;
-		this.DeleteEntityTypesOperation = dtos.DeleteEntityTypesOperation;
-		this.DeleteTagsOperation = dtos.DeleteTagsOperation;
-		this.DeleteAuthorizationGroupsOperation = dtos.DeleteAuthorizationGroupsOperation;
-		this.DeleteRoleAssignmentsOperation = dtos.DeleteRoleAssignmentsOperation;
-		this.DeleteOperationExecutionsOperation = dtos.DeleteOperationExecutionsOperation;
-		this.DeleteSemanticAnnotationsOperation = dtos.DeleteSemanticAnnotationsOperation;
-
-		this.RevertDeletionsOperation = dtos.RevertDeletionsOperation;
-		this.ConfirmDeletionsOperation = dtos.ConfirmDeletionsOperation;
-		this.ExecuteCustomASServiceOperation = dtos.ExecuteCustomASServiceOperation;
-		this.ArchiveDataSetsOperation = dtos.ArchiveDataSetsOperation;
-		this.UnarchiveDataSetsOperation = dtos.UnarchiveDataSetsOperation;
-
-		this.SynchronousOperationExecutionOptions = dtos.SynchronousOperationExecutionOptions;
-		this.AsynchronousOperationExecutionOptions = dtos.AsynchronousOperationExecutionOptions;
-		this.OperationExecutionDeletionOptions = dtos.OperationExecutionDeletionOptions;
-		this.OperationExecutionFetchOptions = dtos.OperationExecutionFetchOptions;
-		this.OperationExecutionPermId = dtos.OperationExecutionPermId;
-		this.OperationExecutionSearchCriteria = dtos.OperationExecutionSearchCriteria;
-		this.OperationExecutionUpdate = dtos.OperationExecutionUpdate;
-
-		this.getDtos = function() {
-			return dtos;
-		}
-
-		this.getId = function(entity) {
-			if (typeof entity["getPermId"] === 'function') {
-				return entity.getPermId();
-			}
-			if (typeof entity["getId"] === 'function') {
-				return entity.getId();
-			}
-			this.fail("Neither 'getPermId()' nor 'getId()' are functions of entity " + entity);
-		}
-
-		this.generateId = function(base) {
-			var date = new Date();
-			var parts = [ "V3", base, date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), Math.round(1000000 * Math.random()) ];
-			return parts.join("_");
-		},
-
-		this.createSpace = function(facade) {
-			var c = this;
-			var creation = new dtos.SpaceCreation();
-			creation.setCode(c.generateId("SPACE"));
-			return facade.createSpaces([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createProject = function(facade) {
-			var c = this;
-			return c.createSpace(facade).then(function(spacePermId) {
-				var creation = new dtos.ProjectCreation();
-				creation.setCode(c.generateId("PROJECT"));
-				creation.setSpaceId(spacePermId);
-				return facade.createProjects([ creation ]).then(function(permIds) {
-					return permIds[0];
-				});
-			});
-		}.bind(this);
-
-		this.createExperiment = function(facade) {
-			var c = this;
-			return c.createProject(facade).then(function(projectPermId) {
-				var creation = new dtos.ExperimentCreation();
-				creation.setCode(c.generateId("EXPERIMENT"));
-				creation.setTypeId(new dtos.EntityTypePermId("UNKNOWN"));
-				creation.setProjectId(projectPermId);
-				return facade.createExperiments([ creation ]).then(function(permIds) {
-					return permIds[0];
-				});
-			});
-		}.bind(this);
-
-		this.createSample = function(facade) {
-			var c = this;
-			return c.createSpace(facade).then(function(spacePermId) {
-				var creation = new dtos.SampleCreation();
-				creation.setCode(c.generateId("SAMPLE"));
-				creation.setTypeId(new dtos.EntityTypePermId("UNKNOWN"));
-				creation.setSpaceId(spacePermId);
-				return facade.createSamples([ creation ]).then(function(permIds) {
-					return permIds[0];
-				});
-			});
-		}.bind(this);
-
-		this.createLinkDataSet = function(facade, path, gitCommitHash, gitRepositoryId) {
-			var c = this;
-			return c.createExperiment(facade).then(function(experimentPermId) {
-				return c.createFileExternalDms(facade).then(function(emdsPermId) {
-					var dataSet = new dtos.DataSetCreation();
-					dataSet.setAutoGeneratedCode(true);
-					dataSet.setTypeId(new c.EntityTypePermId("LINK_TYPE"));
-					dataSet.setExperimentId(experimentPermId);
-					dataSet.setDataStoreId(new c.DataStorePermId("DSS1"));
-					var linkedData = new c.LinkedDataCreation();
-					var cc = new c.ContentCopyCreation();
-					cc.setExternalDmsId(emdsPermId);
-					cc.setPath(path);
-					cc.setGitCommitHash(gitCommitHash);
-					cc.setGitRepositoryId(gitRepositoryId);
-					linkedData.setContentCopies([ cc ]);
-					dataSet.setLinkedData(linkedData);
-					return facade.createDataSets([ dataSet ]).then(function(permIds) {
-						return permIds[0];
-					});
-				});
-			});
-		}.bind(this);
-
-		this.createDataSet = function(facade) {
-			var c = this;
-			return this.getResponseFromJSTestAggregationService(facade, {}, function(response) {
-				return new dtos.DataSetPermId(response.result.rows[0][0].value);
-			});
-		}.bind(this);
-
-		this.waitUntilIndexed = function(facade, dataSetCode, timeout) {
-			var c = this;
-			var dfd = $.Deferred();
-			var start = new Date().getTime();
-
-			var searchAndWait = function() {
-				var criteria = new c.DataSetSearchCriteria();
-				criteria.withPermId().thatEquals(dataSetCode);
-
-				facade.searchDataSets(criteria, c.createDataSetFetchOptions()).then(function(result) {
-					if (result.getTotalCount() == 0) {
-						var now = new Date().getTime();
-						if (now - start > timeout) {
-							c.fail("Data set " + dataSetCode + " not indexed after " + timeout + " msec.");
-							dfd.reject();
-						} else {
-							setTimeout(searchAndWait, 1000);
-						}
-					} else {
-						dfd.resolve();
-					}
-				});
-			};
-			searchAndWait();
-			return dfd.promise();
-		}.bind(this);
-
-		this.getResponseFromJSTestAggregationService = function(facade, params, callback) {
-			var c = this;
-			return $.ajax({
-				"url" : "http://" + testHost + ":20001/datastore_server/rmi-dss-api-v1.json",
-				"type" : "POST",
-				"processData" : false,
-				"dataType" : "json",
-				"data" : JSON.stringify({
-					"method" : "createReportFromAggregationService",
-					"params" : [ facade._private.sessionToken, "js-test", params ],
-					"id" : "1",
-					"jsonrpc" : "2.0"
-				})
-			}).then(callback);
-		}.bind(this);
-
-		this.createMaterial = function(facade) {
-			var c = this;
-			var creation = new dtos.MaterialCreation();
-			creation.setCode(c.generateId("MATERIAL"));
-			creation.setTypeId(new dtos.EntityTypePermId("COMPOUND"));
-			return facade.createMaterials([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createPropertyType = function(facade) {
-			var c = this;
-			var creation = new dtos.PropertyTypeCreation();
-			creation.setCode(c.generateId("PROPERTY_TYPE"));
-			creation.setLabel("Testing");
-			creation.setDescription("testing");
-			creation.setDataType(c.DataType.VARCHAR);
-			return facade.createPropertyTypes([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-		
-		this.createVocabulary = function(facade) {
-			var c = this;
-			var creation = new dtos.VocabularyCreation();
-			creation.setCode(c.generateId("VOCABULARY"));
-			return facade.createVocabularies([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-		
-		this.createVocabularyTerm = function(facade) {
-			var c = this;
-			var creation = new dtos.VocabularyTermCreation();
-			creation.setCode(c.generateId("VOCABULARY_TERM"));
-			creation.setVocabularyId(new c.VocabularyPermId("TEST-VOCABULARY"));
-			return facade.createVocabularyTerms([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createSampleType = function(facade) {
-			var c = this;
-			var creation = new dtos.SampleTypeCreation();
-			creation.setCode(c.generateId("SAMPLE_TYPE"));
-			return facade.createSampleTypes([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createExternalDms = function(facade) {
-			var c = this;
-			var creation = new dtos.ExternalDmsCreation();
-			creation.setCode(c.generateId("EMDS"));
-			creation.setAddressType(c.ExternalDmsAddressType.URL);
-			creation.setAddress("https://my-server:8443/my-app/q=${term}")
-			return facade.createExternalDms([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createFileExternalDms = function(facade) {
-			var c = this;
-			var creation = new c.ExternalDmsCreation();
-			creation.setCode(c.generateId("EMDS"));
-			creation.setLabel("Test File EDMS");
-			creation.setAddressType(c.ExternalDmsAddressType.FILE_SYSTEM);
-			creation.setAddress("host:my/path")
-			return facade.createExternalDms([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createTag = function(facade) {
-			var c = this;
-			var creation = new dtos.TagCreation();
-			creation.setCode(c.generateId("TAG"));
-			return facade.createTags([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createAuthorizationGroup = function(facade) {
-			var c = this;
-			var creation = new dtos.AuthorizationGroupCreation();
-			creation.setCode(c.generateId("AUTHORIZATION_GROUP"));
-			creation.setUserIds([ new c.PersonPermId("power_user") ]);
-			return facade.createAuthorizationGroups([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createRoleAssignment = function(facade, isUser) {
-			var c = this;
-			return c.createSpace(facade).then(function(spaceId) {
-				var creation = new dtos.RoleAssignmentCreation();
-				creation.setRole(c.Role.ADMIN);
-				if (isUser) {
-					creation.setUserId(new c.PersonPermId("power_user"));
-				} else {
-					creation.setAuthorizationGroupId(new c.AuthorizationGroupPermId("TEST-GROUP"));
+define(
+		[ 'jquery', 'openbis', 'underscore', 'test/dtos' ],
+		function($, defaultOpenbis, _, dtos) {
+
+			/*
+			 * These tests should be run against openBIS instance with screening
+			 * sprint server database version
+			 */
+
+			var testProtocol = window.location.protocol;
+			var testHost = window.location.hostname;
+			var testPort = window.location.port;
+			var testUrl = testProtocol + "//" + testHost + ":" + testPort;
+			var testApiUrl = testUrl
+					+ "/openbis/openbis/rmi-application-server-v3.json";
+
+			var testUserId = "openbis_test_js";
+			var testUserPassword = "password";
+
+			var Common = function(assert, openbis) {
+				this.assert = assert;
+
+				if (!openbis) {
+					openbis = defaultOpenbis;
 				}
-				creation.setSpaceId(spaceId);
-				return facade.createRoleAssignments([ creation ]).then(function(permIds) {
-					return permIds[0];
-				});
-			});
-		}.bind(this);
-
-		this.createPerson = function(facade) {
-			var c = this;
-			var creation = new dtos.PersonCreation();
-			creation.setUserId(c.generateId("USER"));
-			return facade.createPersons([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createSemanticAnnotation = function(facade) {
-			var c = this;
-			var creation = new dtos.SemanticAnnotationCreation();
-			creation.setEntityTypeId(new dtos.EntityTypePermId("UNKNOWN", "SAMPLE"));
-			creation.setPredicateOntologyId(c.generateId("jsPredicateOntologyId"));
-			creation.setPredicateOntologyVersion(c.generateId("jsPredicateOntologyVersion"));
-			creation.setPredicateAccessionId(c.generateId("jsPredicateAccessionId"));
-			creation.setDescriptorOntologyId(c.generateId("jsDescriptorOntologyId"));
-			creation.setDescriptorOntologyVersion(c.generateId("jsDescriptorOntologyVersion"));
-			creation.setDescriptorAccessionId(c.generateId("jsDescriptorAccessionId"));
-			return facade.createSemanticAnnotations([ creation ]).then(function(permIds) {
-				return permIds[0];
-			});
-		}.bind(this);
-
-		this.createOperationExecution = function(facade) {
-			var c = this;
-			var operation = new dtos.GetSpacesOperation([ new dtos.SpacePermId("/TEST") ], new dtos.SpaceFetchOptions());
-			var options = new dtos.SynchronousOperationExecutionOptions();
-			options.setExecutionId(new dtos.OperationExecutionPermId());
-			return facade.executeOperations([ operation ], options).then(function() {
-				return options.getExecutionId();
-			});
-		}.bind(this);
-
-		this.findSpace = function(facade, id) {
-			var c = this;
-			return facade.getSpaces([ id ], c.createSpaceFetchOptions()).then(function(spaces) {
-				return spaces[id];
-			});
-		}.bind(this);
-
-		this.findProject = function(facade, id) {
-			var c = this;
-			return facade.getProjects([ id ], c.createProjectFetchOptions()).then(function(projects) {
-				return projects[id];
-			});
-		}.bind(this);
-
-		this.findExperiment = function(facade, id) {
-			var c = this;
-			return facade.getExperiments([ id ], c.createExperimentFetchOptions()).then(function(experiments) {
-				return experiments[id];
-			});
-		}.bind(this);
-
-		this.findExperimentType = function(facade, id) {
-			var c = this;
-			var criteria = new c.ExperimentTypeSearchCriteria();
-			criteria.withId().thatEquals(id);
-			return facade.searchExperimentTypes(criteria, c.createExperimentTypeFetchOptions()).then(function(results) {
-				return results.getObjects()[0];
-			});
-		}.bind(this);
-
-		this.findSample = function(facade, id) {
-			var c = this;
-			return facade.getSamples([ id ], c.createSampleFetchOptions()).then(function(samples) {
-				return samples[id];
-			});
-		}.bind(this);
-
-		this.findSampleType = function(facade, id) {
-			var c = this;
-			var criteria = new c.SampleTypeSearchCriteria();
-			criteria.withId().thatEquals(id);
-			return facade.searchSampleTypes(criteria, c.createSampleTypeFetchOptions()).then(function(results) {
-				return results.getObjects()[0];
-			});
-		}.bind(this);
-
-		this.findDataSet = function(facade, id) {
-			var c = this;
-			return facade.getDataSets([ id ], c.createDataSetFetchOptions()).then(function(dataSets) {
-				return dataSets[id];
-			});
-		}.bind(this);
-
-		this.findDataSetType = function(facade, id) {
-			var c = this;
-			var criteria = new c.DataSetTypeSearchCriteria();
-			criteria.withId().thatEquals(id);
-			return facade.searchDataSetTypes(criteria, c.createDataSetTypeFetchOptions()).then(function(results) {
-				return results.getObjects()[0];
-			});
-		}.bind(this);
-
-		this.findMaterial = function(facade, id) {
-			var c = this;
-			return facade.getMaterials([ id ], c.createMaterialFetchOptions()).then(function(materials) {
-				return materials[id];
-			});
-		}.bind(this);
-
-		this.findMaterialType = function(facade, id) {
-			var c = this;
-			var criteria = new c.MaterialTypeSearchCriteria();
-			criteria.withId().thatEquals(id);
-			return facade.searchMaterialTypes(criteria, c.createMaterialTypeFetchOptions()).then(function(results) {
-				return results.getObjects()[0];
-			});
-		}.bind(this);
-
-		this.findPropertyType = function(facade, id) {
-			var c = this;
-			var criteria = new c.PropertyTypeSearchCriteria();
-			criteria.withId().thatEquals(id);
-			return facade.searchPropertyTypes(criteria, c.createPropertyTypeFetchOptions()).then(function(results) {
-				return results.getObjects()[0];
-			});
-		}.bind(this);
-		
-		this.findVocabulary = function(facade, id) {
-			var c = this;
-			return facade.getVocabularies([ id ], c.createVocabularyFetchOptions()).then(function(vocabularies) {
-				return vocabularies[id];
-			});
-		}.bind(this);
-		
-		this.findVocabularyTerm = function(facade, id) {
-			var c = this;
-			return facade.getVocabularyTerms([ id ], c.createVocabularyTermFetchOptions()).then(function(terms) {
-				return terms[id];
-			});
-		}.bind(this);
-
-		this.findTag = function(facade, id) {
-			var c = this;
-			return facade.getTags([ id ], c.createTagFetchOptions()).then(function(tags) {
-				return tags[id];
-			});
-		}.bind(this);
-
-		this.findAuthorizationGroup = function(facade, id) {
-			var c = this;
-			return facade.getAuthorizationGroups([ id ], c.createAuthorizationGroupFetchOptions()).then(function(groups) {
-				return groups[id];
-			});
-		}.bind(this);
-
-		this.findRoleAssignment = function(facade, id) {
-			var c = this;
-			return facade.getRoleAssignments([ id ], c.createRoleAssignmentFetchOptions()).then(function(assignments) {
-				return assignments[id];
-			});
-		}.bind(this);
-
-		this.findPerson = function(facade, id) {
-			var c = this;
-			return facade.getPersons([ id ], c.createPersonFetchOptions()).then(function(persons) {
-				return persons[id];
-			});
-		}.bind(this);
-
-		this.findSemanticAnnotation = function(facade, id) {
-			var c = this;
-			return facade.getSemanticAnnotations([ id ], c.createSemanticAnnotationFetchOptions()).then(function(annotations) {
-				return annotations[id];
-			});
-		}.bind(this);
-
-		this.findExternalDms = function(facade, id) {
-			var c = this;
-			return facade.getExternalDataManagementSystems([ id ], c.createExternalDmsFetchOptions()).then(function(edms) {
-				return edms[id];
-			});
-		}.bind(this);
-
-		this.findOperationExecution = function(facade, id) {
-			var c = this;
-			return facade.getOperationExecutions([ id ], c.createOperationExecutionFetchOptions()).then(function(executions) {
-				return executions[id];
-			});
-		}.bind(this);
-
-		this.deleteSpace = function(facade, id) {
-			var c = this;
-			var options = new dtos.SpaceDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteSpaces([ id ], options);
-		}.bind(this);
-
-		this.deleteProject = function(facade, id) {
-			var c = this;
-			var options = new dtos.ProjectDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteProjects([ id ], options);
-		}.bind(this);
-
-		this.deleteExperiment = function(facade, id) {
-			var c = this;
-			var options = new dtos.ExperimentDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteExperiments([ id ], options);
-		}.bind(this);
-
-		this.deleteSample = function(facade, id) {
-			var c = this;
-			var options = new dtos.SampleDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteSamples([ id ], options);
-		}.bind(this);
-
-		this.deleteDataSet = function(facade, id) {
-			var c = this;
-			var options = new dtos.DataSetDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteDataSets([ id ], options);
-		}.bind(this);
-
-		this.deleteMaterial = function(facade, id) {
-			var c = this;
-			var options = new dtos.MaterialDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteMaterials([ id ], options);
-		}.bind(this);
-
-		this.deleteExternalDms = function(facade, id) {
-			var c = this;
-			var options = new dtos.ExternalDmsDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteExternalDataManagementSystems([ id ], options);
-		}.bind(this);
-
-		this.deleteEntityType = function(facade, id) {
-			var c = this;
-			var options = new dtos.EntityTypeDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteEntityTypes([ id ], options);
-		}.bind(this);
-
-		this.deletePropertyType = function(facade, id) {
-			var c = this;
-			var options = new dtos.PropertyTypeDeletionOptions();
-			options.setReason("test reason");
-			return facade.deletePropertyTypes([ id ], options);
-		}.bind(this);
-		
-		this.deleteVocabulary = function(facade, id) {
-			var c = this;
-			var options = new dtos.VocabularyDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteVocabularies([ id ], options);
-		}.bind(this);
-		
-		this.deleteVocabularyTerm = function(facade, id) {
-			var c = this;
-			var options = new dtos.VocabularyTermDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteVocabularyTerms([ id ], options);
-		}.bind(this);
-
-		this.replaceVocabularyTerm = function(facade, id) {
-			var c = this;
-			var options = new dtos.VocabularyTermDeletionOptions();
-			options.setReason("test reason");
-			options.replace(id, new c.VocabularyTermPermId("TEST-TERM-1", "TEST-VOCABULARY"));
-			return facade.deleteVocabularyTerms([ id ], options);
-		}.bind(this);
-
-		this.deleteTag = function(facade, id) {
-			var c = this;
-			var options = new dtos.TagDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteTags([ id ], options);
-		}.bind(this);
-
-		this.deleteAuthorizationGroup = function(facade, id) {
-			var c = this;
-			var options = new dtos.AuthorizationGroupDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteAuthorizationGroups([ id ], options);
-		}.bind(this);
-
-		this.deleteRoleAssignment = function(facade, id) {
-			var c = this;
-			var options = new dtos.RoleAssignmentDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteRoleAssignments([ id ], options);
-		}.bind(this);
-
-		this.deleteOperationExecution = function(facade, id) {
-			var c = this;
-			var options = new dtos.OperationExecutionDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteOperationExecutions([ id ], options);
-		}.bind(this);
-
-		this.deleteSemanticAnnotation = function(facade, id) {
-			var c = this;
-			var options = new dtos.SemanticAnnotationDeletionOptions();
-			options.setReason("test reason");
-			return facade.deleteSemanticAnnotations([ id ], options);
-		}.bind(this);
-
-		this.getObjectProperty = function(object, propertyName) {
-			var propertyNames = propertyName.split('.');
-			for ( var pn in propertyNames) {
-				object = object[propertyNames[pn]];
-			}
-			return object;
-		};
-
-		this.createFacade = function() {
-			var dfd = $.Deferred();
-			dfd.resolve(new openbis(testApiUrl));
-			return dfd.promise();
-		};
-
-		this.createFacadeAndLogin = function() {
-			var dfd = $.Deferred();
-
-			this.createFacade().then(function(facade) {
-				facade.login(testUserId, testUserPassword).done(function() {
-					dfd.resolve(facade);
-				}).fail(function() {
-					dfd.reject(arguments);
-				});
-			});
-
-			return dfd.promise();
-		};
-
-		this.createSpaceFetchOptions = function() {
-			var fo = new dtos.SpaceFetchOptions();
-			fo.withProjects();
-			fo.withSamples();
-			fo.withRegistrator();
-			return fo;
-		};
-
-		this.createProjectFetchOptions = function() {
-			var fo = new dtos.ProjectFetchOptions();
-			fo.withSpace();
-			fo.withExperiments();
-			fo.withRegistrator();
-			fo.withModifier();
-			fo.withLeader();
-			fo.withAttachments().withContent();
-			return fo;
-		};
-
-		this.createExperimentFetchOptions = function() {
-			var fo = new dtos.ExperimentFetchOptions();
-			fo.withType();
-			fo.withProject().withSpace();
-			fo.withDataSets();
-			fo.withSamples();
-			fo.withHistory();
-			fo.withProperties();
-			fo.withMaterialProperties();
-			fo.withTags();
-			fo.withRegistrator();
-			fo.withModifier();
-			fo.withAttachments().withContent();
-			return fo;
-		};
-
-		this.createExperimentTypeFetchOptions = function() {
-			var fo = new dtos.ExperimentTypeFetchOptions();
-			fo.withPropertyAssignments().withPropertyType();
-			fo.withPropertyAssignments().withRegistrator();
-			return fo;
-		};
-
-		this.createSampleFetchOptions = function() {
-			var fo = new dtos.SampleFetchOptions();
-			fo.withType();
-			fo.withExperiment().withProject().withSpace();
-			fo.withSpace();
-			fo.withProperties();
-			fo.withMaterialProperties();
-			fo.withParents();
-			fo.withChildren();
-			fo.withContainer();
-			fo.withComponents();
-			fo.withDataSets();
-			fo.withHistory();
-			fo.withTags();
-			fo.withRegistrator();
-			fo.withModifier();
-			fo.withAttachments().withContent();
-			fo.withChildrenUsing(fo);
-			return fo;
-		};
-
-		this.createSampleTypeFetchOptions = function() {
-			var fo = new dtos.SampleTypeFetchOptions();
-			fo.withPropertyAssignments().withPropertyType();
-			fo.withPropertyAssignments().withRegistrator();
-			return fo;
-		};
-
-		this.createDataSetFetchOptions = function() {
-			var fo = new dtos.DataSetFetchOptions();
-			fo.withType();
-			fo.withExperiment().withProject().withSpace();
-			fo.withSample();
-			fo.withProperties();
-			fo.withMaterialProperties();
-			fo.withParents();
-			fo.withChildren();
-			fo.withContainers();
-			fo.withComponents();
-			fo.withPhysicalData().withFileFormatType();
-			fo.withPhysicalData().withLocatorType();
-			fo.withPhysicalData().withStorageFormat();
-			fo.withLinkedData().withExternalDms();
-			fo.withHistory();
-			fo.withTags();
-			fo.withRegistrator();
-			fo.withModifier();
-			return fo;
-		};
-
-		this.createDataSetTypeFetchOptions = function() {
-			var fo = new dtos.DataSetTypeFetchOptions();
-			fo.withPropertyAssignments().withPropertyType();
-			fo.withPropertyAssignments().withRegistrator();
-			return fo;
-		};
-
-		this.createMaterialFetchOptions = function() {
-			var fo = new dtos.MaterialFetchOptions();
-			fo.withType();
-			fo.withHistory();
-			fo.withRegistrator();
-			fo.withProperties();
-			fo.withMaterialProperties();
-			fo.withTags();
-			return fo;
-		};
-
-		this.createMaterialTypeFetchOptions = function() {
-			var fo = new dtos.MaterialTypeFetchOptions();
-			fo.withPropertyAssignments().withPropertyType();
-			fo.withPropertyAssignments().withRegistrator();
-			return fo;
-		};
-
-		this.createPropertyTypeFetchOptions = function() {
-			var fo = new dtos.PropertyTyüeFetchOptions();
-			fo.withVocabulary();
-			fo.withMaterialType();
-			return fo;
-		};
-		
-		this.createVocabularyFetchOptions = function() {
-			var fo = new dtos.VocabularyFetchOptions();
-			fo.withTerms();
-			fo.withRegistrator();
-			return fo;
-		};
-		
-		this.createVocabularyTermFetchOptions = function() {
-			var fo = new dtos.VocabularyTermFetchOptions();
-			fo.withVocabulary();
-			fo.withRegistrator();
-			return fo;
-		};
-
-		this.createGlobalSearchObjectFetchOptions = function() {
-			var fo = new dtos.GlobalSearchObjectFetchOptions();
-			fo.withExperiment();
-			fo.withSample();
-			fo.withDataSet();
-			fo.withMaterial();
-			return fo;
-		};
-
-		this.createObjectKindModificationFetchOptions = function() {
-			var fo = new dtos.ObjectKindModificationFetchOptions();
-			return fo;
-		};
-
-		this.createTagFetchOptions = function() {
-			var fo = new dtos.TagFetchOptions();
-			fo.withExperiments();
-			fo.withSamples();
-			fo.withDataSets();
-			fo.withMaterials();
-			return fo;
-		};
-
-		this.createAuthorizationGroupFetchOptions = function() {
-			var fo = new dtos.AuthorizationGroupFetchOptions();
-			fo.withRegistrator();
-			fo.withUsers();
-			var rafo = fo.withRoleAssignments();
-			rafo.withSpace();
-			rafo.withProject().withSpace();
-			return fo;
-		};
-
-		this.createRoleAssignmentFetchOptions = function() {
-			var fo = new dtos.RoleAssignmentFetchOptions();
-			fo.withProject();
-			fo.withSpace();
-			fo.withUser();
-			fo.withAuthorizationGroup();
-			fo.withRegistrator();
-			return fo;
-		};
-
-		this.createPersonFetchOptions = function() {
-			var fo = new dtos.PersonFetchOptions();
-			fo.withSpace();
-			fo.withRoleAssignments().withSpace();
-			fo.withRegistrator();
-			return fo;
-		};
-
-		this.createPropertyTypeFetchOptions = function() {
-			var fo = new dtos.PropertyTypeFetchOptions();
-			fo.withVocabulary();
-			fo.withMaterialType();
-			fo.withSemanticAnnotations();
-			fo.withRegistrator();
-			return fo;
-		};
-
-		this.createPropertyAssignmentFetchOptions = function() {
-			var fo = new dtos.PropertyAssignmentFetchOptions();
-			fo.withEntityType();
-			fo.withPropertyType();
-			fo.withSemanticAnnotations();
-			fo.withRegistrator();
-			return fo;
-		};
-
-		this.createSemanticAnnotationFetchOptions = function() {
-			var fo = new dtos.SemanticAnnotationFetchOptions();
-			fo.withEntityType();
-			fo.withPropertyType();
-			fo.withPropertyAssignment().withEntityType();
-			fo.withPropertyAssignment().withPropertyType();
-			return fo;
-		};
-
-		this.createExternalDmsFetchOptions = function() {
-			var fo = new dtos.ExternalDmsFetchOptions();
-			return fo;
-		};
-
-		this.createOperationExecutionFetchOptions = function() {
-			var fo = new dtos.OperationExecutionFetchOptions();
-			fo.withOwner().withSpace();
-			fo.withOwner().withRegistrator();
-			fo.withNotification();
-			fo.withSummary().withOperations();
-			fo.withSummary().withProgress();
-			fo.withSummary().withError();
-			fo.withSummary().withResults();
-			fo.withDetails().withOperations();
-			fo.withDetails().withProgress();
-			fo.withDetails().withError();
-			fo.withDetails().withResults();
-			return fo;
-		};
-
-		this.createDataStoreFetchOptions = function() {
-			var fo = new dtos.DataStoreFetchOptions();
-			return fo;
-		};
-
-		this.createDataSetFileFetchOptions = function() {
-			var fo = new dtos.DataSetFileFetchOptions();
-			return fo;
-		};
-
-		this.assertNull = function(actual, msg) {
-			this.assertEqual(actual, null, msg)
-		};
-
-		this.assertNotNull = function(actual, msg) {
-			this.assertNotEqual(actual, null, msg);
-		};
-
-		this.assertTrue = function(actual, msg) {
-			this.assertEqual(actual, true, msg);
-		};
-
-		this.assertFalse = function(actual, msg) {
-			this.assertEqual(actual, false, msg);
-		};
-
-		this.assertContains = function(actual, expected, msg) {
-			actual = actual ? actual : "";
-			this.assertTrue(actual.indexOf(expected) >= 0, msg);
-		};
-
-		this.assertEqual = function(actual, expected, msg) {
-			this.assert.equal(actual, expected, msg);
-		};
-
-		this.assertNotEqual = function(actual, expected, msg) {
-			this.assert.notEqual(actual, expected, msg);
-		};
-
-		this.assertDate = function(millis, msg, year, month, day, hour, minute) {
-			var date = new Date(millis);
-			var actual = "";
-			var expected = "";
-
-			if (year) {
-				actual += date.getUTCFullYear();
-				expected += year;
-			}
-			if (month) {
-				actual += "-" + (date.getUTCMonth() + 1);
-				expected += "-" + month;
-			}
-			if (day) {
-				actual += "-" + date.getUTCDate();
-				expected += "-" + day;
-			}
-			if (hour) {
-				actual += " " + date.getUTCHours();
-				expected += " " + hour;
-			}
-			if (minute) {
-				actual += ":" + date.getUTCMinutes();
-				expected += ":" + minute;
-			}
-
-			this.assertEqual(actual, expected, msg);
-		};
-
-		this.assertToday = function(millis, msg) {
-			var today = new Date();
-			this.assertDate(millis, msg, today.getUTCFullYear(), today.getUTCMonth() + 1, today.getUTCDate());
-		};
-
-		this.assertObjectsCount = function(objects, count) {
-			this.assertEqual(objects.length, count, 'Got ' + count + ' object(s)');
-		};
-
-		this.assertObjectsWithValues = function(objects, propertyName, propertyValues) {
-			var thisCommon = this;
-			var values = {};
-
-			$.each(objects, function(index, object) {
-				var value = thisCommon.getObjectProperty(object, propertyName);
-				if (value in values == false) {
-					values[value] = true;
+
+				this.SpaceCreation = dtos.SpaceCreation;
+				this.ProjectCreation = dtos.ProjectCreation;
+				this.ExperimentCreation = dtos.ExperimentCreation;
+				this.SampleCreation = dtos.SampleCreation;
+				this.MaterialCreation = dtos.MaterialCreation;
+				this.AttachmentCreation = dtos.AttachmentCreation;
+				this.VocabularyCreation = dtos.VocabularyCreation;
+				this.VocabularyTermCreation = dtos.VocabularyTermCreation;
+				this.TagCreation = dtos.TagCreation;
+				this.AuthorizationGroupCreation = dtos.AuthorizationGroupCreation;
+				this.RoleAssignmentCreation = dtos.RoleAssignmentCreation;
+				this.PersonCreation = dtos.PersonCreation;
+				this.Role = require('as/dto/roleassignment/Role');
+				this.RoleLevel = require('as/dto/roleassignment/RoleLevel');
+				this.DataType = require('as/dto/property/DataType');
+				this.EntityKind = require('as/dto/entitytype/EntityKind');
+				this.PluginType = require('as/dto/plugin/PluginType');
+				this.ScriptType = require('as/dto/plugin/ScriptType');
+				this.SemanticAnnotationCreation = dtos.SemanticAnnotationCreation;
+				this.DataSetCreation = dtos.DataSetCreation;
+				this.FullDataSetCreation = dtos.FullDataSetCreation;
+				this.UploadedDataSetCreation = dtos.UploadedDataSetCreation;
+				this.DataSetFileCreation = dtos.DataSetFileCreation;
+				this.LinkedDataCreation = dtos.LinkedDataCreation;
+				this.ContentCopyCreation = dtos.ContentCopyCreation;
+				this.ExternalDmsCreation = dtos.ExternalDmsCreation;
+				this.ExternalDmsAddressType = require('as/dto/externaldms/ExternalDmsAddressType');
+				this.SpaceUpdate = dtos.SpaceUpdate;
+				this.ProjectUpdate = dtos.ProjectUpdate;
+				this.ExperimentTypeUpdate = dtos.ExperimentTypeUpdate;
+				this.ExperimentUpdate = dtos.ExperimentUpdate;
+				this.SampleTypeUpdate = dtos.SampleTypeUpdate;
+				this.SampleUpdate = dtos.SampleUpdate;
+				this.DataSetTypeUpdate = dtos.DataSetTypeUpdate;
+				this.DataSetUpdate = dtos.DataSetUpdate;
+				this.PhysicalDataUpdate = dtos.PhysicalDataUpdate;
+				this.LinkedDataUpdate = dtos.LinkedDataUpdate;
+				this.SemanticAnnotationUpdate = dtos.SemanticAnnotationUpdate;
+				this.ContentCopyListUpdateValue = dtos.ContentCopyListUpdateValue;
+				this.DataStorePermId = dtos.DataStorePermId;
+				this.MaterialTypeUpdate = dtos.MaterialTypeUpdate;
+				this.MaterialUpdate = dtos.MaterialUpdate;
+				this.VocabularyUpdate = dtos.VocabularyUpdate;
+				this.VocabularyTermUpdate = dtos.VocabularyTermUpdate;
+				this.ExternalDmsUpdate = dtos.ExternalDmsUpdate;
+				this.TagUpdate = dtos.TagUpdate;
+				this.AuthorizationGroupUpdate = dtos.AuthorizationGroupUpdate;
+				this.PersonUpdate = dtos.PersonUpdate;
+				this.SpaceDeletionOptions = dtos.SpaceDeletionOptions;
+				this.ProjectDeletionOptions = dtos.ProjectDeletionOptions;
+				this.ExperimentDeletionOptions = dtos.ExperimentDeletionOptions;
+				this.SampleDeletionOptions = dtos.SampleDeletionOptions;
+				this.DataSetDeletionOptions = dtos.DataSetDeletionOptions;
+				this.MaterialDeletionOptions = dtos.MaterialDeletionOptions;
+				this.VocabularyTermDeletionOptions = dtos.VocabularyTermDeletionOptions;
+				this.EntityTypeDeletionOptions = dtos.EntityTypeDeletionOptions;
+				this.ExternalDmsDeletionOptions = dtos.ExternalDmsDeletionOptions;
+				this.TagDeletionOptions = dtos.TagDeletionOptions;
+				this.AuthorizationGroupDeletionOptions = dtos.AuthorizationGroupDeletionOptions;
+				this.RoleAssignmentDeletionOptions = dtos.RoleAssignmentDeletionOptions;
+				this.SemanticAnnotationDeletionOptions = dtos.SemanticAnnotationDeletionOptions;
+				this.PersonPermId = dtos.PersonPermId;
+				this.Me = dtos.Me;
+				this.EntityTypePermId = dtos.EntityTypePermId;
+				this.SpacePermId = dtos.SpacePermId;
+				this.ProjectPermId = dtos.ProjectPermId;
+				this.ProjectIdentifier = dtos.ProjectIdentifier;
+				this.ExperimentPermId = dtos.ExperimentPermId;
+				this.ExperimentIdentifier = dtos.ExperimentIdentifier;
+				this.SamplePermId = dtos.SamplePermId;
+				this.SampleIdentifier = dtos.SampleIdentifier;
+				this.DataSetPermId = dtos.DataSetPermId;
+				this.FileFormatTypePermId = dtos.FileFormatTypePermId;
+				this.MaterialPermId = dtos.MaterialPermId;
+				this.ContentCopyPermId = dtos.ContentCopyPermId;
+				this.ExternalDmsPermId = dtos.ExternalDmsPermId;
+				this.VocabularyPermId = dtos.VocabularyPermId;
+				this.VocabularyTermPermId = dtos.VocabularyTermPermId;
+				this.AuthorizationGroupPermId = dtos.AuthorizationGroupPermId;
+				this.RoleAssignmentTechId = dtos.RoleAssignmentTechId;
+				this.TagPermId = dtos.TagPermId;
+				this.TagCode = dtos.TagCode;
+				this.SemanticAnnotationsPermId = dtos.SemanticAnnotationsPermId;
+				this.SpaceSearchCriteria = dtos.SpaceSearchCriteria;
+				this.ProjectSearchCriteria = dtos.ProjectSearchCriteria;
+				this.ExperimentSearchCriteria = dtos.ExperimentSearchCriteria;
+				this.ExperimentTypeSearchCriteria = dtos.ExperimentTypeSearchCriteria;
+				this.SampleSearchCriteria = dtos.SampleSearchCriteria;
+				this.SampleTypeSearchCriteria = dtos.SampleTypeSearchCriteria;
+				this.DataSetSearchCriteria = dtos.DataSetSearchCriteria;
+				this.DataSetTypeSearchCriteria = dtos.DataSetTypeSearchCriteria;
+				this.MaterialSearchCriteria = dtos.MaterialSearchCriteria;
+				this.MaterialTypeSearchCriteria = dtos.MaterialTypeSearchCriteria;
+				this.ExternalDmsSearchCriteria = dtos.ExternalDmsSearchCriteria;
+				this.VocabularySearchCriteria = dtos.VocabularySearchCriteria;
+				this.VocabularyTermSearchCriteria = dtos.VocabularyTermSearchCriteria;
+				this.DataSetFileSearchCriteria = dtos.DataSetFileSearchCriteria;
+				this.TagSearchCriteria = dtos.TagSearchCriteria;
+				this.AuthorizationGroupSearchCriteria = dtos.AuthorizationGroupSearchCriteria;
+				this.RoleAssignmentSearchCriteria = dtos.RoleAssignmentSearchCriteria;
+				this.PersonSearchCriteria = dtos.PersonSearchCriteria;
+				this.DataStoreSearchCriteria = dtos.DataStoreSearchCriteria;
+				this.PropertyTypeSearchCriteria = dtos.PropertyTypeSearchCriteria;
+				this.PropertyAssignmentSearchCriteria = dtos.PropertyAssignmentSearchCriteria;
+				this.SemanticAnnotationSearchCriteria = dtos.SemanticAnnotationSearchCriteria;
+				this.SpaceFetchOptions = dtos.SpaceFetchOptions;
+				this.ProjectFetchOptions = dtos.ProjectFetchOptions;
+				this.ExperimentFetchOptions = dtos.ExperimentFetchOptions;
+				this.ExperimentTypeFetchOptions = dtos.ExperimentTypeFetchOptions;
+				this.SampleFetchOptions = dtos.SampleFetchOptions;
+				this.SampleTypeFetchOptions = dtos.SampleTypeFetchOptions;
+				this.DataSetFetchOptions = dtos.DataSetFetchOptions;
+				this.DataSetTypeFetchOptions = dtos.DataSetTypeFetchOptions;
+				this.MaterialFetchOptions = dtos.MaterialFetchOptions;
+				this.MaterialTypeFetchOptions = dtos.MaterialTypeFetchOptions;
+				this.ExternalDmsFetchOptions = dtos.ExternalDmsFetchOptions;
+				this.VocabularyFetchOptions = dtos.VocabularyFetchOptions;
+				this.VocabularyTermFetchOptions = dtos.VocabularyTermFetchOptions;
+				this.TagFetchOptions = dtos.TagFetchOptions;
+				this.AuthorizationGroupFetchOptions = dtos.AuthorizationGroupFetchOptions;
+				this.RoleAssignmentFetchOptions = dtos.RoleAssignmentFetchOptions;
+				this.PersonFetchOptions = dtos.PersonFetchOptions;
+				this.PluginFetchOptions = dtos.PluginFetchOptions;
+				this.PropertyTypeFetchOptions = dtos.PropertyTypeFetchOptions;
+				this.PropertyAssignmentFetchOptions = dtos.PropertyAssignmentFetchOptions;
+				this.SemanticAnnotationFetchOptions = dtos.SemanticAnnotationFetchOptions;
+				this.DeletionFetchOptions = dtos.DeletionFetchOptions;
+				this.DeletionSearchCriteria = dtos.DeletionSearchCriteria;
+				this.CustomASServiceSearchCriteria = dtos.CustomASServiceSearchCriteria;
+				this.CustomASServiceFetchOptions = dtos.CustomASServiceFetchOptions;
+				this.CustomASServiceCode = dtos.CustomASServiceCode;
+				this.CustomASServiceExecutionOptions = dtos.CustomASServiceExecutionOptions;
+				this.GlobalSearchCriteria = dtos.GlobalSearchCriteria;
+				this.GlobalSearchObjectFetchOptions = dtos.GlobalSearchObjectFetchOptions;
+				this.ObjectKindModificationSearchCriteria = dtos.ObjectKindModificationSearchCriteria;
+				this.ObjectKindModificationFetchOptions = dtos.ObjectKindModificationFetchOptions;
+				this.DataSetArchiveOptions = dtos.DataSetArchiveOptions;
+				this.DataSetUnarchiveOptions = dtos.DataSetUnarchiveOptions;
+				this.PropertyAssignmentCreation = dtos.PropertyAssignmentCreation;
+				this.PropertyTypePermId = dtos.PropertyTypePermId;
+				this.PropertyAssignmentPermId = dtos.PropertyAssignmentPermId;
+				this.PluginPermId = dtos.PluginPermId;
+				this.Plugin = dtos.Plugin;
+				this.PluginCreation = dtos.PluginCreation;
+				this.ExperimentTypeCreation = dtos.ExperimentTypeCreation;
+				this.SampleTypeCreation = dtos.SampleTypeCreation;
+				this.DataSetTypeCreation = dtos.DataSetTypeCreation;
+				this.MaterialTypeCreation = dtos.MaterialTypeCreation;
+				this.PropertyTypeCreation = dtos.PropertyTypeCreation;
+				this.PropertyTypeUpdate = dtos.PropertyTypeUpdate;
+				this.PluginUpdate = dtos.PluginUpdate;
+				this.WebAppSettings = dtos.WebAppSettings;
+
+				// operations
+
+				this.GetSessionInformationOperation = dtos.GetSessionInformationOperation;
+				this.GetSpacesOperation = dtos.GetSpacesOperation;
+				this.GetProjectsOperation = dtos.GetProjectsOperation;
+				this.GetExperimentsOperation = dtos.GetExperimentsOperation;
+				this.GetSamplesOperation = dtos.GetSamplesOperation;
+				this.GetDataSetsOperation = dtos.GetDataSetsOperation;
+				this.GetMaterialsOperation = dtos.GetMaterialsOperation;
+				this.GetPluginsOperation = dtos.GetPluginsOperation;
+				this.GetPropertyTypesOperation = dtos.GetPropertyTypesOperation;
+				this.GetVocabulariesOperation = dtos.GetVocabulariesOperation;
+				this.GetVocabularyTermsOperation = dtos.GetVocabularyTermsOperation;
+				this.GetTagsOperation = dtos.GetTagsOperation;
+				this.GetAuthorizationGroupsOperation = dtos.GetAuthorizationGroupsOperation;
+				this.GetRoleAssignmentsOperation = dtos.GetRoleAssignmentsOperation;
+				this.GetPersonsOperation = dtos.GetPersonsOperation;
+				this.GetExternalDmsOperation = dtos.GetExternalDmsOperation;
+				this.GetSemanticAnnotationsOperation = dtos.GetSemanticAnnotationsOperation;
+
+				this.CreateSpacesOperation = dtos.CreateSpacesOperation;
+				this.CreateProjectsOperation = dtos.CreateProjectsOperation;
+				this.CreateExperimentsOperation = dtos.CreateExperimentsOperation;
+				this.CreateExperimentTypesOperation = dtos.CreateExperimentTypesOperation;
+				this.CreateSamplesOperation = dtos.CreateSamplesOperation;
+				this.CreateSampleTypesOperation = dtos.CreateSampleTypesOperation;
+				this.CreateDataSetsOperation = dtos.CreateDataSetsOperation;
+				this.CreateDataSetTypesOperation = dtos.CreateDataSetTypesOperation;
+				this.CreateMaterialsOperation = dtos.CreateMaterialsOperation;
+				this.CreateMaterialTypesOperation = dtos.CreateMaterialTypesOperation;
+				this.CreatePropertyTypesOperation = dtos.CreatePropertyTypesOperation;
+				this.CreatePluginsOperation = dtos.CreatePluginsOperation;
+				this.CreateVocabulariesOperation = dtos.CreateVocabulariesOperation;
+				this.CreateVocabularyTermsOperation = dtos.CreateVocabularyTermsOperation;
+				this.CreateTagsOperation = dtos.CreateTagsOperation;
+				this.CreateAuthorizationGroupsOperation = dtos.CreateAuthorizationGroupsOperation;
+				this.CreateRoleAssignmentsOperation = dtos.CreateRoleAssignmentsOperation;
+				this.CreatePersonsOperation = dtos.CreatePersonsOperation;
+				this.CreateSemanticAnnotationsOperation = dtos.CreateSemanticAnnotationsOperation;
+				this.CreateExternalDmsOperation = dtos.CreateExternalDmsOperation;
+
+				this.UpdateSpacesOperation = dtos.UpdateSpacesOperation;
+				this.UpdateProjectsOperation = dtos.UpdateProjectsOperation;
+				this.UpdateExperimentsOperation = dtos.UpdateExperimentsOperation;
+				this.UpdateExperimentTypesOperation = dtos.UpdateExperimentTypesOperation;
+				this.UpdateSamplesOperation = dtos.UpdateSamplesOperation;
+				this.UpdateSampleTypesOperation = dtos.UpdateSampleTypesOperation;
+				this.UpdateDataSetsOperation = dtos.UpdateDataSetsOperation;
+				this.UpdateDataSetTypesOperation = dtos.UpdateDataSetTypesOperation;
+				this.UpdateMaterialsOperation = dtos.UpdateMaterialsOperation;
+				this.UpdateMaterialTypesOperation = dtos.UpdateMaterialTypesOperation;
+				this.UpdatePropertyTypesOperation = dtos.UpdatePropertyTypesOperation;
+				this.UpdatePluginsOperation = dtos.UpdatePluginsOperation;
+				this.UpdateVocabulariesOperation = dtos.UpdateVocabulariesOperation;
+				this.UpdateVocabularyTermsOperation = dtos.UpdateVocabularyTermsOperation;
+				this.UpdateExternalDmsOperation = dtos.UpdateExternalDmsOperation;
+				this.UpdateTagsOperation = dtos.UpdateTagsOperation;
+				this.UpdateAuthorizationGroupsOperation = dtos.UpdateAuthorizationGroupsOperation;
+				this.UpdatePersonsOperation = dtos.UpdatePersonsOperation;
+				this.UpdateOperationExecutionsOperation = dtos.UpdateOperationExecutionsOperation;
+				this.UpdateSemanticAnnotationsOperation = dtos.UpdateSemanticAnnotationsOperation;
+
+				this.GetSpacesOperation = dtos.GetSpacesOperation;
+				this.GetProjectsOperation = dtos.GetProjectsOperation;
+				this.GetExperimentsOperation = dtos.GetExperimentsOperation;
+				this.GetSamplesOperation = dtos.GetSamplesOperation;
+				this.GetDataSetsOperation = dtos.GetDataSetsOperation;
+				this.GetMaterialsOperation = dtos.GetMaterialsOperation;
+				this.GetVocabularyTermsOperation = dtos.GetVocabularyTermsOperation;
+				this.GetTagsOperation = dtos.GetTagsOperation;
+				this.GetSemanticAnnotationsOperation = dtos.GetSemanticAnnotationsOperation;
+				this.GetOperationExecutionsOperation = dtos.GetOperationExecutionsOperation;
+
+				this.SearchSpacesOperation = dtos.SearchSpacesOperation;
+				this.SearchProjectsOperation = dtos.SearchProjectsOperation;
+				this.SearchExperimentsOperation = dtos.SearchExperimentsOperation;
+				this.SearchExperimentTypesOperation = dtos.SearchExperimentTypesOperation;
+				this.SearchSamplesOperation = dtos.SearchSamplesOperation;
+				this.SearchSampleTypesOperation = dtos.SearchSampleTypesOperation;
+				this.SearchDataSetsOperation = dtos.SearchDataSetsOperation;
+				this.SearchDataSetTypesOperation = dtos.SearchDataSetTypesOperation;
+				this.SearchMaterialsOperation = dtos.SearchMaterialsOperation;
+				this.SearchMaterialTypesOperation = dtos.SearchMaterialTypesOperation;
+				this.SearchVocabulariesOperation = dtos.SearchVocabulariesOperation;
+				this.SearchVocabularyTermsOperation = dtos.SearchVocabularyTermsOperation;
+				this.SearchExternalDmsOperation = dtos.SearchExternalDmsOperation;
+				this.SearchTagsOperation = dtos.SearchTagsOperation;
+				this.SearchAuthorizationGroupsOperation = dtos.SearchAuthorizationGroupsOperation;
+				this.SearchRoleAssignmentsOperation = dtos.SearchRoleAssignmentsOperation;
+				this.SearchPersonsOperation = dtos.SearchPersonsOperation;
+				this.SearchCustomASServicesOperation = dtos.SearchCustomASServicesOperation;
+				this.SearchObjectKindModificationsOperation = dtos.SearchObjectKindModificationsOperation;
+				this.SearchGloballyOperation = dtos.SearchGloballyOperation;
+				this.SearchOperationExecutionsOperation = dtos.SearchOperationExecutionsOperation;
+				this.SearchDeletionsOperation = dtos.SearchDeletionsOperation;
+				this.SearchDataStoresOperation = dtos.SearchDataStoresOperation;
+				this.SearchPropertyTypesOperation = dtos.SearchPropertyTypesOperation;
+				this.SearchPropertyAssignmentsOperation = dtos.SearchPropertyAssignmentsOperation;
+				this.SearchSemanticAnnotationsOperation = dtos.SearchSemanticAnnotationsOperation;
+
+				this.DeleteSpacesOperation = dtos.DeleteSpacesOperation;
+				this.DeleteProjectsOperation = dtos.DeleteProjectsOperation;
+				this.DeleteExperimentsOperation = dtos.DeleteExperimentsOperation;
+				this.DeleteSamplesOperation = dtos.DeleteSamplesOperation;
+				this.DeleteDataSetsOperation = dtos.DeleteDataSetsOperation;
+				this.DeleteMaterialsOperation = dtos.DeleteMaterialsOperation;
+				this.DeleteExternalDmsOperation = dtos.DeleteExternalDmsOperation;
+				this.DeletePropertyTypesOperation = dtos.DeletePropertyTypesOperation;
+				this.DeleteVocabulariesOperation = dtos.DeleteVocabulariesOperation;
+				this.DeleteVocabularyTermsOperation = dtos.DeleteVocabularyTermsOperation;
+				this.DeleteEntityTypesOperation = dtos.DeleteEntityTypesOperation;
+				this.DeleteTagsOperation = dtos.DeleteTagsOperation;
+				this.DeleteAuthorizationGroupsOperation = dtos.DeleteAuthorizationGroupsOperation;
+				this.DeleteRoleAssignmentsOperation = dtos.DeleteRoleAssignmentsOperation;
+				this.DeleteOperationExecutionsOperation = dtos.DeleteOperationExecutionsOperation;
+				this.DeleteSemanticAnnotationsOperation = dtos.DeleteSemanticAnnotationsOperation;
+
+				this.RevertDeletionsOperation = dtos.RevertDeletionsOperation;
+				this.ConfirmDeletionsOperation = dtos.ConfirmDeletionsOperation;
+				this.ExecuteCustomASServiceOperation = dtos.ExecuteCustomASServiceOperation;
+				this.ArchiveDataSetsOperation = dtos.ArchiveDataSetsOperation;
+				this.UnarchiveDataSetsOperation = dtos.UnarchiveDataSetsOperation;
+
+				this.SynchronousOperationExecutionOptions = dtos.SynchronousOperationExecutionOptions;
+				this.AsynchronousOperationExecutionOptions = dtos.AsynchronousOperationExecutionOptions;
+				this.OperationExecutionDeletionOptions = dtos.OperationExecutionDeletionOptions;
+				this.OperationExecutionFetchOptions = dtos.OperationExecutionFetchOptions;
+				this.OperationExecutionPermId = dtos.OperationExecutionPermId;
+				this.OperationExecutionSearchCriteria = dtos.OperationExecutionSearchCriteria;
+				this.OperationExecutionUpdate = dtos.OperationExecutionUpdate;
+
+				this.getDtos = function() {
+					return dtos;
 				}
-			});
 
-			this.assert.deepEqual(Object.keys(values).sort(), propertyValues.sort(), 'Objects have correct ' + propertyName + ' values')
-		};
+				this.getId = function(entity) {
+					if (typeof entity["getPermId"] === 'function') {
+						return entity.getPermId();
+					}
+					if (typeof entity["getId"] === 'function') {
+						return entity.getId();
+					}
+					this
+							.fail("Neither 'getPermId()' nor 'getId()' are functions of entity "
+									+ entity);
+				}
 
-		this.assertObjectsWithOrWithoutCollections = function(objects, accessor, checker) {
-			var theObjects = null;
+				this.generateId = function(base) {
+					var date = new Date();
+					var parts = [ "V3", base, date.getFullYear(),
+							date.getMonth() + 1, date.getDate(),
+							date.getHours(), date.getMinutes(),
+							Math.round(1000000 * Math.random()) ];
+					return parts.join("_");
+				},
+
+				this.createSpace = function(facade) {
+					var c = this;
+					var creation = new dtos.SpaceCreation();
+					creation.setCode(c.generateId("SPACE"));
+					return facade.createSpaces([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createProject = function(facade) {
+					var c = this;
+					return c.createSpace(facade).then(
+							function(spacePermId) {
+								var creation = new dtos.ProjectCreation();
+								creation.setCode(c.generateId("PROJECT"));
+								creation.setSpaceId(spacePermId);
+								return facade.createProjects([ creation ])
+										.then(function(permIds) {
+											return permIds[0];
+										});
+							});
+				}.bind(this);
+
+				this.createExperiment = function(facade) {
+					var c = this;
+					return c.createProject(facade).then(
+							function(projectPermId) {
+								var creation = new dtos.ExperimentCreation();
+								creation.setCode(c.generateId("EXPERIMENT"));
+								creation.setTypeId(new dtos.EntityTypePermId(
+										"UNKNOWN"));
+								creation.setProjectId(projectPermId);
+								return facade.createExperiments([ creation ])
+										.then(function(permIds) {
+											return permIds[0];
+										});
+							});
+				}.bind(this);
+
+				this.createSample = function(facade) {
+					var c = this;
+					return c.createSpace(facade).then(
+							function(spacePermId) {
+								var creation = new dtos.SampleCreation();
+								creation.setCode(c.generateId("SAMPLE"));
+								creation.setTypeId(new dtos.EntityTypePermId(
+										"UNKNOWN"));
+								creation.setSpaceId(spacePermId);
+								return facade.createSamples([ creation ]).then(
+										function(permIds) {
+											return permIds[0];
+										});
+							});
+				}.bind(this);
+
+				this.createLinkDataSet = function(facade, path, gitCommitHash,
+						gitRepositoryId) {
+					var c = this;
+					return c
+							.createExperiment(facade)
+							.then(
+									function(experimentPermId) {
+										return c
+												.createFileExternalDms(facade)
+												.then(
+														function(emdsPermId) {
+															var dataSet = new dtos.DataSetCreation();
+															dataSet
+																	.setAutoGeneratedCode(true);
+															dataSet
+																	.setTypeId(new c.EntityTypePermId(
+																			"LINK_TYPE"));
+															dataSet
+																	.setExperimentId(experimentPermId);
+															dataSet
+																	.setDataStoreId(new c.DataStorePermId(
+																			"DSS1"));
+															var linkedData = new c.LinkedDataCreation();
+															var cc = new c.ContentCopyCreation();
+															cc
+																	.setExternalDmsId(emdsPermId);
+															cc.setPath(path);
+															cc
+																	.setGitCommitHash(gitCommitHash);
+															cc
+																	.setGitRepositoryId(gitRepositoryId);
+															linkedData
+																	.setContentCopies([ cc ]);
+															dataSet
+																	.setLinkedData(linkedData);
+															return facade
+																	.createDataSets(
+																			[ dataSet ])
+																	.then(
+																			function(
+																					permIds) {
+																				return permIds[0];
+																			});
+														});
+									});
+				}.bind(this);
+
+				this.createDataSet = function(facade, dataSetType) {
+					var c = this;
+					return this.getResponseFromJSTestAggregationService(facade,
+							{ "dataSetType" : dataSetType }, function(response) {
+								return new dtos.DataSetPermId(
+										response.result.rows[0][0].value);
+							});
+				}.bind(this);
+
+				this.waitUntilIndexed = function(facade, dataSetCode, timeout) {
+					var c = this;
+					var dfd = $.Deferred();
+					var start = new Date().getTime();
+
+					var searchAndWait = function() {
+						var criteria = new c.DataSetSearchCriteria();
+						criteria.withPermId().thatEquals(dataSetCode);
+
+						facade.searchDataSets(criteria,
+								c.createDataSetFetchOptions()).then(
+								function(result) {
+									if (result.getTotalCount() == 0) {
+										var now = new Date().getTime();
+										if (now - start > timeout) {
+											c.fail("Data set " + dataSetCode
+													+ " not indexed after "
+													+ timeout + " msec.");
+											dfd.reject();
+										} else {
+											setTimeout(searchAndWait, 1000);
+										}
+									} else {
+										dfd.resolve();
+									}
+								});
+					};
+					searchAndWait();
+					return dfd.promise();
+				}.bind(this);
+
+				this.getResponseFromJSTestAggregationService = function(facade,
+						params, callback) {
+					var c = this;
+					return $
+							.ajax(
+									{
+										"url" : "http://"
+												+ testHost
+												+ ":20001/datastore_server/rmi-dss-api-v1.json",
+										"type" : "POST",
+										"processData" : false,
+										"dataType" : "json",
+										"data" : JSON
+												.stringify({
+													"method" : "createReportFromAggregationService",
+													"params" : [
+															facade._private.sessionToken,
+															"js-test", params ],
+													"id" : "1",
+													"jsonrpc" : "2.0"
+												})
+									}).then(callback);
+				}.bind(this);
+
+				this.createMaterial = function(facade) {
+					var c = this;
+					var creation = new dtos.MaterialCreation();
+					creation.setCode(c.generateId("MATERIAL"));
+					creation.setTypeId(new dtos.EntityTypePermId("COMPOUND"));
+					return facade.createMaterials([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createPropertyType = function(facade) {
+					var c = this;
+					var creation = new dtos.PropertyTypeCreation();
+					creation.setCode(c.generateId("PROPERTY_TYPE"));
+					creation.setLabel("Testing");
+					creation.setDescription("testing");
+					creation.setDataType(c.DataType.VARCHAR);
+					return facade.createPropertyTypes([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createPlugin = function(facade) {
+					var c = this;
+					var creation = new dtos.PluginCreation();
+					creation.setName(c.generateId("PLUGIN"));
+					creation.setPluginType(c.PluginType.PREDEPLOYED);
+					creation.setScriptType(c.ScriptType.DYNAMIC_PROPERTY);
+					creation.setEntityKind(c.EntityKind.SAMPLE);
+					return facade.createPlugins([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createVocabulary = function(facade) {
+					var c = this;
+					var creation = new dtos.VocabularyCreation();
+					creation.setCode(c.generateId("VOCABULARY"));
+					return facade.createVocabularies([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+				
+				this.createVocabularyTerm = function(facade) {
+					var c = this;
+					var creation = new dtos.VocabularyTermCreation();
+					creation.setCode(c.generateId("VOCABULARY_TERM"));
+					creation.setVocabularyId(new c.VocabularyPermId(
+							"TEST-VOCABULARY"));
+					return facade.createVocabularyTerms([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createSampleType = function(facade) {
+					var c = this;
+					var creation = new dtos.SampleTypeCreation();
+					creation.setCode(c.generateId("SAMPLE_TYPE"));
+					return facade.createSampleTypes([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createExternalDms = function(facade) {
+					var c = this;
+					var creation = new dtos.ExternalDmsCreation();
+					creation.setCode(c.generateId("EMDS"));
+					creation.setAddressType(c.ExternalDmsAddressType.URL);
+					creation
+							.setAddress("https://my-server:8443/my-app/q=${term}")
+					return facade.createExternalDms([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createFileExternalDms = function(facade) {
+					var c = this;
+					var creation = new c.ExternalDmsCreation();
+					creation.setCode(c.generateId("EMDS"));
+					creation.setLabel("Test File EDMS");
+					creation
+							.setAddressType(c.ExternalDmsAddressType.FILE_SYSTEM);
+					creation.setAddress("host:my/path")
+					return facade.createExternalDms([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createTag = function(facade) {
+					var c = this;
+					var creation = new dtos.TagCreation();
+					creation.setCode(c.generateId("TAG"));
+					return facade.createTags([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createAuthorizationGroup = function(facade) {
+					var c = this;
+					var creation = new dtos.AuthorizationGroupCreation();
+					creation.setCode(c.generateId("AUTHORIZATION_GROUP"));
+					creation.setUserIds([ new c.PersonPermId("power_user") ]);
+					return facade.createAuthorizationGroups([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createRoleAssignment = function(facade, isUser) {
+					var c = this;
+					return c
+							.createSpace(facade)
+							.then(
+									function(spaceId) {
+										var creation = new dtos.RoleAssignmentCreation();
+										creation.setRole(c.Role.ADMIN);
+										if (isUser) {
+											creation
+													.setUserId(new c.PersonPermId(
+															"power_user"));
+										} else {
+											creation
+													.setAuthorizationGroupId(new c.AuthorizationGroupPermId(
+															"TEST-GROUP"));
+										}
+										creation.setSpaceId(spaceId);
+										return facade.createRoleAssignments(
+												[ creation ]).then(
+												function(permIds) {
+													return permIds[0];
+												});
+									});
+				}.bind(this);
+
+				this.createPerson = function(facade) {
+					var c = this;
+					var creation = new dtos.PersonCreation();
+					creation.setUserId(c.generateId("USER"));
+					return facade.createPersons([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createSemanticAnnotation = function(facade) {
+					var c = this;
+					var creation = new dtos.SemanticAnnotationCreation();
+					creation.setEntityTypeId(new dtos.EntityTypePermId(
+							"UNKNOWN", "SAMPLE"));
+					creation.setPredicateOntologyId(c
+							.generateId("jsPredicateOntologyId"));
+					creation.setPredicateOntologyVersion(c
+							.generateId("jsPredicateOntologyVersion"));
+					creation.setPredicateAccessionId(c
+							.generateId("jsPredicateAccessionId"));
+					creation.setDescriptorOntologyId(c
+							.generateId("jsDescriptorOntologyId"));
+					creation.setDescriptorOntologyVersion(c
+							.generateId("jsDescriptorOntologyVersion"));
+					creation.setDescriptorAccessionId(c
+							.generateId("jsDescriptorAccessionId"));
+					return facade.createSemanticAnnotations([ creation ]).then(
+							function(permIds) {
+								return permIds[0];
+							});
+				}.bind(this);
+
+				this.createOperationExecution = function(facade) {
+					var c = this;
+					var operation = new dtos.GetSpacesOperation(
+							[ new dtos.SpacePermId("/TEST") ],
+							new dtos.SpaceFetchOptions());
+					var options = new dtos.SynchronousOperationExecutionOptions();
+					options.setExecutionId(new dtos.OperationExecutionPermId());
+					return facade.executeOperations([ operation ], options)
+							.then(function() {
+								return options.getExecutionId();
+							});
+				}.bind(this);
+
+				this.findSpace = function(facade, id) {
+					var c = this;
+					return facade
+							.getSpaces([ id ], c.createSpaceFetchOptions())
+							.then(function(spaces) {
+								return spaces[id];
+							});
+				}.bind(this);
+
+				this.findProject = function(facade, id) {
+					var c = this;
+					return facade.getProjects([ id ],
+							c.createProjectFetchOptions()).then(
+							function(projects) {
+								return projects[id];
+							});
+				}.bind(this);
+
+				this.findExperiment = function(facade, id) {
+					var c = this;
+					return facade.getExperiments([ id ],
+							c.createExperimentFetchOptions()).then(
+							function(experiments) {
+								return experiments[id];
+							});
+				}.bind(this);
+
+				this.findExperimentType = function(facade, id) {
+					var c = this;
+					var criteria = new c.ExperimentTypeSearchCriteria();
+					criteria.withId().thatEquals(id);
+					return facade.searchExperimentTypes(criteria,
+							c.createExperimentTypeFetchOptions()).then(
+							function(results) {
+								return results.getObjects()[0];
+							});
+				}.bind(this);
+
+				this.findSample = function(facade, id) {
+					var c = this;
+					return facade.getSamples([ id ],
+							c.createSampleFetchOptions()).then(
+							function(samples) {
+								return samples[id];
+							});
+				}.bind(this);
+
+				this.findSampleType = function(facade, id) {
+					var c = this;
+					var criteria = new c.SampleTypeSearchCriteria();
+					criteria.withId().thatEquals(id);
+					return facade.searchSampleTypes(criteria,
+							c.createSampleTypeFetchOptions()).then(
+							function(results) {
+								return results.getObjects()[0];
+							});
+				}.bind(this);
+
+				this.findDataSet = function(facade, id) {
+					var c = this;
+					return facade.getDataSets([ id ],
+							c.createDataSetFetchOptions()).then(
+							function(dataSets) {
+								return dataSets[id];
+							});
+				}.bind(this);
+
+				this.findDataSetType = function(facade, id) {
+					var c = this;
+					var criteria = new c.DataSetTypeSearchCriteria();
+					criteria.withId().thatEquals(id);
+					return facade.searchDataSetTypes(criteria,
+							c.createDataSetTypeFetchOptions()).then(
+							function(results) {
+								return results.getObjects()[0];
+							});
+				}.bind(this);
+
+				this.findMaterial = function(facade, id) {
+					var c = this;
+					return facade.getMaterials([ id ],
+							c.createMaterialFetchOptions()).then(
+							function(materials) {
+								return materials[id];
+							});
+				}.bind(this);
+
+				this.findMaterialType = function(facade, id) {
+					var c = this;
+					var criteria = new c.MaterialTypeSearchCriteria();
+					criteria.withId().thatEquals(id);
+					return facade.searchMaterialTypes(criteria,
+							c.createMaterialTypeFetchOptions()).then(
+							function(results) {
+								return results.getObjects()[0];
+							});
+				}.bind(this);
+
+				this.findPropertyType = function(facade, id) {
+					var c = this;
+					var criteria = new c.PropertyTypeSearchCriteria();
+					criteria.withId().thatEquals(id);
+					return facade.searchPropertyTypes(criteria,
+							c.createPropertyTypeFetchOptions()).then(
+							function(results) {
+								return results.getObjects()[0];
+							});
+				}.bind(this);
+
+				this.findPlugin = function(facade, id) {
+					var c = this;
+					return facade.getPlugins([ id ],
+							c.createPluginFetchOptions()).then(
+							function(plugins) {
+								return plugins[id];
+							});
+				}.bind(this);
+
+				this.findVocabulary = function(facade, id) {
+					var c = this;
+					return facade.getVocabularies([ id ],
+							c.createVocabularyFetchOptions()).then(
+							function(vocabularies) {
+								return vocabularies[id];
+							});
+				}.bind(this);
+
+				this.findVocabularyTerm = function(facade, id) {
+					var c = this;
+					return facade.getVocabularyTerms([ id ],
+							c.createVocabularyTermFetchOptions()).then(
+							function(terms) {
+								return terms[id];
+							});
+				}.bind(this);
+
+				this.findTag = function(facade, id) {
+					var c = this;
+					return facade.getTags([ id ], c.createTagFetchOptions())
+							.then(function(tags) {
+								return tags[id];
+							});
+				}.bind(this);
+
+				this.findAuthorizationGroup = function(facade, id) {
+					var c = this;
+					return facade.getAuthorizationGroups([ id ],
+							c.createAuthorizationGroupFetchOptions()).then(
+							function(groups) {
+								return groups[id];
+							});
+				}.bind(this);
+
+				this.findRoleAssignment = function(facade, id) {
+					var c = this;
+					return facade.getRoleAssignments([ id ],
+							c.createRoleAssignmentFetchOptions()).then(
+							function(assignments) {
+								return assignments[id];
+							});
+				}.bind(this);
+
+				this.findPerson = function(facade, id) {
+					var c = this;
+					return facade.getPersons([ id ],
+							c.createPersonFetchOptions()).then(
+							function(persons) {
+								return persons[id];
+							});
+				}.bind(this);
+
+				this.findSemanticAnnotation = function(facade, id) {
+					var c = this;
+					return facade.getSemanticAnnotations([ id ],
+							c.createSemanticAnnotationFetchOptions()).then(
+							function(annotations) {
+								return annotations[id];
+							});
+				}.bind(this);
+
+				this.findExternalDms = function(facade, id) {
+					var c = this;
+					return facade.getExternalDataManagementSystems([ id ],
+							c.createExternalDmsFetchOptions()).then(
+							function(edms) {
+								return edms[id];
+							});
+				}.bind(this);
+
+				this.findOperationExecution = function(facade, id) {
+					var c = this;
+					return facade.getOperationExecutions([ id ],
+							c.createOperationExecutionFetchOptions()).then(
+							function(executions) {
+								return executions[id];
+							});
+				}.bind(this);
+
+				this.deleteSpace = function(facade, id) {
+					var c = this;
+					var options = new dtos.SpaceDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteSpaces([ id ], options);
+				}.bind(this);
+
+				this.deleteProject = function(facade, id) {
+					var c = this;
+					var options = new dtos.ProjectDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteProjects([ id ], options);
+				}.bind(this);
+
+				this.deleteExperiment = function(facade, id) {
+					var c = this;
+					var options = new dtos.ExperimentDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteExperiments([ id ], options);
+				}.bind(this);
+
+				this.deleteSample = function(facade, id) {
+					var c = this;
+					var options = new dtos.SampleDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteSamples([ id ], options);
+				}.bind(this);
+
+				this.deleteDataSet = function(facade, id) {
+					var c = this;
+					var options = new dtos.DataSetDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteDataSets([ id ], options);
+				}.bind(this);
+
+				this.deleteMaterial = function(facade, id) {
+					var c = this;
+					var options = new dtos.MaterialDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteMaterials([ id ], options);
+				}.bind(this);
+
+				this.deleteExternalDms = function(facade, id) {
+					var c = this;
+					var options = new dtos.ExternalDmsDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteExternalDataManagementSystems([ id ],
+							options);
+				}.bind(this);
+
+				this.deleteEntityType = function(facade, id) {
+					var c = this;
+					var options = new dtos.EntityTypeDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteEntityTypes([ id ], options);
+				}.bind(this);
+
+				this.deletePropertyType = function(facade, id) {
+					var c = this;
+					var options = new dtos.PropertyTypeDeletionOptions();
+					options.setReason("test reason");
+					return facade.deletePropertyTypes([ id ], options);
+				}.bind(this);
+
+				this.deleteVocabulary = function(facade, id) {
+					var c = this;
+					var options = new dtos.VocabularyDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteVocabularies([ id ], options);
+				}.bind(this);
+
+				this.deleteVocabularyTerm = function(facade, id) {
+					var c = this;
+					var options = new dtos.VocabularyTermDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteVocabularyTerms([ id ], options);
+				}.bind(this);
+
+				this.replaceVocabularyTerm = function(facade, id) {
+					var c = this;
+					var options = new dtos.VocabularyTermDeletionOptions();
+					options.setReason("test reason");
+					options.replace(id, new c.VocabularyTermPermId(
+							"TEST-TERM-1", "TEST-VOCABULARY"));
+					return facade.deleteVocabularyTerms([ id ], options);
+				}.bind(this);
+
+				this.deleteTag = function(facade, id) {
+					var c = this;
+					var options = new dtos.TagDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteTags([ id ], options);
+				}.bind(this);
+
+				this.deleteAuthorizationGroup = function(facade, id) {
+					var c = this;
+					var options = new dtos.AuthorizationGroupDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteAuthorizationGroups([ id ], options);
+				}.bind(this);
+
+				this.deleteRoleAssignment = function(facade, id) {
+					var c = this;
+					var options = new dtos.RoleAssignmentDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteRoleAssignments([ id ], options);
+				}.bind(this);
+
+				this.deleteOperationExecution = function(facade, id) {
+					var c = this;
+					var options = new dtos.OperationExecutionDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteOperationExecutions([ id ], options);
+				}.bind(this);
+
+				this.deleteSemanticAnnotation = function(facade, id) {
+					var c = this;
+					var options = new dtos.SemanticAnnotationDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteSemanticAnnotations([ id ], options);
+				}.bind(this);
+
+				this.getObjectProperty = function(object, propertyName) {
+					var propertyNames = propertyName.split('.');
+					for ( var pn in propertyNames) {
+						object = object[propertyNames[pn]];
+					}
+					return object;
+				};
+
+				this.createFacade = function() {
+					var dfd = $.Deferred();
+					dfd.resolve(new openbis(testApiUrl));
+					return dfd.promise();
+				};
+
+				this.createFacadeAndLogin = function() {
+					var dfd = $.Deferred();
+
+					this.createFacade().then(
+							function(facade) {
+								facade.login(testUserId, testUserPassword)
+										.done(function() {
+											dfd.resolve(facade);
+										}).fail(function() {
+											dfd.reject(arguments);
+										});
+							});
+
+					return dfd.promise();
+				};
+
+				this.createSpaceFetchOptions = function() {
+					var fo = new dtos.SpaceFetchOptions();
+					fo.withProjects();
+					fo.withSamples();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createProjectFetchOptions = function() {
+					var fo = new dtos.ProjectFetchOptions();
+					fo.withSpace();
+					fo.withExperiments();
+					fo.withRegistrator();
+					fo.withModifier();
+					fo.withLeader();
+					fo.withAttachments().withContent();
+					return fo;
+				};
+
+				this.createExperimentFetchOptions = function() {
+					var fo = new dtos.ExperimentFetchOptions();
+					fo.withType();
+					fo.withProject().withSpace();
+					fo.withDataSets();
+					fo.withSamples();
+					fo.withHistory();
+					fo.withProperties();
+					fo.withMaterialProperties();
+					fo.withTags();
+					fo.withRegistrator();
+					fo.withModifier();
+					fo.withAttachments().withContent();
+					return fo;
+				};
+
+				this.createExperimentTypeFetchOptions = function() {
+					var fo = new dtos.ExperimentTypeFetchOptions();
+					fo.withPropertyAssignments().withPropertyType();
+					fo.withPropertyAssignments().withRegistrator();
+					return fo;
+				};
+
+				this.createSampleFetchOptions = function() {
+					var fo = new dtos.SampleFetchOptions();
+					fo.withType();
+					fo.withExperiment().withProject().withSpace();
+					fo.withSpace();
+					fo.withProperties();
+					fo.withMaterialProperties();
+					fo.withParents();
+					fo.withChildren();
+					fo.withContainer();
+					fo.withComponents();
+					fo.withDataSets();
+					fo.withHistory();
+					fo.withTags();
+					fo.withRegistrator();
+					fo.withModifier();
+					fo.withAttachments().withContent();
+					fo.withChildrenUsing(fo);
+					return fo;
+				};
+
+				this.createSampleTypeFetchOptions = function() {
+					var fo = new dtos.SampleTypeFetchOptions();
+					fo.withPropertyAssignments().withPropertyType();
+					fo.withPropertyAssignments().withRegistrator();
+					return fo;
+				};
+
+				this.createDataSetFetchOptions = function() {
+					var fo = new dtos.DataSetFetchOptions();
+					fo.withType();
+					fo.withExperiment().withProject().withSpace();
+					fo.withSample();
+					fo.withProperties();
+					fo.withMaterialProperties();
+					fo.withParents();
+					fo.withChildren();
+					fo.withContainers();
+					fo.withComponents();
+					fo.withPhysicalData().withFileFormatType();
+					fo.withPhysicalData().withLocatorType();
+					fo.withPhysicalData().withStorageFormat();
+					fo.withLinkedData().withExternalDms();
+					fo.withHistory();
+					fo.withTags();
+					fo.withRegistrator();
+					fo.withModifier();
+					return fo;
+				};
+
+				this.createDataSetTypeFetchOptions = function() {
+					var fo = new dtos.DataSetTypeFetchOptions();
+					fo.withPropertyAssignments().withPropertyType();
+					fo.withPropertyAssignments().withRegistrator();
+					return fo;
+				};
+
+				this.createMaterialFetchOptions = function() {
+					var fo = new dtos.MaterialFetchOptions();
+					fo.withType();
+					fo.withHistory();
+					fo.withRegistrator();
+					fo.withProperties();
+					fo.withMaterialProperties();
+					fo.withTags();
+					return fo;
+				};
+
+				this.createMaterialTypeFetchOptions = function() {
+					var fo = new dtos.MaterialTypeFetchOptions();
+					fo.withPropertyAssignments().withPropertyType();
+					fo.withPropertyAssignments().withRegistrator();
+					return fo;
+				};
+
+				this.createPropertyTypeFetchOptions = function() {
+					var fo = new dtos.PropertyTyüeFetchOptions();
+					fo.withVocabulary();
+					fo.withMaterialType();
+					return fo;
+				};
+
+				this.createPluginFetchOptions = function() {
+					var fo = new dtos.PluginFetchOptions();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createVocabularyFetchOptions = function() {
+					var fo = new dtos.VocabularyFetchOptions();
+					fo.withTerms();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createVocabularyTermFetchOptions = function() {
+					var fo = new dtos.VocabularyTermFetchOptions();
+					fo.withVocabulary();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createGlobalSearchObjectFetchOptions = function() {
+					var fo = new dtos.GlobalSearchObjectFetchOptions();
+					fo.withExperiment();
+					fo.withSample();
+					fo.withDataSet();
+					fo.withMaterial();
+					return fo;
+				};
+
+				this.createObjectKindModificationFetchOptions = function() {
+					var fo = new dtos.ObjectKindModificationFetchOptions();
+					return fo;
+				};
+
+				this.createTagFetchOptions = function() {
+					var fo = new dtos.TagFetchOptions();
+					fo.withExperiments();
+					fo.withSamples();
+					fo.withDataSets();
+					fo.withMaterials();
+					return fo;
+				};
+
+				this.createAuthorizationGroupFetchOptions = function() {
+					var fo = new dtos.AuthorizationGroupFetchOptions();
+					fo.withRegistrator();
+					fo.withUsers();
+					var rafo = fo.withRoleAssignments();
+					rafo.withSpace();
+					rafo.withProject().withSpace();
+					return fo;
+				};
+
+				this.createRoleAssignmentFetchOptions = function() {
+					var fo = new dtos.RoleAssignmentFetchOptions();
+					fo.withProject();
+					fo.withSpace();
+					fo.withUser();
+					fo.withAuthorizationGroup();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createPersonFetchOptions = function() {
+					var fo = new dtos.PersonFetchOptions();
+					fo.withSpace();
+					fo.withRoleAssignments().withSpace();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createPropertyTypeFetchOptions = function() {
+					var fo = new dtos.PropertyTypeFetchOptions();
+					fo.withVocabulary();
+					fo.withMaterialType();
+					fo.withSemanticAnnotations();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createPropertyAssignmentFetchOptions = function() {
+					var fo = new dtos.PropertyAssignmentFetchOptions();
+					fo.withEntityType();
+					fo.withPropertyType();
+					fo.withSemanticAnnotations();
+					fo.withRegistrator();
+					return fo;
+				};
+
+				this.createSemanticAnnotationFetchOptions = function() {
+					var fo = new dtos.SemanticAnnotationFetchOptions();
+					fo.withEntityType();
+					fo.withPropertyType();
+					fo.withPropertyAssignment().withEntityType();
+					fo.withPropertyAssignment().withPropertyType();
+					return fo;
+				};
+
+				this.createExternalDmsFetchOptions = function() {
+					var fo = new dtos.ExternalDmsFetchOptions();
+					return fo;
+				};
+
+				this.createOperationExecutionFetchOptions = function() {
+					var fo = new dtos.OperationExecutionFetchOptions();
+					fo.withOwner().withSpace();
+					fo.withOwner().withRegistrator();
+					fo.withNotification();
+					fo.withSummary().withOperations();
+					fo.withSummary().withProgress();
+					fo.withSummary().withError();
+					fo.withSummary().withResults();
+					fo.withDetails().withOperations();
+					fo.withDetails().withProgress();
+					fo.withDetails().withError();
+					fo.withDetails().withResults();
+					return fo;
+				};
+
+				this.createDataStoreFetchOptions = function() {
+					var fo = new dtos.DataStoreFetchOptions();
+					return fo;
+				};
+
+				this.createDataSetFileFetchOptions = function() {
+					var fo = new dtos.DataSetFileFetchOptions();
+					return fo;
+				};
+
+				this.assertNull = function(actual, msg) {
+					this.assertEqual(actual, null, msg)
+				};
+
+				this.assertNotNull = function(actual, msg) {
+					this.assertNotEqual(actual, null, msg);
+				};
+
+				this.assertTrue = function(actual, msg) {
+					this.assertEqual(actual, true, msg);
+				};
+
+				this.assertFalse = function(actual, msg) {
+					this.assertEqual(actual, false, msg);
+				};
+
+				this.assertContains = function(actual, expected, msg) {
+					actual = actual ? actual : "";
+					this.assertTrue(actual.indexOf(expected) >= 0, msg);
+				};
+
+				this.assertEqual = function(actual, expected, msg) {
+					this.assert.equal(actual, expected, msg);
+				};
+
+				this.assertNotEqual = function(actual, expected, msg) {
+					this.assert.notEqual(actual, expected, msg);
+				};
+
+				this.assertDate = function(millis, msg, year, month, day, hour,
+						minute) {
+					var date = new Date(millis);
+					var actual = "";
+					var expected = "";
+
+					if (year) {
+						actual += date.getUTCFullYear();
+						expected += year;
+					}
+					if (month) {
+						actual += "-" + (date.getUTCMonth() + 1);
+						expected += "-" + month;
+					}
+					if (day) {
+						actual += "-" + date.getUTCDate();
+						expected += "-" + day;
+					}
+					if (hour) {
+						actual += " " + date.getUTCHours();
+						expected += " " + hour;
+					}
+					if (minute) {
+						actual += ":" + date.getUTCMinutes();
+						expected += ":" + minute;
+					}
 
-			if ($.isArray(objects)) {
-				theObjects = objects;
-			} else {
-				theObjects = [ objects ];
-			}
+					this.assertEqual(actual, expected, msg);
+				};
+
+				this.assertToday = function(millis, msg) {
+					var today = new Date();
+					this.assertDate(millis, msg, today.getUTCFullYear(), today
+							.getUTCMonth() + 1, today.getUTCDate());
+				};
+
+				this.assertObjectsCount = function(objects, count) {
+					this.assertEqual(objects.length, count, 'Got ' + count
+							+ ' object(s)');
+				};
+
+				this.assertObjectsWithValues = function(objects, propertyName,
+						propertyValues) {
+					var thisCommon = this;
+					var values = {};
+
+					$.each(objects, function(index, object) {
+						var value = thisCommon.getObjectProperty(object,
+								propertyName);
+						if (value in values == false) {
+							values[value] = true;
+						}
+					});
 
-			var theAccessor = null;
+					this.assert.deepEqual(Object.keys(values).sort(),
+							propertyValues.sort(), 'Objects have correct '
+									+ propertyName + ' values')
+				};
 
-			if ($.isFunction(accessor)) {
-				theAccessor = accessor;
-			} else {
-				theAccessor = function(object) {
-					return object[accessor];
-				}
-			}
-
-			checker(theObjects, theAccessor);
-		};
-
-		this.assertObjectsWithCollections = function(objects, accessor) {
-			var thisCommon = this;
-			this.assertObjectsWithOrWithoutCollections(objects, accessor, function(objects, accessor) {
-				thisCommon.assert.ok(objects.some(function(object) {
-					var value = accessor(object);
-					return value && Object.keys(value).length > 0;
-				}), 'Objects have non-empty collections accessed via: ' + accessor);
-			});
-		};
-
-		this.assertObjectsWithoutCollections = function(objects, accessor) {
-			var thisCommon = this;
-			this.assertObjectsWithOrWithoutCollections(objects, accessor, function(objects, accessor) {
-				thisCommon.assert.ok(objects.every(function(object) {
-					var value = accessor(object);
-					return !value || Object.keys(value).length == 0;
-				}), 'Objects have empty collections accessed via: ' + accessor);
-			});
-		};
-
-		this.shallowEqual = function(actual, expected, message) {
-			function oneWay(from, to) {
-				var isBad = _.chain(_.keys(from)).filter(function(k) {
-					return !_.isFunction(from[k]) && !_.isArray(from[k]) && !_.isObject(from[k]) && !_.isFunction(to[k]) && !_.isArray(to[k]) && !_.isObject(to[k]);
-				}).any(function(k) {
-					if (from[k] !== to[k]) {
-						return true;
-					}
-				}).value();
+				this.assertObjectsWithOrWithoutCollections = function(objects,
+						accessor, checker) {
+					var theObjects = null;
 
-				if (isBad) {
-					assert.propEqual(actual, expected);
-				}
-			}
+					if ($.isArray(objects)) {
+						theObjects = objects;
+					} else {
+						theObjects = [ objects ];
+					}
 
-			oneWay(actual, expected);
-			oneWay(expected, actual);
-		};
+					var theAccessor = null;
 
-		this.start = function() {
-			this.done = this.assert.async();
-		};
+					if ($.isFunction(accessor)) {
+						theAccessor = accessor;
+					} else {
+						theAccessor = function(object) {
+							return object[accessor];
+						}
+					}
 
-		this.finish = function() {
-			if (this.done) {
-				this.done();
-			}
-		};
+					checker(theObjects, theAccessor);
+				};
+
+				this.assertObjectsWithCollections = function(objects, accessor) {
+					var thisCommon = this;
+					this.assertObjectsWithOrWithoutCollections(objects,
+							accessor, function(objects, accessor) {
+								thisCommon.assert.ok(objects.some(function(
+										object) {
+									var value = accessor(object);
+									return value
+											&& Object.keys(value).length > 0;
+								}),
+										'Objects have non-empty collections accessed via: '
+												+ accessor);
+							});
+				};
+
+				this.assertObjectsWithoutCollections = function(objects,
+						accessor) {
+					var thisCommon = this;
+					this.assertObjectsWithOrWithoutCollections(objects,
+							accessor, function(objects, accessor) {
+								thisCommon.assert.ok(objects.every(function(
+										object) {
+									var value = accessor(object);
+									return !value
+											|| Object.keys(value).length == 0;
+								}),
+										'Objects have empty collections accessed via: '
+												+ accessor);
+							});
+				};
+
+				this.shallowEqual = function(actual, expected, message) {
+					function oneWay(from, to) {
+						var isBad = _.chain(_.keys(from)).filter(
+								function(k) {
+									return !_.isFunction(from[k])
+											&& !_.isArray(from[k])
+											&& !_.isObject(from[k])
+											&& !_.isFunction(to[k])
+											&& !_.isArray(to[k])
+											&& !_.isObject(to[k]);
+								}).any(function(k) {
+							if (from[k] !== to[k]) {
+								return true;
+							}
+						}).value();
+
+						if (isBad) {
+							assert.propEqual(actual, expected);
+						}
+					}
 
-		this.ok = function(msg) {
-			this.assert.ok(true, msg);
-		};
+					oneWay(actual, expected);
+					oneWay(expected, actual);
+				};
 
-		this.section = function(msg) {
-			this.assert.ok(true, "******************************************************");
-			this.assert.ok(true, msg);
-			this.assert.ok(true, "******************************************************");
-		};
+				this.start = function() {
+					this.done = this.assert.async();
+				};
 
-		this.fail = function(msg) {
-			this.assert.ok(false, msg);
-		};
+				this.finish = function() {
+					if (this.done) {
+						this.done();
+					}
+				};
+
+				this.ok = function(msg) {
+					this.assert.ok(true, msg);
+				};
+
+				this.section = function(msg) {
+					this.assert
+							.ok(true,
+									"******************************************************");
+					this.assert.ok(true, msg);
+					this.assert
+							.ok(true,
+									"******************************************************");
+				};
+
+				this.fail = function(msg) {
+					this.assert.ok(false, msg);
+				};
 
-	};
+			};
 
-	return Common;
-})
+			return Common;
+		})
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
index 46f96ac1a90af5d0ecb8c6a4141b500ad1c64143..34005cf93fa39f980b423c4cdbd44187cae2ba3e 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
@@ -472,7 +472,20 @@ var sources = [
 	'as/dto/roleassignment/RoleLevel',
 	'as/dto/roleassignment/RoleAssignment',
 	
+	'as/dto/plugin/create/PluginCreation',
+	'as/dto/plugin/create/CreatePluginsOperation',
+	'as/dto/plugin/create/CreatePluginsOperationResult',
+	'as/dto/plugin/fetchoptions/PluginFetchOptions',
+	'as/dto/plugin/fetchoptions/PluginSortOptions',
+	'as/dto/plugin/get/GetPluginsOperation',
+	'as/dto/plugin/get/GetPluginsOperationResult',
 	'as/dto/plugin/id/PluginPermId',
+	'as/dto/plugin/update/PluginUpdate',
+	'as/dto/plugin/update/UpdatePluginsOperation',
+	'as/dto/plugin/update/UpdatePluginsOperationResult',
+	'as/dto/plugin/Plugin',
+	'as/dto/plugin/PluginType',
+	'as/dto/plugin/ScriptType',
 	
 	'as/dto/sample/create/SampleCreation',
 	'as/dto/sample/create/CreateSamplesOperation',
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js
index cdf49ef76ee3359b1f6662bb0a94e7a2c2063a2c..153657ec4a9c8b965a22bc47674c5ff387415ff9 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js
@@ -133,10 +133,14 @@ define([ 'jquery', 'openbis', 'test/common' ], function($, openbis, common) {
 			return this._executeCreateOperation(new c.CreatePropertyTypesOperation(creations));
 		}
 		
+		this.createPlugins = function(creations) {
+			return this._executeCreateOperation(new c.CreatePluginsOperation(creations));
+		}
+
 		this.createVocabularyTerms = function(creations) {
 			return this._executeCreateOperation(new c.CreateVocabularyTermsOperation(creations));
 		}
-
+		
 		this.createVocabularies = function(creations) {
 			return this._executeCreateOperation(new c.CreateVocabulariesOperation(creations));
 		}
@@ -209,6 +213,10 @@ define([ 'jquery', 'openbis', 'test/common' ], function($, openbis, common) {
 			return this._executeUpdateOperation(new c.UpdatePropertyTypesOperation(updates));
 		}
 		
+		this.updatePlugins = function(updates) {
+			return this._executeUpdateOperation(new c.UpdatePluginsOperation(updates));
+		}
+		
 		this.updateVocabularyTerms = function(updates) {
 			return this._executeUpdateOperation(new c.UpdateVocabularyTermsOperation(updates));
 		}
@@ -265,10 +273,14 @@ define([ 'jquery', 'openbis', 'test/common' ], function($, openbis, common) {
 			return this._executeGetOperation(new c.GetPropertyTypesOperation(ids, fetchOptions));
 		}
 		
+		this.getPlugins = function(ids, fetchOptions) {
+			return this._executeGetOperation(new c.GetPluginsOperation(ids, fetchOptions));
+		}
+
 		this.getVocabularies = function(ids, fetchOptions) {
 			return this._executeGetOperation(new c.GetVocabulariesOperation(ids, fetchOptions));
 		}
-
+		
 		this.getVocabularyTerms = function(ids, fetchOptions) {
 			return this._executeGetOperation(new c.GetVocabularyTermsOperation(ids, fetchOptions));
 		}
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
index fb9a6faa84a93f856b8830718fa9156412608f1a..8ed2989b2e1144976e6cc5d38f585b2db9edd368 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
@@ -475,6 +475,33 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			testCreate(c, fCreate, c.findPropertyType, fCheck);
 		});
 
+		QUnit.test("createPlugins()", function(assert) {
+			var c = new common(assert, openbis);
+			var name = c.generateId("PLUGIN");
+			
+			var fCreate = function(facade) {
+				var creation = new c.PluginCreation();
+				creation.setName(name);
+				creation.setDescription("hello");
+				creation.setPluginType(c.PluginType.JYTHON);
+				creation.setScriptType(c.ScriptType.ENTITY_VALIDATION);
+				creation.setScript("def a():\n  pass");
+				return facade.createPlugins([ creation ]);
+			}
+			
+			var fCheck = function(plugin) {
+				c.assertEqual(plugin.getName(), name, "Name");
+				c.assertEqual(plugin.getPermId().getPermId(), name, "Perm id");
+				c.assertEqual(plugin.getDescription(), "hello", "Description");
+				c.assertEqual(plugin.getPluginType(), c.PluginType.JYTHON, "Plugin type");
+				c.assertEqual(plugin.getScriptType(), c.ScriptType.ENTITY_VALIDATION, "Script type");
+				c.assertEqual(plugin.getScript(), "def a():\n  pass", "Script");
+				c.assertEqual(plugin.isAvailable(), true, "Available?");
+			}
+			
+			testCreate(c, fCreate, c.findPlugin, fCheck);
+		});
+		
 		QUnit.test("createVocabularies()", function(assert) {
 			var c = new common(assert, openbis);
 			var code = c.generateId("VOCABULARY");
@@ -633,7 +660,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			var fCreate = function(facade) {
 				var personCreation = new c.PersonCreation();
 				personCreation.setUserId(userId);
-				personCreation.setHomeSpaceId(new c.SpacePermId("TEST"))
+				personCreation.setSpaceId(new c.SpacePermId("TEST"))
 				return facade.createPersons([ personCreation ]);
 			}
 
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-delete.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-delete.js
index 966b1117d6d3b8e65cdf45f26881cb744971416a..2e7bfcc708fe5fb6da4c3a31ac8907cd3aa84c4f 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-delete.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-delete.js
@@ -165,6 +165,52 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			testDeleteWithTrashAndConfirm(c, c.createDataSet, c.findDataSet, c.deleteDataSet);
 		});
 
+		QUnit.test("deleteDataSets() with disallowed type without force flag", function(assert) {
+			var c = new common(assert, openbis);
+
+			c.start();
+
+			c.createFacadeAndLogin().then(function(facade) {
+				return c.createDataSet(facade, "UNKNOWN").then(function(permId) {
+					c.assertNotNull(permId, "Entity was created");
+					return c.deleteDataSet(facade, permId).then(function(deletionId) {
+						c.assertNotNull(deletionId, "Entity was moved to trash");
+						return facade.confirmDeletions([ deletionId ]).then(function() {
+							c.fail("Confirmation of deletion should fail without the force flag");
+							c.finish();
+						});
+					});
+				});
+			}).fail(function(error) {
+				c.assertContains(error.message, "Deletion failed because the following data sets have 'Disallow deletion' flag set to true in their type", "Expected error message");
+				c.finish();
+			});
+		});
+		
+		QUnit.test("deleteDataSets() with disallowed type with force flag", function(assert) {
+			var c = new common(assert, openbis);
+
+			c.start();
+
+			c.createFacadeAndLogin().then(function(facade) {
+				return c.createDataSet(facade, "UNKNOWN").then(function(permId) {
+					c.assertNotNull(permId, "Entity was created");
+					return c.deleteDataSet(facade, permId).then(function(deletionId) {
+						c.assertNotNull(deletionId, "Entity was moved to trash");
+						var operation = new c.ConfirmDeletionsOperation([ deletionId ]);
+						operation.setForceDeletion(true);
+						var options = new c.SynchronousOperationExecutionOptions();
+						return facade.executeOperations([ operation ], options).then(function() {
+							c.finish();
+						});
+					});
+				});
+			}).fail(function(error) {
+				c.fail("Confirmation of deletion should not fail with the force flag");
+				c.finish();
+			});
+		});
+
 		QUnit.test("deleteMaterials()", function(assert) {
 			var c = new common(assert, openbis);
 			testDeleteWithoutTrash(c, c.createMaterial, c.findMaterial, c.deleteMaterial);
@@ -184,12 +230,12 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			var c = new common(assert, openbis);
 			testDeleteWithoutTrash(c, c.createVocabularyTerm, c.findVocabularyTerm, c.deleteVocabularyTerm);
 		});
-		
+
 		QUnit.test("deleteEntityTypes()", function(assert) {
 			var c = new common(assert, openbis);
 			testDeleteWithoutTrash(c, c.createSampleType, c.findSampleType, c.deleteEntityType);
 		});
-		
+
 		QUnit.test("deleteExternalDms()", function(assert) {
 			var c = new common(assert, openbis);
 			testDeleteWithoutTrash(c, c.createExternalDms, c.findExternalDms, c.deleteExternalDms);
@@ -209,12 +255,12 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			var c = new common(assert, openbis);
 			testDeleteWithoutTrash(c, c.createAuthorizationGroup, c.findAuthorizationGroup, c.deleteAuthorizationGroup);
 		});
-		
+
 		QUnit.test("deleteRoleAssignments()", function(assert) {
 			var c = new common(assert, openbis);
 			testDeleteWithoutTrash(c, c.createRoleAssignment, c.findRoleAssignment, c.deleteRoleAssignment);
 		});
-		
+
 		QUnit.test("deleteOperationExecutions()", function(assert) {
 			var c = new common(assert, openbis);
 
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-get.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-get.js
index 7eb31d85fa634390f7fb62eb7d26c3812d818067..2e63c3092a35e2061ef9c966111e9f43233bc003 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-get.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-get.js
@@ -297,6 +297,30 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			testGet(c, fCreate, fGet, fGetEmptyFetchOptions, fechOptionsTestConfig);
 		});
 		
+		QUnit.test("getPlugins()", function(assert) {
+			var c = new common(assert, openbis);
+			var fo = new c.PluginFetchOptions();
+			var fechOptionsTestConfig = getConfigForFetchOptions(fo);
+			fechOptionsTestConfig.SortBy = null;
+			
+			var fCreate = function(facade) {
+				return $.when(c.createPlugin(facade), c.createPlugin(facade)).then(function(permId1, permId2) {
+					return [ permId1, permId2 ];
+				});
+			}
+			
+			var fGet = function(facade, permIds) {
+				testFetchOptionsAssignation(c, fo, fechOptionsTestConfig);
+				return facade.getPlugins(permIds, fo);
+			}
+			
+			var fGetEmptyFetchOptions = function(facade, permIds) {
+				return facade.getPlugins(permIds, new c.PluginFetchOptions());
+			}
+			
+			testGet(c, fCreate, fGet, fGetEmptyFetchOptions, fechOptionsTestConfig);
+		});
+
 		QUnit.test("getVocabularies()", function(assert) {
 			var c = new common(assert, openbis);
 			var fo = new c.VocabularyFetchOptions();
@@ -320,7 +344,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			
 			testGet(c, fCreate, fGet, fGetEmptyFetchOptions, fechOptionsTestConfig);
 		});
-
+		
 		QUnit.test("getVocabularyTerms()", function(assert) {
 			var c = new common(assert, openbis);
 			var fo = new c.VocabularyTermFetchOptions();
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js
index c5858bbff746e2e2078548da1fbd458f5f30e29f..2df11840c5bb0a2cada5a47c850908e0f08906c0 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-search.js
@@ -1281,7 +1281,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 
 			testSearch(c, fSearch, fCheck);
 		});
-		
+
 		QUnit.test("searchExternalDms()", function(assert) {
 			var c = new common(assert, openbis);
 
@@ -1327,7 +1327,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 		QUnit.test("searchAuthorizationGroups()", function(assert) {
 			var c = new common(assert, openbis);
 			var code;
-			
+
 			var fSearch = function(facade) {
 				return c.createAuthorizationGroup(facade).then(function(permId) {
 					var criteria = new c.AuthorizationGroupSearchCriteria();
@@ -1336,7 +1336,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 					return facade.searchAuthorizationGroups(criteria, c.createAuthorizationGroupFetchOptions());
 				});
 			}
-			
+
 			var fCheck = function(facade, groups) {
 				c.assertEqual(groups.length, 1);
 				var group = groups[0];
@@ -1345,20 +1345,20 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				c.assertEqual(users[0].getUserId(), "power_user", "User");
 				c.assertEqual(users.length, 1, "# Users");
 			}
-			
+
 			testSearch(c, fSearch, fCheck);
 		});
-		
+
 		QUnit.test("searchAuthorizationGroups() existing with role assigments", function(assert) {
 			var c = new common(assert, openbis);
 			var code;
-			
+
 			var fSearch = function(facade) {
 				var criteria = new c.AuthorizationGroupSearchCriteria();
 				criteria.withCode().thatEquals("TEST-GROUP");
 				return facade.searchAuthorizationGroups(criteria, c.createAuthorizationGroupFetchOptions());
 			}
-			
+
 			var fCheck = function(facade, groups) {
 				c.assertEqual(groups.length, 1);
 				var group = groups[0];
@@ -1384,21 +1384,21 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				c.assertEqual(numberOfTestSpaceAssignments, 1, "Number of TEST space assignments");
 				c.assertEqual(numberOfProjectAssignments, 0, "Number of project assignments");
 			}
-			
+
 			testSearch(c, fSearch, fCheck);
 		});
-		
+
 		QUnit.test("searchRoleAssignments()", function(assert) {
 			var c = new common(assert, openbis);
 			var code;
-			
+
 			var fSearch = function(facade) {
 				var criteria = new c.RoleAssignmentSearchCriteria();
 				criteria.withSpace().withCode().thatEquals("TEST");
 				criteria.withUser().withUserId().thatEquals("observer");
 				return facade.searchRoleAssignments(criteria, c.createRoleAssignmentFetchOptions());
 			}
-			
+
 			var fCheck = function(facade, assignments) {
 				c.assertEqual(assignments.length, 1, "# Role Assignments");
 				var assignment = assignments[0];
@@ -1406,20 +1406,20 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				c.assertEqual(assignment.getRoleLevel(), "SPACE", "Role level");
 				c.assertEqual(assignment.getSpace().getCode(), "TEST", "Space");
 			}
-			
+
 			testSearch(c, fSearch, fCheck);
 		});
-		
+
 		QUnit.test("searchPersons()", function(assert) {
 			var c = new common(assert, openbis);
 			var code;
-			
+
 			var fSearch = function(facade) {
 				var criteria = new c.PersonSearchCriteria();
 				criteria.withUserId().thatContains("bser");
 				return facade.searchPersons(criteria, c.createPersonFetchOptions());
 			}
-			
+
 			var fCheck = function(facade, persons) {
 				c.assertEqual(persons.length, 1, "# persons");
 				var person = persons[0];
@@ -1432,10 +1432,10 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				c.assertEqual(assignment.getRoleLevel(), "SPACE", "Role level");
 				c.assertEqual(assignment.getSpace().getCode(), "TEST", "Space");
 			}
-			
+
 			testSearch(c, fSearch, fCheck);
 		});
-		
+
 		QUnit.test("searchOperationExecutions()", function(assert) {
 			var c = new common(assert, openbis);
 
@@ -1758,6 +1758,32 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			testSearch(c, fSearch, fCheck);
 		});
 
+		QUnit.test("searchDeletions()", function(assert) {
+			var c = new common(assert, openbis);
+
+			var fSearch = function(facade) {
+				return c.createSample(facade).then(function(permId) {
+					var options = new c.SampleDeletionOptions();
+					options.setReason("test reason");
+					return facade.deleteSamples([ permId ], options).then(function(deletionId) {
+						var criteria = new c.DeletionSearchCriteria();
+						criteria.withId().thatEquals(deletionId);
+						var fetchOptions = new c.DeletionFetchOptions();
+						return facade.searchDeletions(criteria, fetchOptions);
+					});
+				});
+			}
+
+			var fCheck = function(facade, deletions) {
+				c.assertEqual(deletions.length, 1);
+				var deletion = deletions[0];
+				c.assertEqual(deletion.getReason(), "test reason", "reason");
+				c.assertToday(deletion.getDeletionDate(), "deletion date");
+			}
+
+			testSearch(c, fSearch, fCheck);
+		});
+
 	}
 
 	return function() {
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js
index 94d3b4f57b0642bafa031431646977868a275afb..5944caa21191775cc12e50c990453a7c11c299e9 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js
@@ -737,6 +737,40 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			testUpdate(c, fCreate, fUpdate, c.findPropertyType, fCheck);
 		});
 		
+		QUnit.test("updatePlugins()", function(assert) {
+			var c = new common(assert, openbis);
+			var name = c.generateId("PLUGIN");
+			var description = "Description of " + name;
+			var script = "print 'hello'";
+			
+			var fCreate = function(facade) {
+				var creation = new c.PluginCreation();
+				creation.setName(name);
+				creation.setScript("pass");
+				creation.setDescription("old description");
+				creation.setAvailable(false);
+				creation.setPluginType(c.PluginType.JYTHON);
+				creation.setScriptType(c.ScriptType.MANAGED_PROPERTY);
+				return facade.createPlugins([ creation ]);
+			}
+			
+			var fUpdate = function(facade, permId) {
+				var update = new c.PluginUpdate();
+				update.setPluginId(new c.PluginPermId(name));
+				update.setDescription(description);
+				update.setScript(script);
+				return facade.updatePlugins([ update ]);
+			}
+			
+			var fCheck = function(plugin) {
+				c.assertEqual(plugin.getName(), name, "Name");
+				c.assertEqual(plugin.getDescription(), description, "Description");
+				c.assertEqual(plugin.getScript(), script, "Script");
+			}
+			
+			testUpdate(c, fCreate, fUpdate, c.findPlugin, fCheck);
+		});
+		
 		QUnit.test("updateVocabularies()", function(assert) {
 			var c = new common(assert, openbis);
 			var code = c.generateId("VOCABULARY");
@@ -908,7 +942,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			var fUpdate = function(facade, permId) {
 				var update = new c.PersonUpdate();
 				update.setUserId(permId);
-				update.setHomeSpaceId(new c.SpacePermId("TEST"))
+				update.setSpaceId(new c.SpacePermId("TEST"))
 				return facade.updatePersons([ update ]);
 			}
 			
diff --git a/js-test/servers/common/core-plugins/tests/1/dss/reporting-plugins/js-test/js-test.py b/js-test/servers/common/core-plugins/tests/1/dss/reporting-plugins/js-test/js-test.py
index a07397653fbaa8930cf4253bbf42853717151db1..fb2889e8d641e4cd29862cb441f7603bdfa3a55a 100644
--- a/js-test/servers/common/core-plugins/tests/1/dss/reporting-plugins/js-test/js-test.py
+++ b/js-test/servers/common/core-plugins/tests/1/dss/reporting-plugins/js-test/js-test.py
@@ -30,7 +30,12 @@ def process(tr, parameters, tableBuilder):
 	method = parameters.get("method");
 	if method is None:
 		sample = findSample(tr)
-		dataSet = createDataSet(tr, sample)
+
+		dataSetType = parameters.get("dataSetType")
+		if dataSetType is None:
+			dataSetType = "ALIGNMENT"
+
+		dataSet = createDataSet(tr, sample, dataSetType)
 		
 		tableBuilder.addHeader("DATA_SET_CODE")
 		row = tableBuilder.addRow()
@@ -53,8 +58,8 @@ def findSample(tr):
 	samples = tr.getSearchService().searchForSamples(criteria)
 	return samples[0]
 	
-def createDataSet(tr, sample):
-	dataSet = tr.createNewDataSet("ALIGNMENT")
+def createDataSet(tr, sample, dataSetType):
+	dataSet = tr.createNewDataSet(dataSetType)
 	dataSet.setSample(sample)
 	tr.createNewFile(dataSet, "test")
 	return dataSet
diff --git a/js-test/servers/common/openBIS-server/etc/capabilities b/js-test/servers/common/openBIS-server/etc/capabilities
new file mode 100644
index 0000000000000000000000000000000000000000..a4254a2bde2bee9e63b97203b731d718dd1aed99
--- /dev/null
+++ b/js-test/servers/common/openBIS-server/etc/capabilities
@@ -0,0 +1 @@
+CONFIRM_DELETION_FORCED: INSTANCE_ADMIN
\ No newline at end of file
diff --git a/openbis/etc/capabilities b/openbis/etc/capabilities
index 153742fb0c6022304950eac6331463b77d2d1d75..19b3c7f0290c390b0a0e244845bbd673cfa30fb4 100644
--- a/openbis/etc/capabilities
+++ b/openbis/etc/capabilities
@@ -1 +1,2 @@
-CREATE_PROJECT: SPACE_ADMIN, SPACE_ETL_SERVER
\ No newline at end of file
+CREATE_PROJECT: SPACE_ADMIN, SPACE_ETL_SERVER
+CONFIRM_DELETION_FORCED: INSTANCE_ADMIN
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
index 0e2efb9ef0921c9412c4f9e2eea2fa440734e003..947ee0ad0df7827679b2f68f9c4c7fdce48f9704 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
@@ -202,6 +202,17 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.search.SearchPersonsOpera
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.search.SearchPersonsOperationResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.update.PersonUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.update.UpdatePersonsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.CreatePluginsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.CreatePluginsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.PluginCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.get.GetPluginsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.get.GetPluginsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.PluginUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.UpdatePluginsOperation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create.CreateProjectsOperation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create.CreateProjectsOperationResult;
@@ -534,6 +545,13 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
         return result.getObjectIds();
     }
 
+    @Override
+    public List<PluginPermId> createPlugins(String sessionToken, List<PluginCreation> newPlugins)
+    {
+        CreatePluginsOperationResult result = executeOperation(sessionToken, new CreatePluginsOperation(newPlugins));
+        return result.getObjectIds();
+    }
+
     @Override
     @Transactional
     public List<VocabularyPermId> createVocabularies(String sessionToken, List<VocabularyCreation> creations)
@@ -668,6 +686,12 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
         executeOperation(sessionToken, new UpdatePropertyTypesOperation(propertyTypeUpdates));
     }
 
+    @Override
+    public void updatePlugins(String sessionToken, List<PluginUpdate> pluginUpdates)
+    {
+        executeOperation(sessionToken, new UpdatePluginsOperation(pluginUpdates));
+    }
+
     @Override
     public void updateVocabularies(String sessionToken, List<VocabularyUpdate> vocabularyUpdates)
     {
@@ -774,6 +798,14 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
         return result.getObjectMap();
     }
 
+    @Override
+    @Transactional(readOnly = true)
+    public Map<IPluginId, Plugin> getPlugins(String sessionToken, List<? extends IPluginId> pluginIds, PluginFetchOptions fetchOptions)
+    {
+        GetPluginsOperationResult result = executeOperation(sessionToken, new GetPluginsOperation(pluginIds, fetchOptions));
+        return result.getObjectMap();
+    }
+
     @Override
     @Transactional(readOnly = true)
     public Map<IVocabularyId, Vocabulary> getVocabularies(String sessionToken, List<? extends IVocabularyId> vocabularyIds,
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
index 5366296fc3e84e67c9442ee5c0a13f39b01fc420..5027b65b1974e6c27d4d98862122d31840ee300c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
@@ -110,6 +110,12 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.IPersonId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.PersonPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.search.PersonSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.update.PersonUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.PluginCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.PluginUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create.ProjectCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.delete.ProjectDeletionOptions;
@@ -320,6 +326,13 @@ public class ApplicationServerApiLogger extends AbstractServerLogger implements
         return null;
     }
 
+    @Override
+    public List<PluginPermId> createPlugins(String sessionToken, List<PluginCreation> newPlugins)
+    {
+        logAccess(sessionToken, "create-plugins", "NEW_PLUGINS(%s)", abbreviate(newPlugins));
+        return null;
+    }
+
     @Override
     public List<VocabularyPermId> createVocabularies(String sessionToken, List<VocabularyCreation> newVocabularies)
     {
@@ -444,6 +457,12 @@ public class ApplicationServerApiLogger extends AbstractServerLogger implements
         logAccess(sessionToken, "update-property_types", "PROPERTY_TYPE_UPDATES(%s)", abbreviate(propertyTypeUpdates));
     }
 
+    @Override
+    public void updatePlugins(String sessionToken, List<PluginUpdate> pluginUpdates)
+    {
+        logAccess(sessionToken, "update-plugins", "PLUGIN_UPDATES(%s)", abbreviate(pluginUpdates));
+    }
+
     @Override
     public void updateVocabularies(String sessionToken, List<VocabularyUpdate> vocabularyUpdates)
     {
@@ -531,6 +550,13 @@ public class ApplicationServerApiLogger extends AbstractServerLogger implements
         return null;
     }
 
+    @Override
+    public Map<IPluginId, Plugin> getPlugins(String sessionToken, List<? extends IPluginId> pluginIds, PluginFetchOptions fetchOptions)
+    {
+        logAccess(sessionToken, "get-plugins", "PLUGIN_IDS(%s) FETCH_OPTIONS(%s)", abbreviate(pluginIds), fetchOptions);
+        return null;
+    }
+
     @Override
     public Map<IVocabularyId, Vocabulary> getVocabularies(String sessionToken, List<? extends IVocabularyId> vocabularyIds,
             VocabularyFetchOptions fetchOptions)
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionExecutor.java
index 71705ac166c5cf7d34ca1fc894525000bd1eda8c..4e3f015b7bcdf4e4f910517df058aecd8dce05b7 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionExecutor.java
@@ -68,7 +68,7 @@ public class ConfirmDeletionExecutor implements IConfirmDeletionExecutor
     ICommonBusinessObjectFactory businessObjectFactory;
 
     @Override
-    public void confirm(IOperationContext context, List<? extends IDeletionId> deletionIds)
+    public void confirm(IOperationContext context, List<? extends IDeletionId> deletionIds, boolean forceDeletion)
     {
         if (context == null)
         {
@@ -95,7 +95,13 @@ public class ConfirmDeletionExecutor implements IConfirmDeletionExecutor
 
         try
         {
-            authorizationExecutor.canConfirm(context, deletionIdsWithoutNulls);
+            if (forceDeletion)
+            {
+                authorizationExecutor.canConfirmForced(context, deletionIdsWithoutNulls);
+            } else
+            {
+                authorizationExecutor.canConfirm(context, deletionIdsWithoutNulls);
+            }
         } catch (AuthorizationFailureException ex)
         {
             throw new UnauthorizedObjectAccessException(deletionIdsWithoutNulls);
@@ -135,7 +141,7 @@ public class ConfirmDeletionExecutor implements IConfirmDeletionExecutor
                 throw new ObjectNotFoundException(deletionId);
             }
 
-            deleteDataSets(context, deletion);
+            deleteDataSets(context, deletion, forceDeletion);
             deleteSamples(context, deletion);
             deleteExperiments(context, deletion);
 
@@ -146,7 +152,7 @@ public class ConfirmDeletionExecutor implements IConfirmDeletionExecutor
 
     }
 
-    private void deleteDataSets(IOperationContext context, DeletionPE deletion)
+    private void deleteDataSets(IOperationContext context, DeletionPE deletion, boolean forceDeletion)
     {
         IDeletionDAO deletionDAO = daoFactory.getDeletionDAO();
 
@@ -156,7 +162,7 @@ public class ConfirmDeletionExecutor implements IConfirmDeletionExecutor
         IDeletedDataSetTable deletedDataSetTable =
                 businessObjectFactory.createDeletedDataSetTable(context.getSession());
         deletedDataSetTable.loadByDataSetCodes(dataSetCodes);
-        deletedDataSetTable.permanentlyDeleteLoadedDataSets(deletion.getReason(), false);
+        deletedDataSetTable.permanentlyDeleteLoadedDataSets(deletion.getReason(), forceDeletion);
     }
 
     private void deleteSamples(IOperationContext context, DeletionPE deletion)
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionsOperationExecutor.java
index 7ad13bdc6461a2b61046bb430be7b8d679af7391..961cc60e7c9a64d0531482d6018e13cc3602756e 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionsOperationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/ConfirmDeletionsOperationExecutor.java
@@ -44,7 +44,7 @@ public class ConfirmDeletionsOperationExecutor extends OperationExecutor<Confirm
     @Override
     protected ConfirmDeletionsOperationResult doExecute(IOperationContext context, ConfirmDeletionsOperation operation)
     {
-        executor.confirm(context, operation.getDeletionIds());
+        executor.confirm(context, operation.getDeletionIds(), operation.isForceDeletion());
         return new ConfirmDeletionsOperationResult();
     }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/DeletionAuthorizationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/DeletionAuthorizationExecutor.java
index baf4e1cb6edaf08a7121266cc93c6db7fa4e0eaa..67a7b3c631be68092808aa126f704e57e25afdb1 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/DeletionAuthorizationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/DeletionAuthorizationExecutor.java
@@ -47,6 +47,15 @@ public class DeletionAuthorizationExecutor implements IDeletionAuthorizationExec
     {
     }
 
+    @Override
+    @DatabaseCreateOrDeleteModification(value = { ObjectKind.DELETION, ObjectKind.EXPERIMENT, ObjectKind.SAMPLE, ObjectKind.DATA_SET })
+    @RolesAllowed(RoleWithHierarchy.INSTANCE_DISABLED)
+    @Capability("CONFIRM_DELETION_FORCED")
+    public void canConfirmForced(IOperationContext context,
+            @AuthorizationGuard(guardClass = V3DeletionIdPredicate.class) List<? extends IDeletionId> ids)
+    {
+    }
+
     @Override
     @DatabaseCreateOrDeleteModification(value = ObjectKind.DELETION)
     @DatabaseUpdateModification(value = { ObjectKind.EXPERIMENT, ObjectKind.SAMPLE, ObjectKind.DATA_SET })
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IConfirmDeletionExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IConfirmDeletionExecutor.java
index 32caa4cdb69b3de7820e79e62b0e1b667ca41092..9ddc986fcc4812cc2dbcab70d5fa668a0f051b19 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IConfirmDeletionExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IConfirmDeletionExecutor.java
@@ -27,6 +27,6 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 public interface IConfirmDeletionExecutor
 {
 
-    public void confirm(IOperationContext context, List<? extends IDeletionId> deletionIds);
+    public void confirm(IOperationContext context, List<? extends IDeletionId> deletionIds, boolean forceDeletion);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IDeletionAuthorizationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IDeletionAuthorizationExecutor.java
index 24d394b90930092ed8a273b2b3359498855ba47a..5415820b36fedb74c3c6d077b2576f93c85cf2be 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IDeletionAuthorizationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/IDeletionAuthorizationExecutor.java
@@ -29,6 +29,8 @@ public interface IDeletionAuthorizationExecutor extends IObjectAuthorizationExec
 {
 
     void canConfirm(IOperationContext context, List<? extends IDeletionId> ids);
+    
+    void canConfirmForced(IOperationContext context, List<? extends IDeletionId> ids);
 
     void canRevert(IOperationContext context, List<? extends IDeletionId> ids);
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/SearchDeletionExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/SearchDeletionExecutor.java
index 9ba8ff47808d059a8513b80dfb7f94298a2eabeb..fb824727cb585c1104580410b1c777f501fa5637 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/SearchDeletionExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/deletion/SearchDeletionExecutor.java
@@ -25,9 +25,15 @@ import javax.annotation.Resource;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.IdSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.fetchoptions.DeletionFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.id.DeletionTechId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.search.DeletionSearchCriteria;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.search.AbstractSearchObjectManuallyExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.search.Matcher;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.search.SimpleFieldMatcher;
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IDeletionTable;
@@ -70,6 +76,11 @@ public class SearchDeletionExecutor implements ISearchDeletionExecutor
             deletions = listWithoutDeletedObjects(context);
         }
 
+        if (criteria.getCriteria() != null && false == criteria.getCriteria().isEmpty())
+        {
+            deletions = new SearchDeletions(deletions).search(context, criteria);
+        }
+
         if (deletions == null)
         {
             return Collections.emptyList();
@@ -101,4 +112,57 @@ public class SearchDeletionExecutor implements ISearchDeletionExecutor
         return deletionTable.getDeletions();
     }
 
+    private class SearchDeletions extends AbstractSearchObjectManuallyExecutor<DeletionSearchCriteria, Deletion>
+    {
+
+        private List<Deletion> allDeletions;
+
+        public SearchDeletions(List<Deletion> allDeletions)
+        {
+            this.allDeletions = allDeletions;
+        }
+
+        @Override
+        protected List<Deletion> listAll()
+        {
+            return allDeletions;
+        }
+
+        @Override
+        protected Matcher<Deletion> getMatcher(ISearchCriteria criteria)
+        {
+            if (criteria instanceof IdSearchCriteria<?>)
+            {
+                return new IdMatcher();
+            } else
+            {
+                throw new IllegalArgumentException("Unknown search criteria: " + criteria.getClass());
+            }
+        }
+
+        private class IdMatcher extends SimpleFieldMatcher<Deletion>
+        {
+
+            @Override
+            protected boolean isMatching(IOperationContext context, Deletion object, ISearchCriteria criteria)
+            {
+                Object id = ((IdSearchCriteria<?>) criteria).getId();
+
+                if (id == null)
+                {
+                    return true;
+                } else if (id instanceof DeletionTechId)
+                {
+                    DeletionTechId techId = (DeletionTechId) id;
+                    return object.getId().equals(techId.getTechId());
+                } else
+                {
+                    throw new IllegalArgumentException("Unknown id: " + id.getClass());
+                }
+            }
+
+        }
+
+    }
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java
index 4c512c9b56fd12095ba5e21301b5b52b4a2b3ba4..8a4121b95d49528147c9d0a00cd3a0d0a4eb11af 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java
@@ -82,6 +82,9 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.person.ICreatePerson
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.person.IGetPersonsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.person.ISearchPersonsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.person.IUpdatePersonsOperationExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin.ICreatePluginsOperationExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin.IGetPluginsOperationExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin.IUpdatePluginsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.project.ICreateProjectsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.project.IDeleteProjectsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.project.IGetProjectsOperationExecutor;
@@ -230,6 +233,9 @@ public class OperationsExecutor implements IOperationsExecutor
     @Autowired
     private ICreatePropertyTypesOperationExecutor createPropertyTypesExecutor;
     
+    @Autowired
+    private ICreatePluginsOperationExecutor createPluginsExecutor;
+    
     @Autowired
     private ICreateVocabulariesOperationExecutor createVocabulariesExecutor;
     
@@ -297,8 +303,11 @@ public class OperationsExecutor implements IOperationsExecutor
     private IUpdatePropertyTypesOperationExecutor updatePropertyTypesExecutor;
 
     @Autowired
-    private IUpdateVocabulariesOperationExecutor updateVocabulariesExecutor;
+    private IUpdatePluginsOperationExecutor updatePluginsExecutor;
 
+    @Autowired
+    private IUpdateVocabulariesOperationExecutor updateVocabulariesExecutor;
+    
     @Autowired
     private IUpdateVocabularyTermsOperationExecutor updateVocabularyTermsExecutor;
     
@@ -356,6 +365,9 @@ public class OperationsExecutor implements IOperationsExecutor
     @Autowired
     private IGetPropertyTypesOperationExecutor getPropertyTypesExecutor;
 
+    @Autowired
+    private IGetPluginsOperationExecutor getPluginsExecutor;
+    
     @Autowired
     private IGetVocabulariesOperationExecutor getVocabulariesExecutor;
     
@@ -560,6 +572,7 @@ public class OperationsExecutor implements IOperationsExecutor
         resultMap.putAll(getRoleAssignmentsExecutor.execute(context, operations));
         resultMap.putAll(getPersonsExecutor.execute(context, operations));
         resultMap.putAll(getPropertyTypesExecutor.execute(context, operations));
+        resultMap.putAll(getPluginsExecutor.execute(context, operations));
         resultMap.putAll(getVocabulariesExecutor.execute(context, operations));
         resultMap.putAll(getVocabularyTermsExecutor.execute(context, operations));
         resultMap.putAll(getExternalDmsExecutor.execute(context, operations));
@@ -581,6 +594,7 @@ public class OperationsExecutor implements IOperationsExecutor
     {
         resultMap.putAll(updateSemanticAnnotationsExecutor.execute(context, operations));
         resultMap.putAll(updateOperationExecutionsExecutor.execute(context, operations));
+        resultMap.putAll(updatePluginsExecutor.execute(context, operations));
         resultMap.putAll(updateVocabulariesExecutor.execute(context, operations));
         resultMap.putAll(updatePropertyTypesExecutor.execute(context, operations));
         resultMap.putAll(updateVocabularyTermsExecutor.execute(context, operations));
@@ -604,6 +618,7 @@ public class OperationsExecutor implements IOperationsExecutor
             Map<IOperation, IOperationResult> resultMap, IOperationContext context)
     {
         resultMap.putAll(createPersonsExecutor.execute(context, operations));
+        resultMap.putAll(createPluginsExecutor.execute(context, operations));
         resultMap.putAll(createVocabulariesExecutor.execute(context, operations));
         resultMap.putAll(createVocabularyTermsExecutor.execute(context, operations));
         resultMap.putAll(createPropertyTypesExecutor.execute(context, operations));
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/SetPersonSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/SetPersonSpaceExecutor.java
index 61730e511b14e9a58f1275c8c6d35e64026ec7a4..ebafca8ce924352bfa2cd90fe19fd951e5a925e8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/SetPersonSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/SetPersonSpaceExecutor.java
@@ -50,7 +50,7 @@ public class SetPersonSpaceExecutor
     @Override
     protected ISpaceId getRelatedId(PersonCreation creation)
     {
-        return creation.getHomeSpaceId();
+        return creation.getSpaceId();
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/UpdateHomeSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/UpdateHomeSpaceExecutor.java
index 3d4220b7426440641e970e81ed8db6d23eaecab1..a436855d4416dfa480312823e9fcd630111748e2 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/UpdateHomeSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/person/UpdateHomeSpaceExecutor.java
@@ -69,7 +69,7 @@ public class UpdateHomeSpaceExecutor
     @Override
     protected FieldUpdateValue<ISpaceId> getRelatedUpdate(PersonUpdate update)
     {
-        return update.getHomeSpaceId();
+        return update.getSpaceId();
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/CreatePluginExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/CreatePluginExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5bac1a41f266a5daa73f584d7b0b688e3078ebb
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/CreatePluginExecutor.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.PluginType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.ScriptType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.PluginCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CreateProgress;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.server.util.PluginUtils;
+import ch.systemsx.cisd.openbis.generic.shared.IJythonEvaluatorPool;
+import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class CreatePluginExecutor
+        extends AbstractCreateEntityExecutor<PluginCreation, ScriptPE, PluginPermId>
+        implements ICreatePluginExecutor
+{
+    @Resource(name = ComponentNames.JYTHON_EVALUATOR_POOL)
+    private IJythonEvaluatorPool evaluatorPool;
+    
+    @Autowired
+    private IDAOFactory daoFactory;
+    
+    @Autowired
+    private IPluginAuthorizationExecutor authorizationExecutor;
+
+    @Override
+    protected IObjectId getId(ScriptPE entity)
+    {
+        return new PluginPermId(entity.getName());
+    }
+
+    @Override
+    protected PluginPermId createPermId(IOperationContext context, ScriptPE entity)
+    {
+        return new PluginPermId(entity.getName());
+    }
+    
+    @Override
+    protected void checkData(IOperationContext context, PluginCreation creation)
+    {
+        if (StringUtils.isEmpty(creation.getName()))
+        {
+            throw new UserFailureException("Name cannot be empty.");
+        }
+        if (creation.getScriptType() == null)
+        {
+            throw new UserFailureException("Script type cannot be unspecified.");
+        }
+        if (creation.getPluginType() == null)
+        {
+            throw new UserFailureException("Plugin type cannot be unspecified.");
+        }
+        if (creation.getPluginType() == PluginType.JYTHON && creation.getScript() == null)
+        {
+            throw new UserFailureException("Script unspecified for plugin of type " + PluginType.JYTHON + ".");
+        }
+        if (creation.getPluginType() == PluginType.PREDEPLOYED && creation.getScript() != null)
+        {
+            throw new UserFailureException("Script specified for plugin of type " + PluginType.PREDEPLOYED + ".");
+        }
+    }
+
+    @Override
+    protected void checkAccess(IOperationContext context)
+    {
+        authorizationExecutor.canCreate(context);
+    }
+
+    @Override
+    protected void checkAccess(IOperationContext context, ScriptPE entity)
+    {
+    }
+
+    @Override
+    protected List<ScriptPE> createEntities(IOperationContext context, CollectionBatch<PluginCreation> batch)
+    {
+        List<ScriptPE> scripts = new ArrayList<>();
+        PersonPE person = context.getSession().tryGetPerson();
+        new CollectionBatchProcessor<PluginCreation>(context, batch)
+        {
+            @Override
+            public void process(PluginCreation creation)
+            {
+                ScriptPE script = new ScriptPE();
+                script.setName(creation.getName());
+                script.setDescription(creation.getDescription());
+                EntityKind entityKind = creation.getEntityKind();
+                if (entityKind != null)
+                {
+                    script.setEntityKind(translate(entityKind));
+                }
+                if (creation.getScriptType() != null)
+                {
+                    script.setScriptType(translate(creation.getScriptType()));
+                }
+                if (creation.getPluginType() != null)
+                {
+                    script.setPluginType(translate(creation.getPluginType()));
+                }
+                script.setScript(creation.getScript());
+                PluginUtils.checkScriptCompilation(script, evaluatorPool);
+                script.setAvailable(creation.isAvailable());
+                script.setRegistrator(person);
+                scripts.add(script);
+            }
+
+            @Override
+            public IProgress createProgress(PluginCreation object, int objectIndex, int totalObjectCount)
+            {
+                return new CreateProgress(object, objectIndex, totalObjectCount);
+            }
+        };
+        return scripts;
+    }
+    
+    private ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind translate(EntityKind entityKind)
+    {
+        return ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind.valueOf(entityKind.name());
+    }
+
+    private ch.systemsx.cisd.openbis.generic.shared.basic.dto.ScriptType translate(ScriptType scriptType)
+    {
+        return ch.systemsx.cisd.openbis.generic.shared.basic.dto.ScriptType.valueOf(scriptType.name());
+    }
+
+    private ch.systemsx.cisd.openbis.generic.shared.basic.dto.PluginType translate(PluginType pluginType)
+    {
+        return ch.systemsx.cisd.openbis.generic.shared.basic.dto.PluginType.valueOf(pluginType.name());
+    }
+
+    @Override
+    protected void updateBatch(IOperationContext context, MapBatch<PluginCreation, ScriptPE> batch)
+    {
+    }
+
+    @Override
+    protected void updateAll(IOperationContext context, MapBatch<PluginCreation, ScriptPE> batch)
+    {
+    }
+
+    @Override
+    protected List<ScriptPE> list(IOperationContext context, Collection<Long> ids)
+    {
+        return daoFactory.getScriptDAO().listAllEntities();
+    }
+
+    @Override
+    protected void save(IOperationContext context, List<ScriptPE> scripts, boolean clearCache)
+    {
+        for (ScriptPE script : scripts)
+        {
+            daoFactory.getScriptDAO().createOrUpdate(script);
+        }
+    }
+
+    @Override
+    protected void handleException(DataAccessException e)
+    {
+        DataAccessExceptionTranslator.throwException(e, "plugin", null);
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/CreatePluginsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/CreatePluginsOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..21abc607ddf987d5619a217f2942a5b983acf922
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/CreatePluginsOperationExecutor.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.CreateObjectsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.CreateObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.CreatePluginsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.CreatePluginsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.PluginCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.create.CreateObjectsOperationExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class CreatePluginsOperationExecutor
+        extends CreateObjectsOperationExecutor<PluginCreation, PluginPermId>
+        implements ICreatePluginsOperationExecutor
+{
+    @Autowired
+    private ICreatePluginExecutor executor;
+
+    @Override
+    protected Class<? extends CreateObjectsOperation<PluginCreation>> getOperationClass()
+    {
+        return CreatePluginsOperation.class;
+    }
+
+    @Override
+    protected CreateObjectsOperationResult<PluginPermId> doExecute(IOperationContext context, CreateObjectsOperation<PluginCreation> operation)
+    {
+        return new CreatePluginsOperationResult(executor.create(context, operation.getCreations()));
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/GetPluginsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/GetPluginsOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fc2c29b482567383fb51137235e5c0cc8acd2ba
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/GetPluginsOperationExecutor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.get.GetObjectsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.get.GetObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.get.GetPluginsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.get.GetPluginsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.get.GetObjectsPEOperationExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.get.IMapObjectByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.ITranslator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin.IPluginTranslator;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class GetPluginsOperationExecutor
+        extends GetObjectsPEOperationExecutor<IPluginId, ScriptPE, Plugin, PluginFetchOptions>
+        implements IGetPluginsOperationExecutor
+{
+    @Autowired
+    private IMapPluginByIdExecutor mapExecutor;
+    
+    @Autowired
+    private IPluginTranslator translator;
+
+    @Override
+    protected IMapObjectByIdExecutor<IPluginId, ScriptPE> getExecutor()
+    {
+        return mapExecutor;
+    }
+
+    @Override
+    protected ITranslator<Long, Plugin, PluginFetchOptions> getTranslator()
+    {
+        return translator;
+    }
+
+    @Override
+    protected GetObjectsOperationResult<IPluginId, Plugin> getOperationResult(Map<IPluginId, Plugin> objectMap)
+    {
+        return new GetPluginsOperationResult(objectMap);
+    }
+
+    @Override
+    protected Class<? extends GetObjectsOperation<IPluginId, PluginFetchOptions>> getOperationClass()
+    {
+        return GetPluginsOperation.class;
+    }
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/ICreatePluginExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/ICreatePluginExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a5219ef7865a5d74262444aca257adfe0251d14
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/ICreatePluginExecutor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.PluginCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.ICreateEntityExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface ICreatePluginExecutor extends ICreateEntityExecutor<PluginCreation, PluginPermId>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/ICreatePluginsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/ICreatePluginsOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..a531971d93ef6a1a3aba4c28613014785efaa748
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/ICreatePluginsOperationExecutor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.IOperationExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface ICreatePluginsOperationExecutor extends IOperationExecutor
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IGetPluginsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IGetPluginsOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fcd2929d1d8fdbec406c6c289bfdd7a5f7925ab
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IGetPluginsOperationExecutor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.IOperationExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IGetPluginsOperationExecutor extends IOperationExecutor
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IPluginAuthorizationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IPluginAuthorizationExecutor.java
index dffd881341942ba450b7f175bbfb9afc200b2ded..f38050797d43b58485161237e2e06b7b6904c469 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IPluginAuthorizationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IPluginAuthorizationExecutor.java
@@ -16,8 +16,10 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.IObjectAuthorizationExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
 
 /**
  * @author pkupczyk
@@ -27,4 +29,8 @@ public interface IPluginAuthorizationExecutor extends IObjectAuthorizationExecut
 
     void canGet(IOperationContext context);
 
+    void canCreate(IOperationContext context);
+
+    void canUpdate(IOperationContext context, IPluginId id, ScriptPE entity);
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IUpdatePluginExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IUpdatePluginExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..2127021dafef41f40c64bcf428cda7eab69b1b10
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IUpdatePluginExecutor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.PluginUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IUpdatePluginExecutor extends IUpdateEntityExecutor<PluginUpdate, PluginPermId>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IUpdatePluginsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IUpdatePluginsOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..926cc148f7608cc6ee31cf6250c297639bf3eb7a
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/IUpdatePluginsOperationExecutor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.update.IUpdateObjectsOperationExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IUpdatePluginsOperationExecutor extends IUpdateObjectsOperationExecutor
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/PluginAuthorizationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/PluginAuthorizationExecutor.java
index bb3e10b398321753e2fcd15b9b8186966ac1ceb6..d8c404e27579e8d4316956c016184e41e294d40f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/PluginAuthorizationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/PluginAuthorizationExecutor.java
@@ -18,10 +18,12 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
 
 import org.springframework.stereotype.Component;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.Capability;
 import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
 
 /**
  * @author pkupczyk
@@ -37,4 +39,18 @@ public class PluginAuthorizationExecutor implements IPluginAuthorizationExecutor
     {
     }
 
+    @Override
+    @RolesAllowed({ RoleWithHierarchy.INSTANCE_ADMIN })
+    @Capability("CREATE_PLUGIN")
+    public void canCreate(IOperationContext context)
+    {
+    }
+
+    @Override
+    @RolesAllowed({ RoleWithHierarchy.INSTANCE_ADMIN })
+    @Capability("UPDATE_PLUGIN")
+    public void canUpdate(IOperationContext context, IPluginId id, ScriptPE entity)
+    {
+    }
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/UpdatePluginExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/UpdatePluginExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..be2935b71a1215341dce399ba0bd27c0f97d7df8
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/UpdatePluginExecutor.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.annotation.Resource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.PluginUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.server.util.PluginUtils;
+import ch.systemsx.cisd.openbis.generic.shared.IJythonEvaluatorPool;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class UpdatePluginExecutor
+        extends AbstractUpdateEntityExecutor<PluginUpdate, ScriptPE, IPluginId, PluginPermId>
+        implements IUpdatePluginExecutor
+{
+    @Resource(name = ComponentNames.JYTHON_EVALUATOR_POOL)
+    private IJythonEvaluatorPool jythonEvaluatorPool;
+    
+    @Autowired
+    private IDAOFactory daoFactory;
+    
+    @Autowired
+    private IMapPluginByIdExecutor mapPluginByIdExecutor;
+    
+    @Autowired
+    private IPluginAuthorizationExecutor authorizationExecutor;
+
+    @Override
+    protected IPluginId getId(PluginUpdate update)
+    {
+        return update.getPluginId();
+    }
+
+    @Override
+    protected PluginPermId getPermId(ScriptPE entity)
+    {
+        return new PluginPermId(entity.getName());
+    }
+
+    @Override
+    protected void checkData(IOperationContext context, PluginUpdate update)
+    {
+        if (update.getPluginId() == null)
+        {
+            throw new UserFailureException("Plugin id cannot be null.");
+        }
+    }
+
+    @Override
+    protected void checkAccess(IOperationContext context, IPluginId id, ScriptPE entity)
+    {
+        authorizationExecutor.canUpdate(context, id, entity);
+    }
+
+    @Override
+    protected void updateBatch(IOperationContext context, MapBatch<PluginUpdate, ScriptPE> batch)
+    {
+        Set<Entry<PluginUpdate, ScriptPE>> entrySet = batch.getObjects().entrySet();
+        for (Entry<PluginUpdate, ScriptPE> entry : entrySet)
+        {
+            PluginUpdate update = entry.getKey();
+            ScriptPE script = entry.getValue();
+            script.setDescription(getNewValue(update.getDescription(), script.getDescription()));
+            FieldUpdateValue<String> scriptField = update.getScript();
+            if (scriptField != null && scriptField.isModified())
+            {
+                script.setScript(scriptField.getValue());
+                PluginUtils.checkScriptCompilation(script, jythonEvaluatorPool);
+            }
+            script.setAvailable(getNewValue(update.getAvailable(), script.isAvailable()));
+        }
+    }
+
+    @Override
+    protected void updateAll(IOperationContext context, MapBatch<PluginUpdate, ScriptPE> batch)
+    {
+    }
+
+    @Override
+    protected Map<IPluginId, ScriptPE> map(IOperationContext context, Collection<IPluginId> ids)
+    {
+        return mapPluginByIdExecutor.map(context, ids);
+    }
+
+    @Override
+    protected List<ScriptPE> list(IOperationContext context, Collection<Long> ids)
+    {
+        return daoFactory.getScriptDAO().listAllEntities();
+    }
+
+    @Override
+    protected void save(IOperationContext context, List<ScriptPE> entities, boolean clearCache)
+    {
+        for (ScriptPE script : entities)
+        {
+            daoFactory.getScriptDAO().validateAndSaveUpdatedEntity(script);
+        }
+    }
+
+    @Override
+    protected void handleException(DataAccessException e)
+    {
+        DataAccessExceptionTranslator.throwException(e, "plugin", null);
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/UpdatePluginsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/UpdatePluginsOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d1a7bbaf3eba274dfbe597448a851e53a170164
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/plugin/UpdatePluginsOperationExecutor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.plugin;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.PluginUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.UpdatePluginsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.UpdatePluginsOperationResult;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.update.UpdateObjectsOperationExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class UpdatePluginsOperationExecutor
+        extends UpdateObjectsOperationExecutor<PluginUpdate, IPluginId>
+        implements IUpdatePluginsOperationExecutor
+{
+    @Autowired
+    private IUpdatePluginExecutor executor;
+    
+
+    @Override
+    protected Class<? extends UpdateObjectsOperation<PluginUpdate>> getOperationClass()
+    {
+        return UpdatePluginsOperation.class;
+    }
+
+    @Override
+    protected UpdateObjectsOperationResult<? extends IPluginId> doExecute(IOperationContext context, UpdateObjectsOperation<PluginUpdate> operation)
+    {
+        return new UpdatePluginsOperationResult(executor.update(context, operation.getUpdates()));
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/generators/Generator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/generators/Generator.java
index b4d62ae74acedf069b317fdff829d900102e856c..b1b1ee66f348637fc7df3f160b3848baa22ed3bf 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/generators/Generator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/generators/Generator.java
@@ -402,6 +402,7 @@ public class Generator extends AbstractGenerator
         gen.addSimpleField(IDeletionId.class, "id");
         gen.addStringField("reason");
         gen.addPluralFetchedField("List<DeletedObject>", List.class.getName(), "deletedObjects", "Deleted objects", DeletedObjectFetchOptions.class);
+        gen.addSimpleField(Date.class, "deletionDate");
 
         gen.setToStringMethod("\"Deletion \" + id");
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..808276fd3e228dd9da12e7ffc9a9e87a47440622
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.plugin;
+
+import java.util.Comparator;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginSortOptions;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.AbstractStringComparator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.ComparatorFactory;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class PluginComparatorFactory extends ComparatorFactory
+{
+    private static final Comparator<Plugin> NAME_COMPARATOR = new AbstractStringComparator<Plugin>()
+        {
+            @Override
+            protected String getValue(Plugin plugin)
+            {
+                return plugin.getName();
+            }
+        };
+
+    @Override
+    public boolean accepts(Class<?> sortOptionsClass)
+    {
+        return PluginSortOptions.class.isAssignableFrom(sortOptionsClass);
+    }
+
+    @Override
+    public Comparator<Plugin> getComparator(String field)
+    {
+        if (PluginSortOptions.NAME.equals(field))
+        {
+            return NAME_COMPARATOR;
+        } else
+        {
+            return null;
+        }
+    }
+
+    @Override
+    public Comparator<Plugin> getDefaultComparator()
+    {
+        return NAME_COMPARATOR;
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java
index ebc78b3ba3fb8c78c1bc4dcc50f1e60cab1b3e88..16e7ff12ed63c71d547e4e63c20328f185c7457d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.dataset.DataSetCompara
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.experiment.ExperimentComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.globalsearch.GlobalSearchObjectComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.material.MaterialComparatorFactory;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.plugin.PluginComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.project.ProjectComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.property.PropertyAssignmentComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.property.PropertyTypeComparatorFactory;
@@ -60,6 +61,7 @@ public abstract class ComparatorFactory
         factories.add(new DataSetComparatorFactory());
         factories.add(new MaterialComparatorFactory());
         factories.add(new PropertyTypeComparatorFactory());
+        factories.add(new PluginComparatorFactory());
         factories.add(new PropertyAssignmentComparatorFactory());
     }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/deletion/DeletionTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/deletion/DeletionTranslator.java
index dc1f2c62fafad7fb8ef8bee4fb78577fafbd08db..6c271f771dc5da1f28147fcf5ee8cf30ba741ed4 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/deletion/DeletionTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/deletion/DeletionTranslator.java
@@ -65,6 +65,7 @@ public class DeletionTranslator extends
         Deletion deletion = new Deletion();
         deletion.setId(new DeletionTechId(input.getId()));
         deletion.setReason(input.getReason());
+        deletion.setDeletionDate(input.getRegistrationDate());
         deletion.setFetchOptions(new DeletionFetchOptions());
         return deletion;
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginBaseTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginBaseTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..86b84c1ce82ddb0f6caa4f274bef004487f3db9f
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginBaseTranslator.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.IObjectBaseTranslator;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IPluginBaseTranslator extends IObjectBaseTranslator<PluginRecord>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginRegistratorTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginRegistratorTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..43ab648316cb103a0dd03cdb457540cc4c5efcd5
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginRegistratorTranslator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.IObjectToOneRelationTranslator;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IPluginRegistratorTranslator extends IObjectToOneRelationTranslator<Person, PersonFetchOptions>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf10d55f881d439507ca886bf093eb11fecdd3a1
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/IPluginTranslator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.ITranslator;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IPluginTranslator extends ITranslator<Long, Plugin, PluginFetchOptions>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginBaseTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginBaseTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..42bae1f136863a5a89dcd5ad6c02bad59087a5e9
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginBaseTranslator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import java.util.List;
+
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectBaseTranslator;
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+import net.lemnik.eodsql.QueryTool;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class PluginBaseTranslator extends ObjectBaseTranslator<PluginRecord> implements IPluginBaseTranslator
+{
+    @Override
+    protected List<PluginRecord> loadRecords(LongOpenHashSet pluginIds)
+    {
+        PluginQuery query = QueryTool.getManagedQuery(PluginQuery.class);
+        return query.getPlugins(pluginIds);
+    }
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginQuery.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..22b9b8d0c168df2ce5829803f8f7c8434a421a94
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginQuery.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectQuery;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectRelationRecord;
+import ch.systemsx.cisd.common.db.mapper.LongSetMapper;
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import net.lemnik.eodsql.Select;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface PluginQuery extends ObjectQuery
+{
+    @Select(sql = "select * from scripts where id = any(?{1})", parameterBindings = {
+            LongSetMapper.class }, fetchSize = FETCH_SIZE)
+    public List<PluginRecord> getPlugins(LongSet pluginIds);
+
+    @Select(sql = "select id as objectId, pers_id_registerer as relatedId from scripts where id = any(?{1})", parameterBindings = {
+            LongSetMapper.class }, fetchSize = FETCH_SIZE)
+    public List<ObjectRelationRecord> getRegistartorIds(LongOpenHashSet objectIds);
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginRecord.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginRecord.java
new file mode 100644
index 0000000000000000000000000000000000000000..9642759ca5d144120299bd252c683c8a11481cdc
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginRecord.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import java.util.Date;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectBaseRecord;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public class PluginRecord extends ObjectBaseRecord
+{
+    public String name;
+    
+    public String description;
+    
+    public String entity_kind;
+    
+    public String script_type;
+    
+    public String plugin_type;
+    
+    public String script;
+    
+    public Date registration_timestamp;
+    
+    public boolean is_available;
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginRegistratorTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginRegistratorTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f03b06887e21c5302dcd0a6b3efce86d50a821c
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginRegistratorTranslator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectRelationRecord;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectToOneRelationTranslator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.person.IPersonTranslator;
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+import net.lemnik.eodsql.QueryTool;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class PluginRegistratorTranslator
+        extends ObjectToOneRelationTranslator<Person, PersonFetchOptions>
+        implements IPluginRegistratorTranslator
+{
+    @Autowired
+    private IPersonTranslator personTranslator;
+
+    @Override
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
+    {
+        PluginQuery query = QueryTool.getManagedQuery(PluginQuery.class);
+        return query.getRegistartorIds(objectIds);
+    }
+
+    @Override
+    protected Map<Long, Person> translateRelated(TranslationContext context, Collection<Long> relatedIds, PersonFetchOptions relatedFetchOptions)
+    {
+        return personTranslator.translate(context, relatedIds, relatedFetchOptions);
+    }
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..de8666e84b346ddecbb3bd1c803173c1ef257870
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/plugin/PluginTranslator.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Resource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.PluginType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.ScriptType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.AbstractCachingTranslator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.TranslationResults;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.dynamic_property.IDynamicPropertyCalculatorFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.entity_validation.IEntityValidatorFactory;
+import ch.systemsx.cisd.openbis.generic.shared.hotdeploy_plugins.api.ICommonPropertyBasedHotDeployPlugin;
+import ch.systemsx.cisd.openbis.generic.shared.managed_property.IManagedPropertyEvaluatorFactory;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class PluginTranslator
+        extends AbstractCachingTranslator<Long, Plugin, PluginFetchOptions>
+        implements IPluginTranslator
+{
+    @Autowired
+    private IPluginBaseTranslator baseTranslator;
+
+    @Autowired
+    private IPluginRegistratorTranslator registratorTranslator;
+
+    @Resource(name = "entity-validation-factory")
+    private IEntityValidatorFactory entityValidationFactory;
+    
+    @Resource(name = "dynamic-property-calculator-factory")
+    private IDynamicPropertyCalculatorFactory dynamicPropertyCalculatorFactory;
+    
+    @Resource(name = "managed-property-evaluator-factory")
+    private IManagedPropertyEvaluatorFactory managedPropertyEvaluatorFactory;
+
+    @Override
+    protected Plugin createObject(TranslationContext context, Long pluginId, PluginFetchOptions fetchOptions)
+    {
+        Plugin plugin = new Plugin();
+        plugin.setFetchOptions(fetchOptions);
+        return plugin;
+    }
+
+    @Override
+    protected Object getObjectsRelations(TranslationContext context, Collection<Long> ids, PluginFetchOptions fetchOptions)
+    {
+        TranslationResults relations = new TranslationResults();
+
+        relations.put(IPluginBaseTranslator.class, baseTranslator.translate(context, ids, null));
+        
+        if (fetchOptions.hasRegistrator())
+        {
+            relations.put(IPluginRegistratorTranslator.class,
+                    registratorTranslator.translate(context, ids, fetchOptions.withRegistrator()));
+        }
+        
+        return relations;
+    }
+
+    @Override
+    protected void updateObject(TranslationContext context, Long pluginId, Plugin plugin, Object objectRelations, PluginFetchOptions fetchOptions)
+    {
+        TranslationResults relations = (TranslationResults) objectRelations;
+        PluginRecord baseRecord = relations.get(IPluginBaseTranslator.class, pluginId);
+        
+        plugin.setName(baseRecord.name);
+        plugin.setPermId(new PluginPermId(baseRecord.name));
+        plugin.setDescription(baseRecord.description);
+        plugin.setRegistrationDate(baseRecord.registration_timestamp);
+        plugin.setAvailable(baseRecord.is_available);
+        if (baseRecord.entity_kind != null)
+        {
+            plugin.setEntityKinds(EnumSet.of(EntityKind.valueOf(baseRecord.entity_kind)));
+        }
+        injectEntityKindsFromPredeployed(plugin);
+        plugin.setPluginType(PluginType.valueOf(baseRecord.plugin_type));
+        plugin.setScriptType(ScriptType.valueOf(baseRecord.script_type));
+        plugin.setScript(baseRecord.script);
+        
+        if (fetchOptions.hasRegistrator())
+        {
+            plugin.setRegistrator(relations.get(IPluginRegistratorTranslator.class, pluginId));
+            plugin.getFetchOptions().withRegistratorUsing(fetchOptions.withRegistrator());
+        }
+
+    }
+    
+    private void injectEntityKindsFromPredeployed(Plugin plugin)
+    {
+        if (plugin.getPluginType() == PluginType.PREDEPLOYED)
+        {
+            ICommonPropertyBasedHotDeployPlugin hotDeployPlugin = null;
+            switch (plugin.getScriptType())
+            {
+                case ENTITY_VALIDATION:
+                    hotDeployPlugin =
+                            entityValidationFactory.tryGetPredeployedPluginByName(plugin.getName());
+                    break;
+                case DYNAMIC_PROPERTY:
+                    hotDeployPlugin =
+                            dynamicPropertyCalculatorFactory.tryGetPredeployedPluginByName(plugin
+                                    .getName());
+                    break;
+                case MANAGED_PROPERTY:
+                    hotDeployPlugin =
+                            managedPropertyEvaluatorFactory.tryGetPredeployedPluginByName(plugin
+                                    .getName());
+            }
+
+            if (hotDeployPlugin != null)
+            {
+                EnumSet<ch.systemsx.cisd.openbis.generic.shared.hotdeploy_plugins.api.ICommonPropertyBasedHotDeployPlugin.EntityKind> supportedEntityKinds =
+                        hotDeployPlugin.getSupportedEntityKinds();
+                plugin.setEntityKinds(translateEntityKinds(supportedEntityKinds));
+            }
+        }
+    }
+
+    private static Set<EntityKind> translateEntityKinds(
+            EnumSet<ch.systemsx.cisd.openbis.generic.shared.hotdeploy_plugins.api.ICommonPropertyBasedHotDeployPlugin.EntityKind> entityKinds)
+    {
+        if (entityKinds == null)
+        {
+            return null;
+        } else if (entityKinds.size() == ch.systemsx.cisd.openbis.generic.shared.hotdeploy_plugins.api.ICommonPropertyBasedHotDeployPlugin.EntityKind
+                .values().length)
+        {
+            return null;
+        }
+
+        List<EntityKind> kinds = new ArrayList<EntityKind>(entityKinds.size());
+        for (ch.systemsx.cisd.openbis.generic.shared.hotdeploy_plugins.api.ICommonPropertyBasedHotDeployPlugin.EntityKind kind : entityKinds)
+        {
+            kinds.add(EntityKind.valueOf(kind.name()));
+        }
+        return EnumSet.copyOf(kinds);
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
index 965e4d1b6c9502bd0ad8ddc7851dbea0cfb7c834..24e38c3ff241db577d8e2fcf6580eb336eb14aa7 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
@@ -51,4 +51,6 @@ public final class ComponentNames
     public static final String RELATIONSHIP_SERVICE = "relationship-service";
 
     public static final String PROPERTIES_BATCH_MANAGER = "properties-batch-manager";
+    
+    public static final String JYTHON_EVALUATOR_POOL = "jython-evaluator-pool";
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ScriptBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ScriptBO.java
index dc638865bf85cbf3170f35cb543d846560c42998..58c32c8eda026e2d1ec80c756dc17dde8fc1f831 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ScriptBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ScriptBO.java
@@ -21,13 +21,11 @@ import org.springframework.dao.DataRetrievalFailureException;
 
 import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.common.jython.evaluator.EvaluatorException;
 import ch.systemsx.cisd.openbis.generic.server.business.IRelationshipService;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.util.DataSetTypeWithoutExperimentChecker;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IScriptDAO;
-import ch.systemsx.cisd.openbis.generic.server.dataaccess.dynamic_property.calculator.JythonDynamicPropertyCalculator;
-import ch.systemsx.cisd.openbis.generic.server.dataaccess.dynamic_property.calculator.JythonEntityValidationCalculator;
+import ch.systemsx.cisd.openbis.generic.server.util.PluginUtils;
 import ch.systemsx.cisd.openbis.generic.shared.IJythonEvaluatorPool;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IScriptUpdates;
@@ -38,7 +36,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.managed_property.IManagedPropertyEvaluatorFactory;
-import ch.systemsx.cisd.openbis.generic.shared.managed_property.JythonManagedPropertyEvaluator;
 
 /**
  * The only productive implementation of {@link IScriptBO}. We are using an interface here to keep the system testable.
@@ -125,8 +122,7 @@ public final class ScriptBO extends AbstractBusinessObject implements IScriptBO
         assert script != null : "Script not defined";
         try
         {
-            checkScriptCompilation(script.getScriptType(), script.getPluginType(),
-                    script.getScript());
+            PluginUtils.checkScriptCompilation(script, jythonEvaluatorPool);
             getScriptDAO().createOrUpdate(script);
         } catch (final DataAccessException e)
         {
@@ -235,8 +231,7 @@ public final class ScriptBO extends AbstractBusinessObject implements IScriptBO
         {
             scriptChanged = true;
             script.setScript(updates.getScript());
-            checkScriptCompilation(script.getScriptType(), script.getPluginType(),
-                    updates.getScript());
+            PluginUtils.checkScriptCompilation(script, jythonEvaluatorPool);
         }
         getScriptDAO().createOrUpdate(script);
 
@@ -265,28 +260,4 @@ public final class ScriptBO extends AbstractBusinessObject implements IScriptBO
                     .scheduleDynamicPropertiesEvaluation(assignment);
         }
     }
-
-    private void checkScriptCompilation(ScriptType scriptType, PluginType pluginType,
-            String scriptExpression) throws EvaluatorException
-    {
-        if (pluginType == PluginType.PREDEPLOYED)
-        {
-            return;
-        }
-
-        if (scriptType == ScriptType.MANAGED_PROPERTY)
-        {
-            new JythonManagedPropertyEvaluator(scriptExpression);
-        } else if (scriptType == ScriptType.DYNAMIC_PROPERTY)
-        {
-            JythonDynamicPropertyCalculator calculator =
-                    JythonDynamicPropertyCalculator.create(scriptExpression, jythonEvaluatorPool);
-            calculator.checkScriptCompilation();
-        } else
-        {
-            JythonEntityValidationCalculator calculator =
-                    JythonEntityValidationCalculator.create(scriptExpression, null, jythonEvaluatorPool);
-            calculator.checkScriptCompilation();
-        }
-    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/util/PluginUtils.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/util/PluginUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f34a944c598d1f574ced72c6d464c3bf6c218bd
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/util/PluginUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.systemsx.cisd.openbis.generic.server.util;
+
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.dynamic_property.calculator.JythonDynamicPropertyCalculator;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.dynamic_property.calculator.JythonEntityValidationCalculator;
+import ch.systemsx.cisd.openbis.generic.shared.IJythonEvaluatorPool;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PluginType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ScriptType;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
+import ch.systemsx.cisd.openbis.generic.shared.managed_property.JythonManagedPropertyEvaluator;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class PluginUtils
+{
+    public static void checkScriptCompilation(ScriptPE script, IJythonEvaluatorPool evaluatorPool)
+    {
+        PluginType pluginType = script.getPluginType();
+        if (pluginType == PluginType.PREDEPLOYED)
+        {
+            return;
+        }
+
+        String scriptExpression = script.getScript();
+        ScriptType scriptType = script.getScriptType();
+        if (scriptType == ScriptType.MANAGED_PROPERTY)
+        {
+            new JythonManagedPropertyEvaluator(scriptExpression);
+        } else
+        {
+            if (scriptType == ScriptType.DYNAMIC_PROPERTY)
+            {
+                JythonDynamicPropertyCalculator calculator =
+                        JythonDynamicPropertyCalculator.create(scriptExpression, evaluatorPool);
+                calculator.checkScriptCompilation();
+            } else
+            {
+                JythonEntityValidationCalculator calculator =
+                        JythonEntityValidationCalculator.create(scriptExpression, null, evaluatorPool);
+                calculator.checkScriptCompilation();
+            }
+        }
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ScriptPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ScriptPE.java
index 5df95ef8819c218ae3c50d2b33a0b01140ec6cb4..59d93141b106aedfe6f8a8e7c8a31d7af5c8f62d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ScriptPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ScriptPE.java
@@ -43,7 +43,7 @@ import org.hibernate.validator.constraints.Length;
 import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.common.collection.UnmodifiableListDecorator;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants;
-import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdentityHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PluginType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ScriptType;
@@ -56,7 +56,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
  */
 @Entity
 @Table(name = TableNames.SCRIPTS_TABLE)
-public class ScriptPE extends HibernateAbstractRegistrationHolder implements IIdHolder,
+public class ScriptPE extends HibernateAbstractRegistrationHolder implements IIdentityHolder,
         Comparable<ScriptPE>, Serializable
 {
 
@@ -122,6 +122,20 @@ public class ScriptPE extends HibernateAbstractRegistrationHolder implements IId
         return id;
     }
 
+    @Override
+    @Transient
+    public String getPermId()
+    {
+        return getName();
+    }
+
+    @Override
+    @Transient
+    public String getIdentifier()
+    {
+        return getName();
+    }
+
     @Column(name = ColumnNames.NAME_COLUMN)
     @NotNull(message = ValidationMessages.NAME_NOT_NULL_MESSAGE)
     @Length(min = 1, max = 200, message = ValidationMessages.NAME_LENGTH_MESSAGE)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/Deletion.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/Deletion.js
index 21a4188f58f429356dff04bdad75bc0f03b0b2d3..3d3579bbf9c204b51a0862429c509a89b57a29b4 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/Deletion.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/Deletion.js
@@ -12,6 +12,7 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.id = null;
 		prototype.reason = null;
 		prototype.deletedObjects = null;
+		prototype.deletionDate = null;
 		prototype.getFetchOptions = function() {
 			return this.fetchOptions;
 		};
@@ -40,13 +41,20 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.setDeletedObjects = function(deletedObjects) {
 			this.deletedObjects = deletedObjects;
 		};
+		prototype.getDeletionDate = function() {
+			return this.deletionDate;
+		};
+		prototype.setDeletionDate = function(deletionDate) {
+			this.deletionDate = deletionDate;
+		};
 	}, {
 		fetchOptions : "DeletionFetchOptions",
 		id : "IDeletionId",
 		deletedObjects : {
 			name : "List",
 			arguments : [ "DeletedObject" ]
-		}
+		},
+		deletionDate : "Date"
 	});
 	return Deletion;
 })
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/confirm/ConfirmDeletionsOperation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/confirm/ConfirmDeletionsOperation.js
index a48edced5ddeed2c70ab1f96f497e93a48fb65bc..38f25e3f73d32f3d6d90a5e99e1954b76aab1bc7 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/confirm/ConfirmDeletionsOperation.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/deletion/confirm/ConfirmDeletionsOperation.js
@@ -8,9 +8,16 @@ define([ "stjs", "as/dto/common/operation/IOperation" ], function(stjs, IOperati
 	stjs.extend(ConfirmDeletionsOperation, null, [ IOperation ], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.deletion.confirm.ConfirmDeletionsOperation';
 		prototype.deletionIds = null;
+		prototype.forceDeletion = false;
 		prototype.getDeletionIds = function() {
 			return this.deletionIds;
 		};
+		prototype.setForceDeletion = function(forceDeletion) {
+			this.forceDeletion = forceDeletion;
+		};
+		prototype.isForceDeletion = function() {
+			return this.forceDeletion;
+		};
 		prototype.getMessage = function() {
 			return "ConfirmDeletionsOperation";
 		};
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/create/PersonCreation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/create/PersonCreation.js
index 3f265a0157783d76f5bea3114ab8b4c9118f1dfb..6ec8e4d76492d7dcdf2471201dac3ffcd5a22307 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/create/PersonCreation.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/create/PersonCreation.js
@@ -5,7 +5,7 @@ define([ "stjs" ], function(stjs) {
 		prototype['@type'] = 'as.dto.person.create.PersonCreation';
 		constructor.serialVersionUID = 1;
 		prototype.userId = null;
-		prototype.homeSpaceId = null;
+		prototype.spaceId = null;
 
 		prototype.getUserId = function() {
 			return this.userId;
@@ -13,11 +13,11 @@ define([ "stjs" ], function(stjs) {
 		prototype.setUserId = function(userId) {
 			this.userId = userId;
 		};
-		prototype.getHomeSpaceId = function() {
-			return this.homeSpaceId;
+		prototype.getSpaceId = function() {
+			return this.spaceId;
 		};
-		prototype.setHomeSpaceId = function(spaceId) {
-			this.homeSpaceId = spaceId;
+		prototype.setSpaceId = function(spaceId) {
+			this.spaceId = spaceId;
 		};
 	}, {
 	});
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/update/PersonUpdate.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/update/PersonUpdate.js
index bb4fc30f72b90a190356abd80ae122d9ead0679a..4e651fe6d51e50057f738235d9e5598653c2b2ec 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/update/PersonUpdate.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/update/PersonUpdate.js
@@ -1,12 +1,12 @@
 define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/IdListUpdateValue" ], function(stjs, FieldUpdateValue, IdListUpdateValue) {
 	var PersonUpdate = function() {
-		this.homeSpaceId = new FieldUpdateValue();
+		this.spaceId = new FieldUpdateValue();
 	};
 	stjs.extend(PersonUpdate, null, [], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.person.update.PersonUpdate';
 		constructor.serialVersionUID = 1;
 		prototype.userId = null;
-		prototype.homeSpaceId = null;
+		prototype.spaceId = null;
 		prototype.active = true;
 
 		prototype.getObjectId = function() {
@@ -18,11 +18,11 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 		prototype.setUserId = function(userId) {
 			this.userId = userId;
 		};
-		prototype.getHomeSpaceId = function() {
-			return this.homeSpaceId;
+		prototype.getSpaceId = function() {
+			return this.spaceId;
 		};
-		prototype.setHomeSpaceId = function(spaceId) {
-			this.homeSpaceId.setValue(spaceId);
+		prototype.setSpaceId = function(spaceId) {
+			this.spaceId.setValue(spaceId);
 		};
 		prototype.isActive = function() {
 			return this.active;
@@ -32,7 +32,7 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 		};
 	}, {
 		userId : "IPersonId",
-		homeSpaceId : {
+		spaceId : {
 			name : "FieldUpdateValue",
 			arguments : [ "ISpaceId" ]
 		}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/Plugin.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/Plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..ba7900eb9b2b9004e79ff6d4518810f9287a0bf2
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/Plugin.js
@@ -0,0 +1,102 @@
+define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
+	var Plugin = function() {
+	};
+	stjs.extend(Plugin, null, [], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.Plugin';
+		constructor.serialVersionUID = 1;
+		prototype.fetchOptions = null;
+		prototype.name = null;
+		prototype.permId = null;
+		prototype.description = null;
+		prototype.pluginType = null;
+		prototype.scriptType = null;
+		prototype.entityKinds = null;
+		prototype.script = null;
+		prototype.available = null;
+		prototype.registrator = null;
+		prototype.registrationDate = null;
+
+		prototype.getFetchOptions = function() {
+			return this.fetchOptions;
+		};
+		prototype.setFetchOptions = function(fetchOptions) {
+			this.fetchOptions = fetchOptions;
+		};
+		prototype.getName = function() {
+			return this.name;
+		};
+		prototype.setName = function(name) {
+			this.name = name;
+		};
+		prototype.getPermId = function() {
+			return this.permId;
+		};
+		prototype.setPermId = function(permId) {
+			this.permId = permId;
+		};
+		prototype.getDescription = function() {
+			return this.description;
+		};
+		prototype.setDescription = function(description) {
+			this.description = description;
+		};
+		prototype.getPluginType = function() {
+			return this.pluginType;
+		};
+		prototype.setPluginType = function(pluginType) {
+			this.pluginType = pluginType;
+		};
+		prototype.getScriptType = function() {
+			return this.scriptType;
+		};
+		prototype.setScriptType = function(scriptType) {
+			this.scriptType = scriptType;
+		};
+		prototype.getEntityKinds = function() {
+			return this.entityKinds;
+		};
+		prototype.setEntityKinds = function(entityKinds) {
+			this.entityKinds = entityKinds;
+		};
+		prototype.getScript = function() {
+			return this.script;
+		};
+		prototype.setScript = function(script) {
+			this.script = script;
+		};
+		prototype.isAvailable = function() {
+			return this.available;
+		};
+		prototype.setAvailable = function(available) {
+			this.available = available;
+		};
+		prototype.getRegistrator = function() {
+			if (this.getFetchOptions() && this.getFetchOptions().hasRegistrator()) {
+				return this.registrator;
+			} else {
+				throw new exceptions.NotFetchedException("Registrator has not been fetched.");
+			}
+		};
+		prototype.setRegistrator = function(registrator) {
+			this.registrator = registrator;
+		};
+		prototype.getRegistrationDate = function() {
+			return this.registrationDate;
+		};
+		prototype.setRegistrationDate = function(registrationDate) {
+			this.registrationDate = registrationDate;
+		};
+	}, {
+		fetchOptions : "PluginFetchOptions",
+		permId : "PluginPermId",
+		pluginType : "PluginType",
+		scriptType : "ScriptType",
+		entityKinds : {
+			name : "Set",
+			arguments : [ "EntityKind" ]
+		},
+		registrator : "Person",
+		registrationDate : "Date"
+	});
+	return Plugin;
+})
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/PluginType.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/PluginType.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f0b63a2450b0c2a49cb85fe4274dcf7993c827d
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/PluginType.js
@@ -0,0 +1,12 @@
+/**
+ * @author Franz-Josef Elmer
+ */
+
+define([ "stjs", "as/dto/common/Enum" ], function(stjs, Enum) {
+    var PluginType = function() {
+        Enum.call(this, [ "JYTHON", "PREDEPLOYED" ]);
+    };
+    stjs.extend(PluginType, Enum, [ Enum ], function(constructor, prototype) {
+    }, {});
+    return new PluginType();
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/ScriptType.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/ScriptType.js
new file mode 100644
index 0000000000000000000000000000000000000000..be6592779e8023cdfbd3036c98dafdc4a738a046
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/ScriptType.js
@@ -0,0 +1,12 @@
+/**
+ * @author Franz-Josef Elmer
+ */
+
+define([ "stjs", "as/dto/common/Enum" ], function(stjs, Enum) {
+    var ScriptType = function() {
+        Enum.call(this, [ "DYNAMIC_PROPERTY", "MANAGED_PROPERTY", "ENTITY_VALIDATION" ]);
+    };
+    stjs.extend(ScriptType, Enum, [ Enum ], function(constructor, prototype) {
+    }, {});
+    return new ScriptType();
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/CreatePluginsOperation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/CreatePluginsOperation.js
new file mode 100644
index 0000000000000000000000000000000000000000..a8548c892ec9f2b06405cf4f01fb0c493e18431f
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/CreatePluginsOperation.js
@@ -0,0 +1,13 @@
+define([ "stjs", "as/dto/common/create/CreateObjectsOperation" ], function(stjs, CreateObjectsOperation) {
+    var CreatePluginsOperation = function(creations) {
+        CreateObjectsOperation.call(this, creations);
+    };
+    stjs.extend(CreatePluginsOperation, CreateObjectsOperation, [ CreateObjectsOperation ], function(constructor, prototype) {
+        prototype['@type'] = 'as.dto.plugin.create.CreatePluginsOperation';
+        prototype.getMessage = function() {
+            return "CreatePluginsOperation";
+        };
+    }, {});
+    return CreatePluginsOperation;
+})
+
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/CreatePluginsOperationResult.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/CreatePluginsOperationResult.js
new file mode 100644
index 0000000000000000000000000000000000000000..8eca54f40a02fc7abcb13b59990daa78e860b3d8
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/CreatePluginsOperationResult.js
@@ -0,0 +1,12 @@
+define([ "stjs", "as/dto/common/create/CreateObjectsOperationResult" ], function(stjs, CreateObjectsOperationResult) {
+	var CreatePluginsOperationResult = function(objectIds) {
+		CreateObjectsOperationResult.call(this, objectIds);
+	};
+	stjs.extend(CreatePluginsOperationResult, CreateObjectsOperationResult, [ CreateObjectsOperationResult ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.create.CreatePluginsOperationResult';
+		prototype.getMessage = function() {
+			return "CreatePluginsOperationResult";
+		};
+	}, {});
+	return CreatePluginsOperationResult;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/PluginCreation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/PluginCreation.js
new file mode 100644
index 0000000000000000000000000000000000000000..78f1031006fbc1a5bf47477a43290a23f08e582d
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/create/PluginCreation.js
@@ -0,0 +1,63 @@
+define([ "stjs" ], function(stjs) {
+	var PluginCreation = function() {
+	};
+	stjs.extend(PluginCreation, null, [], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.create.PluginCreation';
+		constructor.serialVersionUID = 1;
+		prototype.name = null;
+		prototype.description = null;
+		prototype.script = null;
+		prototype.pluginType = null;
+		prototype.scriptType = null;
+		prototype.available = true;
+		prototype.entityKind = null;
+
+		prototype.getName = function() {
+			return this.name;
+		};
+		prototype.setName = function(name) {
+			this.name = name;
+		};
+		prototype.getDescription = function() {
+			return this.description;
+		};
+		prototype.setDescription = function(description) {
+			this.description = description;
+		};
+		prototype.getScript = function() {
+			return this.script;
+		};
+		prototype.setScript = function(script) {
+			this.script = script;
+		};
+		prototype.getPluginType = function() {
+			return this.pluginType;
+		};
+		prototype.setPluginType = function(pluginType) {
+			this.pluginType = pluginType;
+		};
+		prototype.getScriptType = function() {
+			return this.scriptType;
+		};
+		prototype.setScriptType = function(scriptType) {
+			this.scriptType = scriptType;
+		};
+		prototype.isAvailable = function() {
+			return this.available;
+		};
+		prototype.setAvailable = function(available) {
+			this.available = available;
+		};
+		prototype.getEntityKind = function() {
+			return this.entityKind;
+		};
+		prototype.setEntityKind = function(entityKind) {
+			this.entityKind = entityKind;
+		};
+	}, {
+		pluginType : "PluginType",
+		scriptType : "ScriptType",
+		entityKind : "EntityKind"
+	});
+	return PluginCreation;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/fetchoptions/PluginFetchOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/fetchoptions/PluginFetchOptions.js
new file mode 100644
index 0000000000000000000000000000000000000000..d0fa36a27996af19bb231e7da472c68dcf7407b3
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/fetchoptions/PluginFetchOptions.js
@@ -0,0 +1,40 @@
+define([ "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/person/fetchoptions/PersonFetchOptions", 
+         "as/dto/plugin/fetchoptions/PluginSortOptions" ], function(
+		stjs, FetchOptions) {
+	var PluginFetchOptions = function() {
+	};
+	stjs.extend(PluginFetchOptions, FetchOptions, [ FetchOptions ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.fetchoptions.PluginFetchOptions';
+		constructor.serialVersionUID = 1;
+		prototype.registrator = null;
+		prototype.sort = null;
+
+		prototype.withRegistrator = function() {
+			if (this.registrator == null) {
+				var PersonFetchOptions = require("as/dto/person/fetchoptions/PersonFetchOptions");
+				this.registrator = new PersonFetchOptions();
+			}
+			return this.registrator;
+		};
+		prototype.withRegistratorUsing = function(fetchOptions) {
+			return this.registrator = fetchOptions;
+		};
+		prototype.hasRegistrator = function() {
+			return this.registrator != null;
+		};
+		prototype.sortBy = function() {
+			if (this.sort == null) {
+				var PluginSortOptions = require("as/dto/plugin/fetchoptions/PluginSortOptions");
+				this.sort = new PluginSortOptions();
+			}
+			return this.sort;
+		};
+		prototype.getSortBy = function() {
+			return this.sort;
+		};
+	}, {
+		registrator : "PersonFetchOptions",
+		sort : "PluginSortOptions"
+	});
+	return PluginFetchOptions;
+})
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/fetchoptions/PluginSortOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/fetchoptions/PluginSortOptions.js
new file mode 100644
index 0000000000000000000000000000000000000000..792355b479404324f4cc3b5c5fd22b633d0aa3ad
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/fetchoptions/PluginSortOptions.js
@@ -0,0 +1,22 @@
+define([ "require", "stjs", "as/dto/common/fetchoptions/SortOptions" ], function(require, stjs, SortOptions) {
+	var PluginSortOptions = function() {
+		SortOptions.call(this);
+	};
+	
+	var fields = {
+		NAME: "NAME",
+	};
+	
+	stjs.extend(PluginSortOptions, SortOptions, [ SortOptions ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.fetchoptions.PluginSortOptions';
+		constructor.serialVersionUID = 1;
+		
+		prototype.code = function() {
+			return this.getOrCreateSorting(fields.NAME);
+		};
+		prototype.getCode = function() {
+			return this.getSorting(fields.NAME);
+		};
+	}, {});
+	return PluginSortOptions;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/get/GetPluginsOperation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/get/GetPluginsOperation.js
new file mode 100644
index 0000000000000000000000000000000000000000..e2903361f32c17e24c00e70ab34583fb8c2034d4
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/get/GetPluginsOperation.js
@@ -0,0 +1,12 @@
+define([ "stjs", "as/dto/common/get/GetObjectsOperation" ], function(stjs, GetObjectsOperation) {
+	var GetPluginsOperation = function(objectIds, fetchOptions) {
+		GetObjectsOperation.call(this, objectIds, fetchOptions);
+	};
+	stjs.extend(GetPluginsOperation, GetObjectsOperation, [ GetObjectsOperation ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.get.GetPluginsOperation';
+		prototype.getMessage = function() {
+			return "GetPluginsOperation";
+		};
+	}, {});
+	return GetPluginsOperation;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/get/GetPluginsOperationResult.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/get/GetPluginsOperationResult.js
new file mode 100644
index 0000000000000000000000000000000000000000..439e94ecece06c7d53c10650b8c08919c0d949ae
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/get/GetPluginsOperationResult.js
@@ -0,0 +1,12 @@
+define([ "stjs", "as/dto/common/get/GetObjectsOperationResult" ], function(stjs, GetObjectsOperationResult) {
+	var GetPluginsOperationResult = function(objectMap) {
+		GetObjectsOperationResult.call(this, objectMap);
+	};
+	stjs.extend(GetPluginsOperationResult, GetObjectsOperationResult, [ GetObjectsOperationResult ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.get.GetPluginsOperationResult';
+		prototype.getMessage = function() {
+			return "GetPluginsOperationResult";
+		};
+	}, {});
+	return GetPluginsOperationResult;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/PluginUpdate.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/PluginUpdate.js
new file mode 100644
index 0000000000000000000000000000000000000000..db24743b5f5ea7a88b03de83b660892e1045c89a
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/PluginUpdate.js
@@ -0,0 +1,46 @@
+define([ "stjs", "as/dto/common/update/FieldUpdateValue" ], function(stjs, FieldUpdateValue) {
+	var PluginUpdate = function() {
+		this.description = new FieldUpdateValue();
+		this.script = new FieldUpdateValue();
+		this.available = new FieldUpdateValue();
+	};
+	stjs.extend(PluginUpdate, null, [], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.update.PluginUpdate';
+		constructor.serialVersionUID = 1;
+		prototype.pluginId = null;
+		prototype.description = null;
+		prototype.script = null;
+		prototype.available = null;
+
+		prototype.getObjectId = function() {
+			return this.getPluginId();
+		};
+		prototype.getPluginId = function() {
+			return this.pluginId;
+		};
+		prototype.setPluginId = function(pluginId) {
+			this.pluginId = pluginId;
+		};
+		prototype.getDescription = function() {
+			return this.description;
+		};
+		prototype.setDescription = function(description) {
+			this.description.setValue(description);
+		};
+		prototype.getScript = function() {
+			return this.script;
+		};
+		prototype.setScript = function(script) {
+			this.script.setValue(script);
+		};
+		prototype.getAvailable = function() {
+			return this.available;
+		};
+		prototype.setAvailable = function(available) {
+			this.available.setValue(available);
+		};
+	}, {
+		pluginId : "IPluginId"
+	});
+	return PluginUpdate;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/UpdatePluginsOperation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/UpdatePluginsOperation.js
new file mode 100644
index 0000000000000000000000000000000000000000..710c350d94e618399b032c09c49ad6607482faf2
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/UpdatePluginsOperation.js
@@ -0,0 +1,12 @@
+define([ "stjs", "as/dto/common/update/UpdateObjectsOperation" ], function(stjs, UpdateObjectsOperation) {
+	var UpdatePluginsOperation = function(updates) {
+		UpdateObjectsOperation.call(this, updates);
+	};
+	stjs.extend(UpdatePluginsOperation, UpdateObjectsOperation, [ UpdateObjectsOperation ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.update.UpdatePluginsOperation';
+		prototype.getMessage = function() {
+			return "UpdatePluginsOperation";
+		};
+	}, {});
+	return UpdatePluginsOperation;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/UpdatePluginsOperationResult.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/UpdatePluginsOperationResult.js
new file mode 100644
index 0000000000000000000000000000000000000000..534a9bd5d92706ca4409c31ed4d1d4b4d1700434
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/plugin/update/UpdatePluginsOperationResult.js
@@ -0,0 +1,12 @@
+define([ "stjs", "as/dto/common/update/UpdateObjectsOperationResult" ], function(stjs, UpdateObjectsOperationResult) {
+	var UpdatePluginsOperationResult = function(objectIds) {
+		UpdateObjectsOperationResult.call(this, objectIds);
+	};
+	stjs.extend(UpdatePluginsOperationResult, UpdateObjectsOperationResult, [ UpdateObjectsOperationResult ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.plugin.update.UpdatePluginsOperationResult';
+		prototype.getMessage = function() {
+			return "UpdatePluginsOperationResult";
+		};
+	}, {});
+	return UpdatePluginsOperationResult;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
index 7d927f9e88a4a4b8ed095f8def9f8a934ba8630a..7dcd63fadd7c87aaebd272d27c9cdceb0cb6d75d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
@@ -525,6 +525,21 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 			});
 		}
 		
+		this.createPlugins = function(creations) {
+			var thisFacade = this;
+			return thisFacade._private.ajaxRequest({
+				url : openbisUrl,
+				data : {
+					"method" : "createPlugins",
+					"params" : [ thisFacade._private.sessionToken, creations ]
+				},
+				returnType : {
+					name : "List",
+					arguments : [ "PluginPermId" ]
+				}
+			});
+		}
+
 		this.createVocabularies = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -539,7 +554,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-
+		
 		this.createVocabularyTerms = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -762,6 +777,17 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 			});
 		}
 
+		this.updatePlugins = function(updates) {
+			var thisFacade = this;
+			return thisFacade._private.ajaxRequest({
+				url : openbisUrl,
+				data : {
+					"method" : "updatePlugins",
+					"params" : [ thisFacade._private.sessionToken, updates ]
+				}
+			});
+		}
+		
 		this.updateVocabularies = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -944,6 +970,21 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 			});
 		}
 		
+		this.getPlugins = function(ids, fetchOptions) {
+			var thisFacade = this;
+			return thisFacade._private.ajaxRequest({
+				url : openbisUrl,
+				data : {
+					"method" : "getPlugins",
+					"params" : [ thisFacade._private.sessionToken, ids, fetchOptions ]
+				},
+				returnType : {
+					name : "Map",
+					arguments : [ "IPluginId", "Plugin" ]
+				}
+			});
+		}
+		
 		this.getVocabularies = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractDeletionTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractDeletionTest.java
index a9a5950bf744653055266daf6434c1e4d80ecf2c..a8b599007241bbdece289a0e22d9a8f1e75930bc 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractDeletionTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractDeletionTest.java
@@ -22,7 +22,10 @@ import java.util.List;
 import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.Deletion;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.fetchoptions.DeletionFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.id.IDeletionId;
@@ -48,6 +51,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchO
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.SpacePermId;
+
 import junit.framework.Assert;
 
 /**
@@ -206,15 +210,9 @@ public class AbstractDeletionTest extends AbstractTest
 
     private void assertDataSetExists(DataSetPermId dataSetId, boolean exists)
     {
-        String sessionToken = generalInformationService.tryToAuthenticateForAllServices(TEST_USER, PASSWORD);
-
-        ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria criteria =
-                new ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria();
-        criteria.addMatchClause(ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause.createAttributeMatch(
-                ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute.CODE, dataSetId.getPermId()));
-
-        List<ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet> result = generalInformationService.searchForDataSets(sessionToken, criteria);
-        Assert.assertEquals(exists ? 1 : 0, result.size());
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        Map<IDataSetId, DataSet> map = v3api.getDataSets(sessionToken, Collections.singletonList(dataSetId), new DataSetFetchOptions());
+        Assert.assertEquals(exists ? 1 : 0, map.size());
     }
 
     protected void assertDeletionExists(IDeletionId deletionId)
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java
index f1ddfb168d0368078584a0edddb7847fb4505b1d..31c0181d0d82b933a1777919d71151d83f3621e9 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java
@@ -72,6 +72,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ISpaceHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ITagsHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.DataStore;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.Deletion;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.id.IDeletionId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.history.HistoryEntry;
@@ -169,7 +171,7 @@ public class AbstractTest extends SystemTestCase
         logRecorder.reset();
         System.out.println("<<<<<<<<< AFTER METHOD: " + method.getName());
     }
-    
+
     protected void sortPropertyAssignments(List<PropertyAssignment> assignments)
     {
         Collections.sort(assignments, ASSIGNMENT_COMPARATOR);
@@ -830,6 +832,16 @@ public class AbstractTest extends SystemTestCase
         assertCollectionContainsOnly(actualPermIds, expectedTagPermIds);
     }
 
+    protected void assertDeletions(Collection<Deletion> deletions, IDeletionId... expectedIds)
+    {
+        Set<IDeletionId> actualIds = new HashSet<IDeletionId>();
+        for (Deletion deletion : deletions)
+        {
+            actualIds.add(deletion.getId());
+        }
+        assertCollectionContainsOnly(actualIds, expectedIds);
+    }
+
     protected Map<String, Attachment> assertAttachments(Collection<Attachment> attachments, AttachmentCreation... expectedAttachments)
     {
         if (expectedAttachments == null || expectedAttachments.length == 0)
@@ -1199,7 +1211,7 @@ public class AbstractTest extends SystemTestCase
         Object[][] objects = new Object[users.length][];
         for (int i = 0; i < users.length; i++)
         {
-            objects[i] = new Object[] {users[i]};
+            objects[i] = new Object[] { users[i] };
         }
         return objects;
     }
@@ -1251,7 +1263,7 @@ public class AbstractTest extends SystemTestCase
         for (RoleAssignment roleAssignment : roleAssignments)
         {
             Space space = roleAssignment.getSpace();
-            renderedAssignments.add(roleAssignment.getRoleLevel() + "_" + roleAssignment.getRole() + 
+            renderedAssignments.add(roleAssignment.getRoleLevel() + "_" + roleAssignment.getRole() +
                     (space == null ? "" : " " + space));
         }
         Collections.sort(renderedAssignments);
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java
index fb18a9ea74d9840324fc0cac46dff3f565e07206..2be796be99039524a0270196475412506be943f9 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java
@@ -18,14 +18,28 @@ package ch.ethz.sis.openbis.systemtest.asapi.v3;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 
 import org.testng.annotations.Test;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetTypeCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.PhysicalDataCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.delete.DataSetDeletionOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.FileFormatTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.ProprietaryStorageFormatPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.RelativeLocationLocatorTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.id.DataStorePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.confirm.ConfirmDeletionsOperation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.id.DeletionTechId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.id.IDeletionId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.EntityTypePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.delete.ExperimentDeletionOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentIdentifier;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.SynchronousOperationExecutionOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.delete.SampleDeletionOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
 import ch.systemsx.cisd.common.action.IDelegatedAction;
@@ -36,6 +50,102 @@ import ch.systemsx.cisd.common.action.IDelegatedAction;
 public class ConfirmDeletionTest extends AbstractDeletionTest
 {
 
+    @Test
+    public void testConfirmDeletionOfDataSetOfTypeWithDisallowDeletionFalse()
+    {
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+
+        DataSetTypeCreation typeCreation = new DataSetTypeCreation();
+        typeCreation.setCode("TYPE_WITH_DELETION_ALLOWED");
+        v3api.createDataSetTypes(sessionToken, Arrays.asList(typeCreation));
+
+        DataSetCreation dataSetCreation = dataSetCreation(typeCreation.getCode(), "DATA_SET_WITH_DELETION_ALLOWED");
+        List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(dataSetCreation));
+        DataSetPermId dataSetId = dataSetIds.get(0);
+
+        assertDataSetExists(dataSetId);
+
+        DataSetDeletionOptions options = new DataSetDeletionOptions();
+        options.setReason("testing");
+
+        IDeletionId deletionId = v3api.deleteDataSets(sessionToken, Collections.singletonList(dataSetId), options);
+
+        assertDeletionExists(deletionId);
+        assertDataSetDoesNotExist(dataSetId);
+
+        v3api.confirmDeletions(sessionToken, Collections.singletonList(deletionId));
+
+        assertDeletionDoesNotExist(deletionId);
+        assertDataSetDoesNotExist(dataSetId);
+    }
+
+    @Test
+    public void testConfirmDeletionOfDataSetOfTypeWithDisallowDeletionTrueUnforced()
+    {
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+
+        DataSetTypeCreation typeCreation = new DataSetTypeCreation();
+        typeCreation.setCode("TYPE_WITH_DELETION_DISALLOWED");
+        typeCreation.setDisallowDeletion(true);
+        v3api.createDataSetTypes(sessionToken, Arrays.asList(typeCreation));
+
+        DataSetCreation dataSetCreation = dataSetCreation(typeCreation.getCode(), "DATA_SET_WITH_DELETION_DISALLOWED");
+        List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(dataSetCreation));
+        DataSetPermId dataSetId = dataSetIds.get(0);
+
+        assertDataSetExists(dataSetId);
+
+        DataSetDeletionOptions options = new DataSetDeletionOptions();
+        options.setReason("testing");
+
+        IDeletionId deletionId = v3api.deleteDataSets(sessionToken, Collections.singletonList(dataSetId), options);
+
+        assertDeletionExists(deletionId);
+        assertDataSetDoesNotExist(dataSetId);
+
+        assertUserFailureException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    v3api.confirmDeletions(sessionToken, Collections.singletonList(deletionId));
+                }
+            }, "Deletion failed because the following data sets have 'Disallow deletion' flag set to true in their type.");
+    }
+
+    @Test
+    public void testConfirmDeletionOfDataSetOfTypeWithDisallowDeletionTrueForced()
+    {
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+
+        DataSetTypeCreation typeCreation = new DataSetTypeCreation();
+        typeCreation.setCode("TYPE_WITH_DELETION_DISALLOWED");
+        typeCreation.setDisallowDeletion(true);
+        v3api.createDataSetTypes(sessionToken, Arrays.asList(typeCreation));
+
+        DataSetCreation dataSetCreation = dataSetCreation(typeCreation.getCode(), "DATA_SET_WITH_DELETION_DISALLOWED");
+        List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(dataSetCreation));
+        DataSetPermId dataSetId = dataSetIds.get(0);
+
+        assertDataSetExists(dataSetId);
+
+        DataSetDeletionOptions options = new DataSetDeletionOptions();
+        options.setReason("testing");
+
+        IDeletionId deletionId = v3api.deleteDataSets(sessionToken, Collections.singletonList(dataSetId), options);
+
+        assertDeletionExists(deletionId);
+        assertDataSetDoesNotExist(dataSetId);
+
+        ConfirmDeletionsOperation confirmOperation = new ConfirmDeletionsOperation(Arrays.asList(deletionId));
+        confirmOperation.setForceDeletion(true);
+
+        v3api.executeOperations(sessionToken, Arrays.asList(confirmOperation), new SynchronousOperationExecutionOptions());
+
+        assertDeletionDoesNotExist(deletionId);
+        assertDataSetDoesNotExist(dataSetId);
+    }
+
     @Test
     public void testConfirmDeletionOfExperimentWithSample()
     {
@@ -218,4 +328,23 @@ public class ConfirmDeletionTest extends AbstractDeletionTest
             }, deletionId);
     }
 
+    private DataSetCreation dataSetCreation(String typeCode, String dataSetCode)
+    {
+        PhysicalDataCreation physicalCreation = new PhysicalDataCreation();
+        physicalCreation.setLocation("a/b/c");
+        physicalCreation.setFileFormatTypeId(new FileFormatTypePermId("TIFF"));
+        physicalCreation.setLocatorTypeId(new RelativeLocationLocatorTypePermId());
+        physicalCreation.setStorageFormatId(new ProprietaryStorageFormatPermId());
+
+        DataSetCreation creation = new DataSetCreation();
+        creation.setCode(dataSetCode);
+        creation.setDataSetKind(DataSetKind.PHYSICAL);
+        creation.setTypeId(new EntityTypePermId(typeCode));
+        creation.setExperimentId(new ExperimentIdentifier("/CISD/NEMO/EXP1"));
+        creation.setDataStoreId(new DataStorePermId("STANDARD"));
+        creation.setPhysicalData(physicalCreation);
+
+        return creation;
+    }
+
 }
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePersonTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePersonTest.java
index 15343e8befa7e20fd438e7854247b4a5c997c2ef..d17adfc9e9fe22d457ee4b5c68a2c78236307ea6 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePersonTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePersonTest.java
@@ -45,7 +45,7 @@ public class CreatePersonTest extends AbstractTest
         String sessionToken = v3api.login(TEST_USER, PASSWORD);
         PersonCreation personCreation = new PersonCreation();
         personCreation.setUserId("user-" + System.currentTimeMillis());
-        personCreation.setHomeSpaceId(new SpacePermId("CISD"));
+        personCreation.setSpaceId(new SpacePermId("CISD"));
         
         // When
         List<PersonPermId> persons = v3api.createPersons(sessionToken, Arrays.asList(personCreation));
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePluginTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePluginTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..28a7c20a774600dfcc79829a1e55077bad37d9b7
--- /dev/null
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePluginTest.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.systemtest.asapi.v3;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.PluginType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.ScriptType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.PluginCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.systemsx.cisd.common.action.IDelegatedAction;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class CreatePluginTest extends AbstractTest
+{
+    @Test
+    public void testCreateMinimalManagedPropertyJythonPlugin()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginCreation creation = new PluginCreation();
+        creation.setName("test " + System.currentTimeMillis());
+        creation.setScriptType(ScriptType.MANAGED_PROPERTY);
+        creation.setPluginType(PluginType.JYTHON);
+        creation.setScript("pass");
+
+        // When
+        List<PluginPermId> ids = v3api.createPlugins(sessionToken, Arrays.asList(creation));
+
+        // Then
+        assertEquals(ids.size(), 1);
+        PluginFetchOptions fetchOptions = new PluginFetchOptions();
+        fetchOptions.withRegistrator();
+        Plugin plugin = v3api.getPlugins(sessionToken, ids, fetchOptions).get(ids.get(0));
+        assertEquals(plugin.getName(), creation.getName());
+        assertEquals(plugin.getPermId().getPermId(), creation.getName());
+        assertEquals(plugin.getDescription(), null);
+        assertEquals(plugin.getEntityKinds(), null);
+        assertEquals(plugin.getScriptType(), ScriptType.MANAGED_PROPERTY);
+        assertEquals(plugin.getPluginType(), PluginType.JYTHON);
+        assertEquals(plugin.getScript(), creation.getScript());
+        assertEquals(plugin.getRegistrator().getUserId(), TEST_USER);
+        assertEquals(plugin.isAvailable(), true);
+
+        v3api.logout(sessionToken);
+    }
+    
+    @Test
+    public void testCreateDynamicPropertyJythonPlugin()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginCreation creation = new PluginCreation();
+        creation.setName("test " + System.currentTimeMillis());
+        creation.setScriptType(ScriptType.DYNAMIC_PROPERTY);
+        creation.setPluginType(PluginType.JYTHON);
+        creation.setScript("42");
+        
+        // When
+        List<PluginPermId> ids = v3api.createPlugins(sessionToken, Arrays.asList(creation));
+        
+        // Then
+        assertEquals(ids.size(), 1);
+        PluginFetchOptions fetchOptions = new PluginFetchOptions();
+        fetchOptions.withRegistrator();
+        Plugin plugin = v3api.getPlugins(sessionToken, ids, fetchOptions).get(ids.get(0));
+        assertEquals(plugin.getName(), creation.getName());
+        assertEquals(plugin.getPermId().getPermId(), creation.getName());
+        assertEquals(plugin.getDescription(), null);
+        assertEquals(plugin.getEntityKinds(), null);
+        assertEquals(plugin.getScriptType(), ScriptType.DYNAMIC_PROPERTY);
+        assertEquals(plugin.getPluginType(), PluginType.JYTHON);
+        assertEquals(plugin.getScript(), creation.getScript());
+        assertEquals(plugin.getRegistrator().getUserId(), TEST_USER);
+        assertEquals(plugin.isAvailable(), true);
+        
+        v3api.logout(sessionToken);
+    }
+    
+    @Test
+    public void testCreateEntityEvaluationJythonPlugin()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginCreation creation = new PluginCreation();
+        creation.setName("test " + System.currentTimeMillis());
+        creation.setScriptType(ScriptType.ENTITY_VALIDATION);
+        creation.setPluginType(PluginType.JYTHON);
+        creation.setScript("42");
+        creation.setEntityKind(EntityKind.DATA_SET);
+        
+        // When
+        List<PluginPermId> ids = v3api.createPlugins(sessionToken, Arrays.asList(creation));
+        
+        // Then
+        assertEquals(ids.size(), 1);
+        PluginFetchOptions fetchOptions = new PluginFetchOptions();
+        fetchOptions.withRegistrator();
+        Plugin plugin = v3api.getPlugins(sessionToken, ids, fetchOptions).get(ids.get(0));
+        assertEquals(plugin.getName(), creation.getName());
+        assertEquals(plugin.getPermId().getPermId(), creation.getName());
+        assertEquals(plugin.getDescription(), null);
+        assertEquals(plugin.getEntityKinds(), EnumSet.of(EntityKind.DATA_SET));
+        assertEquals(plugin.getScriptType(), ScriptType.ENTITY_VALIDATION);
+        assertEquals(plugin.getPluginType(), PluginType.JYTHON);
+        assertEquals(plugin.getScript(), creation.getScript());
+        assertEquals(plugin.getRegistrator().getUserId(), TEST_USER);
+        assertEquals(plugin.isAvailable(), true);
+        
+        v3api.logout(sessionToken);
+    }
+    
+    @Test
+    public void testCreatePredeployedPlugin()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginCreation creation = new PluginCreation();
+        creation.setName("test " + System.currentTimeMillis());
+        creation.setScriptType(ScriptType.DYNAMIC_PROPERTY);
+        creation.setPluginType(PluginType.PREDEPLOYED);
+        creation.setDescription("a test");
+        creation.setAvailable(false);
+        creation.setEntityKind(EntityKind.SAMPLE);
+        PluginFetchOptions fetchOptions = new PluginFetchOptions();
+        fetchOptions.withRegistrator();
+        
+        // When
+        List<PluginPermId> ids = v3api.createPlugins(sessionToken, Arrays.asList(creation));
+        
+        // Then
+        assertEquals(ids.size(), 1);
+        Plugin plugin = v3api.getPlugins(sessionToken, ids, fetchOptions).get(ids.get(0));
+        assertEquals(plugin.getName(), creation.getName());
+        assertEquals(plugin.getPermId().getPermId(), creation.getName());
+        assertEquals(plugin.getDescription(), creation.getDescription());
+        assertEquals(plugin.getEntityKinds(), EnumSet.of(creation.getEntityKind()));
+        assertEquals(plugin.getScriptType(), ScriptType.DYNAMIC_PROPERTY);
+        assertEquals(plugin.getPluginType(), PluginType.PREDEPLOYED);
+        assertEquals(plugin.getScript(), null);
+        assertEquals(plugin.getRegistrator().getUserId(), TEST_USER);
+        assertEquals(plugin.isAvailable(), false);
+        
+        v3api.logout(sessionToken);
+    }
+    
+    @Test
+    public void testMissingName()
+    {
+        PluginCreation creation = createBasic();
+        creation.setName(null);
+        assertUserFailureException(creation, "Name cannot be empty.");
+    }
+    
+    @Test
+    public void testEmptyName()
+    {
+        PluginCreation creation = createBasic();
+        creation.setName("");
+        assertUserFailureException(creation, "Name cannot be empty.");
+    }
+    
+    @Test
+    public void testMissingScriptType()
+    {
+        PluginCreation creation = createBasic();
+        creation.setScriptType(null);
+        assertUserFailureException(creation, "Script type cannot be unspecified.");
+    }
+    
+    @Test
+    public void testMissingScriptForPluginTypeJython()
+    {
+        PluginCreation creation = createBasic();
+        creation.setPluginType(PluginType.JYTHON);
+        creation.setScript(null);
+        assertUserFailureException(creation, "Script unspecified for plugin of type JYTHON.");
+    }
+    
+    @Test
+    public void testScriptForPluginTypePredeployed()
+    {
+        PluginCreation creation = createBasic();
+        creation.setPluginType(PluginType.PREDEPLOYED);
+        creation.setScript("pass");
+        assertUserFailureException(creation, "Script specified for plugin of type PREDEPLOYED.");
+    }
+    
+    @Test
+    public void testPluginTypeMissing()
+    {
+        PluginCreation creation = createBasic();
+        creation.setPluginType(null);
+        assertUserFailureException(creation, "Plugin type cannot be unspecified.");
+    }
+    
+    @Test(dataProvider = "scriptTypes")
+    public void testScriptCanNotCompile(ScriptType scriptType)
+    {
+        PluginCreation creation = createBasic();
+        creation.setPluginType(PluginType.JYTHON);
+        creation.setScriptType(scriptType);
+        creation.setScript("d:\n");
+        assertUserFailureException(creation, "SyntaxError");
+    }
+    
+    @DataProvider
+    Object[][] scriptTypes()
+    {
+        ScriptType[] values = ScriptType.values();
+        Object[][] result = new Object[values.length][];
+        for (int i = 0; i < values.length; i++)
+        {
+            result[i] = new Object[] { values[i] };
+        }
+        return result;
+    }
+
+    
+    @Test(dataProvider = "usersNotAllowedToCreatePlugins")
+    public void testCreateWithUserCausingAuthorizationFailure(final String user)
+    {
+        assertAuthorizationFailureException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    String sessionToken = v3api.login(user, PASSWORD);
+                    PluginCreation creation = createBasic();
+                    v3api.createPlugins(sessionToken, Arrays.asList(creation));
+                }
+            });
+    }
+
+    @DataProvider
+    Object[][] usersNotAllowedToCreatePlugins()
+    {
+        return createTestUsersProvider(TEST_GROUP_ADMIN, TEST_GROUP_OBSERVER, TEST_GROUP_POWERUSER,
+                TEST_INSTANCE_OBSERVER, TEST_OBSERVER_CISD, TEST_POWER_USER_CISD, TEST_SPACE_USER);
+    }
+    
+    private PluginCreation createBasic()
+    {
+        PluginCreation creation = new PluginCreation();
+        creation.setName("test " + System.currentTimeMillis());
+        creation.setScriptType(ScriptType.DYNAMIC_PROPERTY);
+        creation.setPluginType(PluginType.JYTHON);
+        creation.setDescription("a test");
+        creation.setScript("pass");
+        creation.setAvailable(false);
+        creation.setEntityKind(EntityKind.EXPERIMENT);
+        return creation;
+    }
+    
+    private void assertUserFailureException(PluginCreation creation, String expectedMessage)
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        assertUserFailureException(new IDelegatedAction()
+            {
+
+                @Override
+                public void execute()
+                {
+                    // When
+                    v3api.createPlugins(sessionToken, Arrays.asList(creation));
+                }
+            },
+
+                // Then
+                expectedMessage);
+        v3api.logout(sessionToken);
+    }
+}
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetPluginTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetPluginTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c8c251d19edef16a70e04c020bee1470a684f3e
--- /dev/null
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetPluginTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.systemtest.asapi.v3;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.testng.annotations.Test;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.PluginType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.ScriptType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public class GetPluginTest extends AbstractTest
+{
+    @Test
+    public void testGetPluginForNoSpecificEntityKind()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginPermId id = new PluginPermId("properties");
+        PluginFetchOptions fetchOptions = new PluginFetchOptions();
+        fetchOptions.withRegistrator();
+        
+        // When
+        Plugin plugin = v3api.getPlugins(sessionToken, Arrays.asList(id), fetchOptions).get(id);
+        
+        // Then
+        assertEquals(plugin.getName(), id.getPermId());
+        assertEquals(plugin.getPermId(), id);
+        assertEquals(plugin.getDescription(), "number of properties");
+        assertEquals(plugin.getEntityKinds(), null);
+        assertEquals(plugin.getPluginType(), PluginType.JYTHON);
+        assertEquals(plugin.getScriptType(), ScriptType.DYNAMIC_PROPERTY);
+        assertEquals(plugin.isAvailable(), true);
+        assertEquals(plugin.getScript(), "str(entity.properties().size()) + ' properties'");
+        assertEqualsDate(plugin.getRegistrationDate(), "2010-10-27 15:16:48");
+        assertEquals(plugin.getRegistrator().getPermId().getPermId(), "test");
+        
+        v3api.logout(sessionToken);
+    }
+    
+    @Test
+    public void testGetPluginForASpecificEntityKind()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginPermId id = new PluginPermId("testEXPERIMENT");
+        PluginFetchOptions fetchOptions = new PluginFetchOptions();
+        fetchOptions.withRegistrator();
+        
+        // When
+        Plugin plugin = v3api.getPlugins(sessionToken, Arrays.asList(id), fetchOptions).get(id);
+        
+        // Then
+        assertEquals(plugin.getName(), id.getPermId());
+        assertEquals(plugin.getPermId(), id);
+        assertEquals(plugin.getDescription(), null);
+        assertEquals(plugin.getEntityKinds().toString(), "[EXPERIMENT]");
+        assertEquals(plugin.getPluginType(), PluginType.JYTHON);
+        assertEquals(plugin.getScriptType(), ScriptType.ENTITY_VALIDATION);
+        assertEquals(plugin.isAvailable(), true);
+        assertEquals(plugin.getScript(), "import time;\ndef validate(entity, isNew):\n  pass\n ");
+        assertEqualsDate(plugin.getRegistrationDate(), "2010-10-27 15:16:48");
+        assertEquals(plugin.getRegistrator().getPermId().getPermId(), "test");
+        
+        v3api.logout(sessionToken);
+    }
+    
+    @Test
+    public void testGetPluginWithEmptyFetchOptions()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginPermId id = new PluginPermId("managed list");
+        PluginFetchOptions fetchOptions = new PluginFetchOptions();
+        
+        // When
+        Plugin plugin = v3api.getPlugins(sessionToken, Arrays.asList(id), fetchOptions).get(id);
+        
+        // Then
+        assertEquals(plugin.getName(), id.getPermId());
+        assertEquals(plugin.getPermId(), id);
+        assertEquals(plugin.getDescription(), null);
+        assertEquals(plugin.getEntityKinds(), null);
+        assertEquals(plugin.getPluginType(), PluginType.JYTHON);
+        assertEquals(plugin.getScriptType(), ScriptType.MANAGED_PROPERTY);
+        assertEquals(plugin.isAvailable(), true);
+        assertEquals(plugin.getScript(), "pass");
+        assertEqualsDate(plugin.getRegistrationDate(), "2010-10-27 15:16:48");
+        assertEquals(plugin.getFetchOptions().hasRegistrator(), false);
+        
+        v3api.logout(sessionToken);
+    }
+}
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDeletionTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDeletionTest.java
index 0dbad4e6d1fe6af49e7cb425b0011942c269e9e7..fd240f725871d51b62ce720fd913c6d26b16b35f 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDeletionTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDeletionTest.java
@@ -17,16 +17,20 @@
 package ch.ethz.sis.openbis.systemtest.asapi.v3;
 
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 
+import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.Deletion;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.fetchoptions.DeletionFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.id.IDeletionId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.search.DeletionSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.delete.ExperimentDeletionOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.delete.SampleDeletionOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException;
 
@@ -38,6 +42,81 @@ import junit.framework.Assert;
 public class SearchDeletionTest extends AbstractDeletionTest
 {
 
+    private Date beforeDate;
+
+    @BeforeTest
+    private void setBeforeDate()
+    {
+        // A deletion date is set by psql now() function which returns a start date of a database transaction. Therefore, all deletions
+        // created in the tests below will have an earlier deletion date than even a time when a given test method is entered. To be able make
+        // meaningful assertions in the tests, we construct a beforeDate here (i.e. before a database transaction is started).
+        beforeDate = new Date();
+    }
+
+    @Test
+    public void testSearchDeletionsWithIdWithOrOperator()
+    {
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+
+        DeletionFetchOptions fetchOptions = new DeletionFetchOptions();
+
+        ExperimentPermId experimentId = createCisdExperiment();
+        SamplePermId sampleId1 = createCisdSample(experimentId, "SAMPLE_TO_DELETE_1");
+        SamplePermId sampleId2 = createCisdSample(experimentId, "SAMPLE_TO_DELETE_2");
+        SamplePermId sampleId3 = createCisdSample(experimentId, "SAMPLE_TO_DELETE_3");
+
+        SampleDeletionOptions deletionOptions = new SampleDeletionOptions();
+        deletionOptions.setReason("It is just a test");
+
+        IDeletionId deletionId1 = v3api.deleteSamples(sessionToken, Collections.singletonList(sampleId1), deletionOptions);
+        IDeletionId deletionId2 = v3api.deleteSamples(sessionToken, Collections.singletonList(sampleId2), deletionOptions);
+        v3api.deleteSamples(sessionToken, Collections.singletonList(sampleId3), deletionOptions);
+
+        assertSampleDoesNotExist(sampleId1);
+        assertSampleDoesNotExist(sampleId2);
+        assertSampleDoesNotExist(sampleId3);
+
+        DeletionSearchCriteria criteria = new DeletionSearchCriteria();
+        criteria.withOrOperator();
+        criteria.withId().thatEquals(deletionId1);
+        criteria.withId().thatEquals(deletionId2);
+
+        SearchResult<Deletion> result = v3api.searchDeletions(sessionToken, criteria, fetchOptions);
+        assertDeletions(result.getObjects(), deletionId1, deletionId2);
+    }
+
+    @Test
+    public void testSearchDeletionsWithIdWithAndOperator()
+    {
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+
+        DeletionFetchOptions fetchOptions = new DeletionFetchOptions();
+
+        ExperimentPermId experimentId = createCisdExperiment();
+        SamplePermId sampleId1 = createCisdSample(experimentId, "SAMPLE_TO_DELETE_1");
+        SamplePermId sampleId2 = createCisdSample(experimentId, "SAMPLE_TO_DELETE_2");
+        SamplePermId sampleId3 = createCisdSample(experimentId, "SAMPLE_TO_DELETE_3");
+
+        SampleDeletionOptions deletionOptions = new SampleDeletionOptions();
+        deletionOptions.setReason("It is just a test");
+
+        IDeletionId deletionId1 = v3api.deleteSamples(sessionToken, Collections.singletonList(sampleId1), deletionOptions);
+        IDeletionId deletionId2 = v3api.deleteSamples(sessionToken, Collections.singletonList(sampleId2), deletionOptions);
+        v3api.deleteSamples(sessionToken, Collections.singletonList(sampleId3), deletionOptions);
+
+        assertSampleDoesNotExist(sampleId1);
+        assertSampleDoesNotExist(sampleId2);
+        assertSampleDoesNotExist(sampleId3);
+
+        DeletionSearchCriteria criteria = new DeletionSearchCriteria();
+        criteria.withAndOperator();
+        criteria.withId().thatEquals(deletionId1);
+        criteria.withId().thatEquals(deletionId2);
+
+        SearchResult<Deletion> result = v3api.searchDeletions(sessionToken, criteria, fetchOptions);
+        assertDeletions(result.getObjects());
+    }
+
     @Test
     public void testSearchDeletionsWithoutDeletedObjects()
     {
@@ -61,6 +140,8 @@ public class SearchDeletionTest extends AbstractDeletionTest
         Deletion latestDeletion = afterDeletions.get(afterDeletions.size() - 1);
         Assert.assertEquals(deletionId, latestDeletion.getId());
         Assert.assertEquals(deletionOptions.getReason(), latestDeletion.getReason());
+        assertDeletionDate(latestDeletion);
+
         try
         {
             latestDeletion.getDeletedObjects();
@@ -101,6 +182,7 @@ public class SearchDeletionTest extends AbstractDeletionTest
         Assert.assertEquals(deletionOptions.getReason(), latestDeletion.getReason());
         Assert.assertEquals(1, latestDeletion.getDeletedObjects().size());
         Assert.assertEquals(experimentId, latestDeletion.getDeletedObjects().get(0).getId());
+        assertDeletionDate(latestDeletion);
     }
 
     @Test
@@ -124,4 +206,14 @@ public class SearchDeletionTest extends AbstractDeletionTest
         Assert.assertEquals(beforeDeletions.size(), afterDeletions.size());
     }
 
+    private void assertDeletionDate(Deletion deletion)
+    {
+        Date afterDate = new Date();
+
+        Assert.assertTrue("Before date: " + beforeDate + ", deletion date: " + deletion.getDeletionDate(),
+                beforeDate.getTime() <= deletion.getDeletionDate().getTime());
+        Assert.assertTrue("After date: " + afterDate + ", deletion date: " + deletion.getDeletionDate(),
+                afterDate.getTime() >= deletion.getDeletionDate().getTime());
+    }
+
 }
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePersonTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePersonTest.java
index 30682e9a949e3b8993f86145e996fd2dd7d6c90b..40e07ba9fe9eda4f4f96603b036c1cf6aeb60bc0 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePersonTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePersonTest.java
@@ -47,7 +47,7 @@ public class UpdatePersonTest extends AbstractTest
         PersonUpdate personUpdate = new PersonUpdate();
         PersonPermId personId = new PersonPermId("homeless");
         personUpdate.setUserId(personId);
-        personUpdate.setHomeSpaceId(new SpacePermId("TEST-SPACE"));
+        personUpdate.setSpaceId(new SpacePermId("TEST-SPACE"));
         
         assertUserFailureException(new IDelegatedAction()
         {
@@ -69,7 +69,7 @@ public class UpdatePersonTest extends AbstractTest
         PersonUpdate personUpdate = new PersonUpdate();
         PersonPermId personId = new PersonPermId("homeless");
         personUpdate.setUserId(personId);
-        personUpdate.setHomeSpaceId(new SpacePermId("TESTGROUP"));
+        personUpdate.setSpaceId(new SpacePermId("TESTGROUP"));
         
         // When
         v3api.updatePersons(sessionToken, Arrays.asList(personUpdate));
@@ -90,7 +90,7 @@ public class UpdatePersonTest extends AbstractTest
         PersonUpdate personUpdate = new PersonUpdate();
         PersonPermId personId = new PersonPermId("homeless");
         personUpdate.setUserId(personId);
-        personUpdate.setHomeSpaceId(new SpacePermId("CISD"));
+        personUpdate.setSpaceId(new SpacePermId("CISD"));
         
         assertAuthorizationFailureException(new IDelegatedAction()
             {
@@ -109,7 +109,7 @@ public class UpdatePersonTest extends AbstractTest
         // Given
         String sessionToken = v3api.login(TEST_GROUP_OBSERVER, PASSWORD);
         PersonUpdate personUpdate = new PersonUpdate();
-        personUpdate.setHomeSpaceId(new SpacePermId("TESTGROUP"));
+        personUpdate.setSpaceId(new SpacePermId("TESTGROUP"));
         
         // When
         v3api.updatePersons(sessionToken, Arrays.asList(personUpdate));
@@ -131,7 +131,7 @@ public class UpdatePersonTest extends AbstractTest
         PersonUpdate personUpdate = new PersonUpdate();
         IPersonId personId = new Me();
         personUpdate.setUserId(personId);
-        personUpdate.setHomeSpaceId(new SpacePermId("TESTGROUP"));
+        personUpdate.setSpaceId(new SpacePermId("TESTGROUP"));
         
         // When
         v3api.updatePersons(sessionToken, Arrays.asList(personUpdate));
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePluginTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePluginTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4735400927eb738172c8cdbe82355e94e1bfbaa1
--- /dev/null
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePluginTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.systemtest.asapi.v3;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.PluginUpdate;
+import ch.systemsx.cisd.common.action.IDelegatedAction;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class UpdatePluginTest extends AbstractTest
+{
+    @Test
+    public void testUpdate()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PluginPermId id = new PluginPermId("properties");
+        PluginUpdate update = new PluginUpdate();
+        update.setPluginId(id);
+        update.setDescription("test");
+        update.setAvailable(false);
+        update.setScript("'test'");
+
+        // When
+        v3api.updatePlugins(sessionToken, Arrays.asList(update));
+
+        // Then
+        Plugin plugin = v3api.getPlugins(sessionToken, Arrays.asList(id), new PluginFetchOptions()).get(id);
+        assertEquals(plugin.getName(), "properties");
+        assertEquals(plugin.getScript(), update.getScript().getValue());
+        assertEquals(plugin.isAvailable(), false);
+
+        v3api.logout(sessionToken);
+    }
+
+    @Test
+    public void testPluginIdMissing()
+    {
+        PluginUpdate update = new PluginUpdate();
+        assertUserFailureException(update, "Plugin id cannot be null.");
+    }
+
+    @Test
+    public void testPluginScriptCanNotCompile()
+    {
+        PluginUpdate update = new PluginUpdate();
+        update.setPluginId(new PluginPermId("properties"));
+        update.setScript("d:");
+        assertUserFailureException(update, "SyntaxError");
+    }
+    
+    @Test(dataProvider = "usersNotAllowedToUpdatePlugins")
+    public void testCreateWithUserCausingAuthorizationFailure(final String user)
+    {
+        PluginPermId pluginId = new PluginPermId("properties");
+        assertUnauthorizedObjectAccessException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    String sessionToken = v3api.login(user, PASSWORD);
+                    PluginUpdate update = new PluginUpdate();
+                    update.setPluginId(pluginId);
+                    v3api.updatePlugins(sessionToken, Arrays.asList(update));
+                }
+            }, pluginId);
+    }
+
+    @DataProvider
+    Object[][] usersNotAllowedToUpdatePlugins()
+    {
+        return createTestUsersProvider(TEST_GROUP_ADMIN, TEST_GROUP_OBSERVER, TEST_GROUP_POWERUSER,
+                TEST_INSTANCE_OBSERVER, TEST_OBSERVER_CISD, TEST_POWER_USER_CISD, TEST_SPACE_USER);
+    }
+
+    private void assertUserFailureException(PluginUpdate update, String expectedMessage)
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        assertUserFailureException(new IDelegatedAction()
+            {
+
+                @Override
+                public void execute()
+                {
+                    // When
+                    v3api.updatePlugins(sessionToken, Arrays.asList(update));
+                }
+            },
+                // Then
+                expectedMessage);
+        v3api.logout(sessionToken);
+    }
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
index 6c16dfaa26f5ebc66c8ac2c8afeb91c226d492a8..6cf15d109c516ed9254ff78eec371d0469c83101 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
@@ -109,6 +109,12 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.IPersonId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.PersonPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.search.PersonSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.update.PersonUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create.PluginCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update.PluginUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create.ProjectCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.delete.ProjectDeletionOptions;
@@ -248,6 +254,8 @@ public interface IApplicationServerApi extends IRpcService
     public List<EntityTypePermId> createMaterialTypes(String sessionToken, List<MaterialTypeCreation> newMaterialTypes);
     
     public List<PropertyTypePermId> createPropertyTypes(String sessionToken, List<PropertyTypeCreation> newPropertyTypes);
+    
+    public List<PluginPermId> createPlugins(String sessionToken, List<PluginCreation> newPlugins);
 
     public List<VocabularyPermId> createVocabularies(String sessionToken, List<VocabularyCreation> newVocabularies);
 
@@ -292,6 +300,8 @@ public interface IApplicationServerApi extends IRpcService
     
     public void updatePropertyTypes(String sessionToken, List<PropertyTypeUpdate> propertyTypeUpdates);
 
+    public void updatePlugins(String sessionToken, List<PluginUpdate> pluginUpdates);
+    
     public void updateVocabularies(String sessionToken, List<VocabularyUpdate> vocabularyUpdates);
     
     public void updateVocabularyTerms(String sessionToken, List<VocabularyTermUpdate> vocabularyTermUpdates);
@@ -323,6 +333,8 @@ public interface IApplicationServerApi extends IRpcService
 
     public Map<IPropertyTypeId, PropertyType> getPropertyTypes(String sessionToken, List<? extends IPropertyTypeId> typeIds, PropertyTypeFetchOptions fetchOptions);
     
+    public Map<IPluginId, Plugin> getPlugins(String sessionToken, List<? extends IPluginId> pluginIds, PluginFetchOptions fetchOptions);
+    
     public Map<IVocabularyId, Vocabulary> getVocabularies(String sessionToken, List<? extends IVocabularyId> vocabularyIds,
             VocabularyFetchOptions fetchOptions);
 
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/Deletion.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/Deletion.java
index 93bb6385098e191efcb056c449df01c4935118fb..b14a065ca53f7c8b28ea868ac7dffec0a3a772f5 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/Deletion.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/Deletion.java
@@ -22,6 +22,7 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.io.Serializable;
+import java.util.Date;
 import java.util.List;
 
 /*
@@ -44,6 +45,9 @@ public class Deletion implements Serializable
     @JsonProperty
     private List<DeletedObject> deletedObjects;
 
+    @JsonProperty
+    private Date deletionDate;
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public DeletionFetchOptions getFetchOptions()
@@ -103,6 +107,19 @@ public class Deletion implements Serializable
         this.deletedObjects = deletedObjects;
     }
 
+    // Method automatically generated with DtoGenerator
+    @JsonIgnore
+    public Date getDeletionDate()
+    {
+        return deletionDate;
+    }
+
+    // Method automatically generated with DtoGenerator
+    public void setDeletionDate(Date deletionDate)
+    {
+        this.deletionDate = deletionDate;
+    }
+
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/confirm/ConfirmDeletionsOperation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/confirm/ConfirmDeletionsOperation.java
index d26aa06099c75ef2c4488f0ea112537702d1007e..090465b17d174a29e9ff39b3e3a87df30ebe6efa 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/confirm/ConfirmDeletionsOperation.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/confirm/ConfirmDeletionsOperation.java
@@ -33,6 +33,8 @@ public class ConfirmDeletionsOperation implements IOperation
 
     private List<? extends IDeletionId> deletionIds;
 
+    private boolean forceDeletion;
+
     @SuppressWarnings("unused")
     private ConfirmDeletionsOperation()
     {
@@ -48,6 +50,16 @@ public class ConfirmDeletionsOperation implements IOperation
         return deletionIds;
     }
 
+    public void setForceDeletion(boolean forceDeletion)
+    {
+        this.forceDeletion = forceDeletion;
+    }
+
+    public boolean isForceDeletion()
+    {
+        return forceDeletion;
+    }
+
     @Override
     public String getMessage()
     {
@@ -57,7 +69,8 @@ public class ConfirmDeletionsOperation implements IOperation
     @Override
     public String toString()
     {
-        return getClass().getSimpleName() + (deletionIds != null ? " " + deletionIds.size() + " deletion(s)" : "");
+        return getClass().getSimpleName()
+                + (deletionIds != null ? " " + deletionIds.size() + " deletion(s)" + (isForceDeletion() ? " forced" : "") : "");
     }
 
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/search/DeletionSearchCriteria.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/search/DeletionSearchCriteria.java
index 68cf25a1a10f7ebcdac6aa4f8e72506b24af86b4..0986903d2414e272fc692d6c41d30290404a024b 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/search/DeletionSearchCriteria.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/search/DeletionSearchCriteria.java
@@ -18,6 +18,7 @@ package ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.search;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.AbstractObjectSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchCriteriaToStringBuilder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchOperator;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion.id.IDeletionId;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
@@ -30,6 +31,16 @@ public class DeletionSearchCriteria extends AbstractObjectSearchCriteria<IDeleti
 
     private static final long serialVersionUID = 1L;
 
+    public DeletionSearchCriteria withOrOperator()
+    {
+        return (DeletionSearchCriteria) withOperator(SearchOperator.OR);
+    }
+
+    public DeletionSearchCriteria withAndOperator()
+    {
+        return (DeletionSearchCriteria) withOperator(SearchOperator.AND);
+    }
+
     @Override
     protected SearchCriteriaToStringBuilder createBuilder()
     {
@@ -37,4 +48,5 @@ public class DeletionSearchCriteria extends AbstractObjectSearchCriteria<IDeleti
         builder.setName("DELETION");
         return builder;
     }
+
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/create/PersonCreation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/create/PersonCreation.java
index 9417df0aa59c10ae702d55f80ec4beacf654c289..a4a4544cd17256b0a4071c1474c9ef974f189d63 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/create/PersonCreation.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/create/PersonCreation.java
@@ -37,7 +37,7 @@ public class PersonCreation implements ICreation, IObjectCreation
     private String userId;
     
     @JsonProperty
-    private ISpaceId homeSpaceId;
+    private ISpaceId spaceId;
 
     public String getUserId()
     {
@@ -49,14 +49,14 @@ public class PersonCreation implements ICreation, IObjectCreation
         this.userId = userId;
     }
 
-    public ISpaceId getHomeSpaceId()
+    public ISpaceId getSpaceId()
     {
-        return homeSpaceId;
+        return spaceId;
     }
 
-    public void setHomeSpaceId(ISpaceId homeSpaceId)
+    public void setSpaceId(ISpaceId spaceId)
     {
-        this.homeSpaceId = homeSpaceId;
+        this.spaceId = spaceId;
     }
     
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/update/PersonUpdate.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/update/PersonUpdate.java
index 80c5e0af1f4807b94ae26574aee55dea975b1b01..d9dd797700ca8a8b4e781ec3cf257625aa1df9da 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/update/PersonUpdate.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/update/PersonUpdate.java
@@ -41,7 +41,7 @@ public class PersonUpdate implements IUpdate, IObjectUpdate<IPersonId>
     private IPersonId userId;
 
     @JsonProperty
-    private FieldUpdateValue<ISpaceId> homeSpaceId = new FieldUpdateValue<ISpaceId>();
+    private FieldUpdateValue<ISpaceId> spaceId = new FieldUpdateValue<ISpaceId>();
     
     @JsonProperty
     private boolean active = true;
@@ -64,15 +64,15 @@ public class PersonUpdate implements IUpdate, IObjectUpdate<IPersonId>
     }
 
     @JsonIgnore
-    public void setHomeSpaceId(ISpaceId spaceId)
+    public void setSpaceId(ISpaceId spaceId)
     {
-        this.homeSpaceId.setValue(spaceId);
+        this.spaceId.setValue(spaceId);
     }
 
     @JsonIgnore
-    public FieldUpdateValue<ISpaceId> getHomeSpaceId()
+    public FieldUpdateValue<ISpaceId> getSpaceId()
     {
-        return homeSpaceId;
+        return spaceId;
     }
     
     @JsonIgnore
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/Plugin.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/Plugin.java
new file mode 100644
index 0000000000000000000000000000000000000000..bcb9600cc80f013a10788f735513d32f9ca3f796
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/Plugin.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IDescriptionHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPermIdHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationDateHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistratorHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@JsonObject("as.dto.plugin.Plugin")
+public class Plugin implements Serializable, IDescriptionHolder, IPermIdHolder, IRegistrationDateHolder, IRegistratorHolder
+{
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty
+    private PluginFetchOptions fetchOptions;
+
+    @JsonProperty
+    private String name;
+
+    @JsonProperty
+    private PluginPermId permId;
+
+    @JsonProperty
+    private String description;
+
+    @JsonProperty
+    private Person registrator;
+
+    @JsonProperty
+    private Date registrationDate;
+    
+    @JsonProperty
+    private PluginType pluginType;
+    
+    @JsonProperty
+    private Set<EntityKind> entityKinds;
+    
+    @JsonProperty
+    private ScriptType scriptType;
+    
+    @JsonProperty
+    private String script;
+    
+    @JsonProperty
+    private boolean available;
+
+    @JsonIgnore
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    @JsonIgnore
+    @Override
+    public PluginPermId getPermId()
+    {
+        return permId;
+    }
+
+    public void setPermId(PluginPermId permId)
+    {
+        this.permId = permId;
+    }
+
+    @JsonIgnore
+    @Override
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    @JsonIgnore
+    public PluginFetchOptions getFetchOptions()
+    {
+        return fetchOptions;
+    }
+
+    public void setFetchOptions(PluginFetchOptions fetchOptions)
+    {
+        this.fetchOptions = fetchOptions;
+    }
+
+    @JsonIgnore
+    @Override
+    public Person getRegistrator()
+    {
+        if (getFetchOptions() != null && getFetchOptions().hasRegistrator())
+        {
+            return registrator;
+        } else
+        {
+            throw new NotFetchedException("Registrator has not been fetched.");
+        }
+    }
+
+    public void setRegistrator(Person registrator)
+    {
+        this.registrator = registrator;
+    }
+
+    @JsonIgnore
+    @Override
+    public Date getRegistrationDate()
+    {
+        return registrationDate;
+    }
+
+    public void setRegistrationDate(Date registrationDate)
+    {
+        this.registrationDate = registrationDate;
+    }
+
+    @JsonIgnore
+    public PluginType getPluginType()
+    {
+        return pluginType;
+    }
+
+    public void setPluginType(PluginType pluginType)
+    {
+        this.pluginType = pluginType;
+    }
+
+    @JsonIgnore
+    public Set<EntityKind> getEntityKinds()
+    {
+        return entityKinds;
+    }
+
+    public void setEntityKinds(Set<EntityKind> entityKinds)
+    {
+        this.entityKinds = entityKinds;
+    }
+
+    @JsonIgnore
+    public ScriptType getScriptType()
+    {
+        return scriptType;
+    }
+
+    public void setScriptType(ScriptType scriptType)
+    {
+        this.scriptType = scriptType;
+    }
+
+    @JsonIgnore
+    public String getScript()
+    {
+        return script;
+    }
+
+    public void setScript(String script)
+    {
+        this.script = script;
+    }
+
+    @JsonIgnore
+    public boolean isAvailable()
+    {
+        return available;
+    }
+
+    public void setAvailable(boolean available)
+    {
+        this.available = available;
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/PluginType.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/PluginType.java
new file mode 100644
index 0000000000000000000000000000000000000000..b27fa1f8181fd37bb8cadf082469c2d465f8daa6
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/PluginType.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin;
+
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.PluginType")
+public enum PluginType
+{
+    JYTHON, PREDEPLOYED;
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/ScriptType.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/ScriptType.java
new file mode 100644
index 0000000000000000000000000000000000000000..be036b32600b1b5785a4d0895c3caf65d1f1b83d
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/ScriptType.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin;
+
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.ScriptType")
+public enum ScriptType
+{
+    DYNAMIC_PROPERTY, MANAGED_PROPERTY, ENTITY_VALIDATION;
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/CreatePluginsOperation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/CreatePluginsOperation.java
new file mode 100644
index 0000000000000000000000000000000000000000..f203418af4d1cae0197e8a45542bc93b23c280ba
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/CreatePluginsOperation.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.CreateObjectsOperation;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.create.CreatePluginsOperation")
+public class CreatePluginsOperation extends CreateObjectsOperation<PluginCreation>
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private CreatePluginsOperation()
+    {
+    }
+
+    public CreatePluginsOperation(PluginCreation... creations)
+    {
+        super(creations);
+    }
+
+    public CreatePluginsOperation(List<PluginCreation> creations)
+    {
+        super(creations);
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/CreatePluginsOperationResult.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/CreatePluginsOperationResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..4adcf816f0d675d023876c28f4c13737bf7bc34b
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/CreatePluginsOperationResult.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.CreateObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.create.CreatePluginsOperationResult")
+ public class CreatePluginsOperationResult extends CreateObjectsOperationResult<PluginPermId>
+ {
+
+     private static final long serialVersionUID = 1L;
+
+     @SuppressWarnings("unused")
+     private CreatePluginsOperationResult()
+     {
+     }
+
+     public CreatePluginsOperationResult(List<PluginPermId> objectIds)
+     {
+         super(objectIds);
+     }
+ }
\ No newline at end of file
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/PluginCreation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/PluginCreation.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7d256ff11e9d6c1d714e9e92490331f12fc5761
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/create/PluginCreation.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.ICreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.IObjectCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.PluginType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.ScriptType;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.create.PluginCreation")
+public class PluginCreation implements ICreation, IObjectCreation
+{
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty
+    private String name;
+    
+    @JsonProperty
+    private String description;
+    
+    @JsonProperty
+    private PluginType pluginType;
+    
+    @JsonProperty
+    private EntityKind entityKind;
+    
+    @JsonProperty
+    private ScriptType scriptType;
+    
+    @JsonProperty
+    private String script;
+    
+    @JsonProperty
+    private boolean available = true;
+
+    @JsonIgnore
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    @JsonIgnore
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    @JsonIgnore
+    public PluginType getPluginType()
+    {
+        return pluginType;
+    }
+
+    public void setPluginType(PluginType pluginType)
+    {
+        this.pluginType = pluginType;
+    }
+
+    @JsonIgnore
+    public EntityKind getEntityKind()
+    {
+        return entityKind;
+    }
+
+    public void setEntityKind(EntityKind entityKind)
+    {
+        this.entityKind = entityKind;
+    }
+
+    @JsonIgnore
+    public ScriptType getScriptType()
+    {
+        return scriptType;
+    }
+
+    public void setScriptType(ScriptType scriptType)
+    {
+        this.scriptType = scriptType;
+    }
+
+    @JsonIgnore
+    public String getScript()
+    {
+        return script;
+    }
+
+    public void setScript(String script)
+    {
+        this.script = script;
+    }
+
+    @JsonIgnore
+    public boolean isAvailable()
+    {
+        return available;
+    }
+
+    public void setAvailable(boolean available)
+    {
+        this.available = available;
+    }
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/fetchoptions/PluginFetchOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/fetchoptions/PluginFetchOptions.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f9fc7189980fcbad312b15909154dad51aec989
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/fetchoptions/PluginFetchOptions.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptionsToStringBuilder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.fetchoptions.PluginFetchOptions")
+public class PluginFetchOptions extends FetchOptions<Plugin> implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty
+    private PersonFetchOptions registrator;
+
+    @JsonProperty
+    private PluginSortOptions sort;
+
+    public PersonFetchOptions withRegistrator()
+    {
+        if (registrator == null)
+        {
+            registrator = new PersonFetchOptions();
+        }
+        return registrator;
+    }
+
+    public PersonFetchOptions withRegistratorUsing(PersonFetchOptions fetchOptions)
+    {
+        return registrator = fetchOptions;
+    }
+
+    public boolean hasRegistrator()
+    {
+        return registrator != null;
+    }
+    @Override
+    public PluginSortOptions sortBy()
+    {
+        if (sort == null)
+        {
+            sort = new PluginSortOptions();
+        }
+        return sort;
+    }
+
+    @Override
+    public PluginSortOptions getSortBy()
+    {
+        return sort;
+    }
+
+    @Override
+    protected FetchOptionsToStringBuilder getFetchOptionsStringBuilder()
+    {
+        FetchOptionsToStringBuilder f = new FetchOptionsToStringBuilder("Plugin", this);
+        f.addFetchOption("Registrator", registrator);
+        return f;
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/fetchoptions/PluginSortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/fetchoptions/PluginSortOptions.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf089af082d4f903a084fe6c9e18c298bff0c8d1
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/fetchoptions/PluginSortOptions.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortOrder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@JsonObject("as.dto.plugin.fetchoptions.PluginSortOptions")
+public class PluginSortOptions extends SortOptions<Plugin>
+{
+    private static final long serialVersionUID = 1L;
+
+    @JsonIgnore
+    public static final String NAME = "NAME";
+
+    public SortOrder code()
+    {
+        return getOrCreateSorting(NAME);
+    }
+
+    public SortOrder getCode()
+    {
+        return getSorting(NAME);
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/get/GetPluginsOperation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/get/GetPluginsOperation.java
new file mode 100644
index 0000000000000000000000000000000000000000..1201f554229a276de862ccd16aee3852685eebcb
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/get/GetPluginsOperation.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.get;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.get.GetObjectsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.get.GetPluginsOperation")
+public class GetPluginsOperation extends GetObjectsOperation<IPluginId, PluginFetchOptions>
+{
+    private static final long serialVersionUID = 1L;
+
+    @SuppressWarnings("unused")
+    private GetPluginsOperation()
+    {
+    }
+
+    public GetPluginsOperation(List<? extends IPluginId> ids, PluginFetchOptions fetchOptions)
+    {
+        super(ids, fetchOptions);
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/get/GetPluginsOperationResult.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/get/GetPluginsOperationResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc6a0457b1c04d42451441774df69c15cc5792a2
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/get/GetPluginsOperationResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.get;
+
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.get.GetObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.get.GetPluginsOperationResult")
+public class GetPluginsOperationResult extends GetObjectsOperationResult<IPluginId, Plugin>
+{
+
+    private static final long serialVersionUID = 1L;
+
+    @SuppressWarnings("unused")
+    private GetPluginsOperationResult()
+    {
+    }
+
+    public GetPluginsOperationResult(Map<IPluginId, Plugin> objectMap)
+    {
+        super(objectMap);
+    }
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/PluginUpdate.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/PluginUpdate.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d9f642a9cf5462faed63a8f288d4bc147c16c02
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/PluginUpdate.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IObjectUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.update.PluginUpdate")
+public class PluginUpdate implements IUpdate, IObjectUpdate<IPluginId>
+{
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty
+    private IPluginId pluginId;
+
+    @JsonProperty
+    private FieldUpdateValue<String> description = new FieldUpdateValue<String>();
+    
+    @JsonProperty
+    private FieldUpdateValue<String> script = new FieldUpdateValue<String>();
+    
+    @JsonProperty
+    private FieldUpdateValue<Boolean> available = new FieldUpdateValue<Boolean>();
+
+    @Override
+    @JsonIgnore
+    public IPluginId getObjectId()
+    {
+        return getPluginId();
+    }
+
+    @JsonIgnore
+    public IPluginId getPluginId()
+    {
+        return pluginId;
+    }
+
+    public void setPluginId(IPluginId pluginId)
+    {
+        this.pluginId = pluginId;
+    }
+
+    @JsonIgnore
+    public FieldUpdateValue<String> getDescription()
+    {
+        return description;
+    }
+
+    @JsonIgnore
+    public void setDescription(String description)
+    {
+        this.description.setValue(description);
+    }
+    
+    @JsonIgnore
+    public FieldUpdateValue<String> getScript()
+    {
+        return script;
+    }
+    
+    @JsonIgnore
+    public void setScript(String script)
+    {
+        this.script.setValue(script);
+    }
+
+    @JsonIgnore
+    public FieldUpdateValue<Boolean> getAvailable()
+    {
+        return available;
+    }
+
+    @JsonIgnore
+    public void setAvailable(Boolean available)
+    {
+        this.available.setValue(available);
+    }
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/UpdatePluginsOperation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/UpdatePluginsOperation.java
new file mode 100644
index 0000000000000000000000000000000000000000..f08bc5a82ef106e71025d28446b39e1370e3e37b
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/UpdatePluginsOperation.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperation;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.update.UpdatePluginsOperation")
+public class UpdatePluginsOperation extends UpdateObjectsOperation<PluginUpdate>
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private UpdatePluginsOperation()
+    {
+    }
+
+    public UpdatePluginsOperation(PluginUpdate... updates)
+    {
+        super(updates);
+    }
+
+    public UpdatePluginsOperation(List<PluginUpdate> updates)
+    {
+        super(updates);
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/UpdatePluginsOperationResult.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/UpdatePluginsOperationResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..37758df643aa9d6a236ce4a7852ebe97b8d4389e
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/plugin/update/UpdatePluginsOperationResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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,
+ * 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.
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.PluginPermId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.plugin.update.UpdatePluginsOperationResult")
+public class UpdatePluginsOperationResult extends UpdateObjectsOperationResult<PluginPermId>
+{
+
+    private static final long serialVersionUID = 1L;
+
+    @SuppressWarnings("unused")
+    private UpdatePluginsOperationResult()
+    {
+    }
+
+    public UpdatePluginsOperationResult(List<PluginPermId> ids)
+    {
+        super(ids);
+    }
+
+}
diff --git a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
index 8d52c2235190ce8ad1f8056caf8a02badf54f3cd..b2cf614468b50adddab51135a4d7e653d3620019 100644
--- a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
+++ b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
@@ -1887,6 +1887,11 @@ create Uploaded Data Set
 get Upload Id
 set Upload Id
 Uploaded Data Set Creation
+get Deletion Date
+set Deletion Date
+
+is Force Deletion
+set Force Deletion
 
 Property Type Update
 update Property Types
@@ -1897,3 +1902,39 @@ delete Property Types
 Delete Property Types Operation
 Delete Property Types Operation Result
 Property Type Deletion Options
+
+create Plugins
+Create Plugins Operation
+Create Plugins Operation Result
+DYNAMIC_PROPERTY
+ENTITY_VALIDATION
+get EntityKinds
+get Name
+get Plugins
+Get Plugins Operation
+Get Plugins Operation Result
+get Plugin Type
+get Script
+get Script Type
+is Available
+JYTHON
+PREDEPLOYED
+MANAGED_PROPERTY
+NAME
+Plugin
+Plugin Creation
+Plugin Fetch Options
+Plugin Sort Options
+Plugin Type
+Script Type
+set Available
+set Entity Kinds
+set Plugin Type
+set Script
+set Script Type
+get Available
+Plugin Update
+update Plugins
+Update Plugins Operation
+Update Plugins Operation Result
+