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 cea48d55fe324a52df6b32843f00aaa3d52ba5c4..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, 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.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 fd7fb99e370204fd174f64c1c8e575375838ab91..95925de5c24e5ff84e9c0996c1ed8f5a6d0edeb6 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");
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-update.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js
index e5db760f8a3fc0eec817cc532030a0c31685cdce..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");
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/common/get/AbstractGetObjectsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/AbstractGetObjectsOperationExecutor.java
index 00c85047085d96909289eb6217b711cf72b6852f..2ced14b81ed9eed9d5bde3b2f2afe72f2be1e367 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/AbstractGetObjectsOperationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/AbstractGetObjectsOperationExecutor.java
@@ -67,7 +67,7 @@ public abstract class AbstractGetObjectsOperationExecutor<OBJECT_ID extends IObj
         
         // sort and page the objects internal collections - ignore the top level changes
         // (we want to maintain all the results and keep them in order of the passed ids)
-        new SortAndPage().sortAndPage(idToObjectMap.values(), operation.getFetchOptions());
+        new SortAndPage().sortAndPage(idToObjectMap.values(), null, operation.getFetchOptions());
 
         return getOperationResult(idToObjectMap);
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java
index 8abc1cce9a98236f9da569b064b0b75bf848c20a..fe8b92e6a27c171687e1eaa6c55c16b93d834f17 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java
@@ -77,7 +77,7 @@ public abstract class AbstractSearchObjectsOperationExecutor<OBJECT, OBJECT_PE,
         }
 
         Collection<OBJECT> allResults = searchAndTranslate(context, criteria, fetchOptions);
-        List<OBJECT> sortedAndPaged = sortAndPage(context, allResults, fetchOptions);
+        List<OBJECT> sortedAndPaged = sortAndPage(context, allResults, criteria, fetchOptions);
 
         SearchResult<OBJECT> searchResult = new SearchResult<OBJECT>(sortedAndPaged, allResults.size());
         return getOperationResult(searchResult);
@@ -119,7 +119,7 @@ public abstract class AbstractSearchObjectsOperationExecutor<OBJECT, OBJECT_PE,
         return objects;
     }
 
-    private List<OBJECT> sortAndPage(IOperationContext context, Collection<OBJECT> results, FETCH_OPTIONS fetchOptions)
+    private List<OBJECT> sortAndPage(IOperationContext context, Collection<OBJECT> results,  CRITERIA criteria, FETCH_OPTIONS fetchOptions)
     {
         if (results == null || results.isEmpty())
         {
@@ -127,7 +127,7 @@ public abstract class AbstractSearchObjectsOperationExecutor<OBJECT, OBJECT_PE,
         }
 
         SortAndPage sap = new SortAndPage();
-        Collection<OBJECT> objects = sap.sortAndPage(results, fetchOptions);
+        Collection<OBJECT> objects = sap.sortAndPage(results, criteria, fetchOptions);
 
         operationLog.info("Return " + objects.size() + " object(s) after sorting and paging.");
 
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/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/authorizationgroup/AuthorizationGroupComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/authorizationgroup/AuthorizationGroupComparatorFactory.java
index ab031cbce349128caf9e51e454fd4de75981cebd..67a721fe9ff5e8424c949b31ed8f92d4e418f2e5 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/authorizationgroup/AuthorizationGroupComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/authorizationgroup/AuthorizationGroupComparatorFactory.java
@@ -17,9 +17,12 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.authorizationgroup;
 
 import java.util.Comparator;
+import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.AuthorizationGroup;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.fetchoptions.AuthorizationGroupSortOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.ComparatorFactory;
 
@@ -38,7 +41,7 @@ public class AuthorizationGroupComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<AuthorizationGroup> getComparator(String field)
+    public Comparator<AuthorizationGroup> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (AuthorizationGroupSortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java
index f63b3cfb13dcac2a429e5982effde3d11db5fce9..5ef251a313681ed659e72476d5f15702a8119fcb 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.experiment;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.EntityWithPropertiesComparatorFactory;
@@ -36,13 +39,13 @@ public class ExperimentComparatorFactory extends EntityWithPropertiesComparatorF
     }
 
     @Override
-    public Comparator<Experiment> getComparator(String field)
+    public Comparator<Experiment> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (ExperimentSortOptions.IDENTIFIER.equals(field))
         {
             return new IdentifierComparator<Experiment>();
         }
-        return super.getComparator(field);
+        return super.getComparator(field, parameters, criteria);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java
index ee861127a9479ed7ad9fe272f3b10267c223b56c..51d0e92d3478a1655c5569ae1789733fbd6c726d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java
@@ -18,7 +18,10 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.globalsearch;
 
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.global.GlobalSearchObject;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.global.fetchoptions.GlobalSearchObjectSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.ComparatorFactory;
@@ -37,7 +40,7 @@ public class GlobalSearchObjectComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<GlobalSearchObject> getComparator(String field)
+    public Comparator<GlobalSearchObject> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (GlobalSearchObjectSortOptions.SCORE.equals(field))
         {
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..7e53021ebf677de23a8d0ad1d32b2cd75aaeceba
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java
@@ -0,0 +1,67 @@
+/*
+ * 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 java.util.Map;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
+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, Map<SortParameter, String> parameters, ISearchCriteria criteria)
+    {
+        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/project/ProjectComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/project/ProjectComparatorFactory.java
index ed573d531597e6c4878586dd40d82b4b30dbca57..b469076fd30d10b3198d8a428f18010eda805888 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/project/ProjectComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/project/ProjectComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.project;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions.ProjectSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.EntityComparatorFactory;
@@ -36,13 +39,13 @@ public class ProjectComparatorFactory extends EntityComparatorFactory<Project>
     }
 
     @Override
-    public Comparator<Project> getComparator(String field)
+    public Comparator<Project> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (ProjectSortOptions.IDENTIFIER.equals(field))
         {
             return new IdentifierComparator<Project>();
         }
-        return super.getComparator(field);
+        return super.getComparator(field, parameters, criteria);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java
index 251b1e47e6ce9f6e24d2712d3194a33341a6c80f..93b07657b74121a7b1661b68ac6a50b1a01f279f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java
@@ -20,6 +20,8 @@ import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyAssignmentSortOptions;
@@ -69,7 +71,7 @@ public class PropertyAssignmentComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<?> getComparator(String field)
+    public Comparator<?> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         return COMPARATORS_BY_FIELD.get(field);
     }
@@ -77,7 +79,7 @@ public class PropertyAssignmentComparatorFactory extends ComparatorFactory
     @Override
     public Comparator<?> getDefaultComparator()
     {
-        return getComparator(PropertyAssignmentSortOptions.ORDINAL);
+        return getComparator(PropertyAssignmentSortOptions.ORDINAL, null, null);
     }
 
     private abstract static class AbstractPropertyAssignmentComparator extends AbstractStringComparator<PropertyAssignment>
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java
index ef68061cd5891c649d89942ebe5f7949aa52b019..f8c044fa6956b20ee6e015144637a0a3b52b10aa 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.property;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyTypeSortOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularySortOptions;
@@ -38,7 +41,7 @@ public class PropertyTypeComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<PropertyType> getComparator(String field)
+    public Comparator<PropertyType> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (VocabularySortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java
index 4218ac4f930ce5bdd693a38c79dde335ef794b4c..8a1c3ddb2d2c19e2d8a0c32568fc2a557438b771 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sample;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.EntityWithPropertiesComparatorFactory;
@@ -36,13 +39,13 @@ public class SampleComparatorFactory extends EntityWithPropertiesComparatorFacto
     }
 
     @Override
-    public Comparator<Sample> getComparator(String field)
+    public Comparator<Sample> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (SampleSortOptions.IDENTIFIER.equals(field))
         {
             return new IdentifierComparator<Sample>();
         }
-        return super.getComparator(field);
+        return super.getComparator(field, parameters, criteria);
     }
 
 }
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 3a8e1b7b56ce24b16a80c4348fd16c24fce4e17c..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
@@ -19,12 +19,16 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort;
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.authorizationgroup.AuthorizationGroupComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.dataset.DataSetComparatorFactory;
 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;
@@ -57,12 +61,13 @@ public abstract class ComparatorFactory
         factories.add(new DataSetComparatorFactory());
         factories.add(new MaterialComparatorFactory());
         factories.add(new PropertyTypeComparatorFactory());
+        factories.add(new PluginComparatorFactory());
         factories.add(new PropertyAssignmentComparatorFactory());
     }
 
     public abstract boolean accepts(Class<?> sortOptionsClass);
 
-    public abstract Comparator getComparator(String field);
+    public abstract Comparator getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria);
 
     public abstract Comparator getDefaultComparator();
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java
index 88deff478fa9755fa49d8bd693a0cde6784363c9..213a197831c0dde96a9e2651166fc5221f49f3d5 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java
@@ -17,12 +17,15 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort;
 
 import java.util.Comparator;
+import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.EntitySortOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IModificationDateHolder;
 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.search.ISearchCriteria;
 
 /**
  * @author pkupczyk
@@ -38,7 +41,7 @@ public class EntityComparatorFactory<OBJECT extends ICodeHolder & IPermIdHolder
     }
 
     @Override
-    public Comparator<OBJECT> getComparator(String field)
+    public Comparator<OBJECT> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (EntitySortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java
index 0ba498077cf6387dc6e502ca08d367b5e4d9ae22..e57c58e1198c839afe649fc9fcd1bec7028da1ad 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java
@@ -17,14 +17,17 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort;
 
 import java.util.Comparator;
+import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.EntityWithPropertiesSortOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IEntityTypeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IModificationDateHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPermIdHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationDateHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 
 /**
  * @author pkupczyk
@@ -40,9 +43,12 @@ public class EntityWithPropertiesComparatorFactory<OBJECT extends ICodeHolder &
     }
 
     @Override
-    public Comparator<OBJECT> getComparator(String field)
+    public Comparator<OBJECT> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
-        if (field.equals(EntityWithPropertiesSortOptions.TYPE))
+    	if (field.equals(EntityWithPropertiesSortOptions.FETCHED_FIELDS_SCORE))
+        {
+            return new FetchedFieldsScoreComparator<OBJECT>(parameters, criteria);
+        } if (field.equals(EntityWithPropertiesSortOptions.TYPE))
         {
             return new TypeComparator<OBJECT>();
         } else if (field.startsWith(EntityWithPropertiesSortOptions.PROPERTY))
@@ -50,7 +56,7 @@ public class EntityWithPropertiesComparatorFactory<OBJECT extends ICodeHolder &
             return new PropertyComparator<OBJECT>(field.substring(EntityWithPropertiesSortOptions.PROPERTY.length()));
         } else
         {
-            return super.getComparator(field);
+            return super.getComparator(field, parameters, criteria);
         }
     }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/FetchedFieldsScoreComparator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/FetchedFieldsScoreComparator.java
new file mode 100644
index 0000000000000000000000000000000000000000..c02894de135373f3eb7d0ad30aab2b035979a69d
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/FetchedFieldsScoreComparator.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2016 ETH Zuerich, CISD
+ *
+ * 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.sort;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IEntityTypeHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPermIdHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.AbstractEntitySearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.CodeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.StringPropertySearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.DataSetTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.search.AbstractEntityTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.search.ExperimentTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.Material;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.search.MaterialTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
+
+
+/**
+ * @author juanf
+ */
+public class FetchedFieldsScoreComparator<OBJECT extends IEntityTypeHolder & IPropertiesHolder & ICodeHolder & IPermIdHolder> extends AbstractComparator<OBJECT, Integer>
+{
+
+	private Map<SortParameter, String> parameters;
+	private AbstractEntitySearchCriteria criteria;
+    private List<Pattern> partialMatchTerms = new ArrayList<Pattern>();
+    private List<String> exactMatchTerms = new ArrayList<String>();
+    private List<Boost> boosts = new ArrayList<Boost>();
+    private Map<OBJECT, Integer> scoreCache = new HashMap<>();
+    
+    private Integer fullCodeBoost = 0;
+    private Integer partialCodeBoost = 0;
+    private Integer fullPropertyBoost = 0;
+    private Integer fullTypeBoost = 0;
+    private Integer partialPropertyBoost = 0;
+	
+	public FetchedFieldsScoreComparator(Map<SortParameter, String> parameters, ISearchCriteria criteria) 
+	{
+		if (criteria == null || (criteria instanceof AbstractEntitySearchCriteria) == false)
+        {
+            throw new IllegalArgumentException("Missing criteria");
+        }
+        
+        if (parameters == null)
+        {
+            throw new IllegalArgumentException("Missing score parameters");
+        } else 
+        {
+	        	if(parameters.containsKey(SortParameter.FULL_CODE_BOOST)) 
+	        	{
+	        		fullCodeBoost = Integer.parseInt(parameters.get(SortParameter.FULL_CODE_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.PARTIAL_CODE_BOOST)) 
+	        	{
+	        		partialCodeBoost = Integer.parseInt(parameters.get(SortParameter.PARTIAL_CODE_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.FULL_PROPERTY_BOOST)) 
+	        	{
+	        		fullPropertyBoost = Integer.parseInt(parameters.get(SortParameter.FULL_PROPERTY_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.FULL_TYPE_BOOST)) 
+	        	{
+	        		fullTypeBoost = Integer.parseInt(parameters.get(SortParameter.FULL_TYPE_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.PARTIAL_PROPERTY_BOOST)) 
+	        	{
+	        		partialPropertyBoost = Integer.parseInt(parameters.get(SortParameter.PARTIAL_PROPERTY_BOOST));
+	        	}
+        }
+        
+		this.parameters = parameters;
+		this.criteria = (AbstractEntitySearchCriteria) criteria;
+		
+        // Shared
+        this.partialMatchTerms = new ArrayList<Pattern>();
+        this.exactMatchTerms = new ArrayList<String>();
+        this.boosts = new ArrayList<Boost>();
+
+        for(ISearchCriteria subCriteria:this.criteria.getCriteria()) 
+        {
+        		ISearchCriteriaParser<ISearchCriteria> parser = criteriaParsers.get(subCriteria.getClass());
+        		
+        		if(parser != null) 
+        		{
+        			String value = parser.getValue(subCriteria);
+        			
+        			// Full Index
+        			partialMatchTerms.add(getPartialMatchTerm(value));
+        			exactMatchTerms.add(getExactMatchTerm(value));
+        			boosts.add(parser.getBoost(subCriteria, 10));
+
+        			// Split Index
+        			String[] splitIndexes = value.replace("*", " ").replace("?", " ").replaceAll("\\s+", " ").trim().split(" ");
+
+        			for (String splitIndex : splitIndexes)
+        			{
+        				partialMatchTerms.add(getPartialMatchTerm(splitIndex));
+        				exactMatchTerms.add(getExactMatchTerm(splitIndex));
+        				boosts.add(parser.getBoost(subCriteria, 1));
+        			}
+        		}
+        		
+        }
+        
+	}
+    
+	@Override
+    public int compare(OBJECT o1, OBJECT o2)
+    {
+        return -1 * super.compare(o1, o2); // Higher scores first
+    }
+	
+    @Override
+    protected Integer getValue(OBJECT o)
+    {
+    		Integer score = scoreCache.get(o);
+    		if(score == null) {
+    			score = calculateScore(o);
+    			scoreCache.put(o, score);
+    		}
+    		return score;
+    }
+    
+    //
+    // V3 Helper Methods
+    //
+    
+    private static Map<Class, ISearchCriteriaParser> criteriaParsers = new HashMap<>(3);
+    
+    static {
+    		criteriaParsers.put(CodeSearchCriteria.class, new CodeCriteriaParser());
+    		criteriaParsers.put(StringPropertySearchCriteria.class, new StringPropertySearchCriteriaParser());
+    		criteriaParsers.put(SampleTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    		criteriaParsers.put(ExperimentTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    		criteriaParsers.put(DataSetTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    		criteriaParsers.put(MaterialTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    }
+
+    private static interface ISearchCriteriaParser<ISearchCriteria> {
+    		public String getValue(ISearchCriteria criteria);
+    		public Boost getBoost(ISearchCriteria criteria, int boost);
+    }
+    
+    private static class CodeCriteriaParser implements ISearchCriteriaParser<CodeSearchCriteria> {
+		@Override
+		public String getValue(CodeSearchCriteria criteria) {
+			return criteria.getFieldValue().getValue();
+		}
+
+		@Override
+		public Boost getBoost(CodeSearchCriteria criteria, int boost) {
+			return new Boost(boost, 0, 0, 0, null);
+		}
+    }
+    
+    private static class StringPropertySearchCriteriaParser implements ISearchCriteriaParser<StringPropertySearchCriteria> {
+		@Override
+		public String getValue(StringPropertySearchCriteria criteria) {
+			return criteria.getFieldValue().getValue();
+		}
+
+		@Override
+		public Boost getBoost(StringPropertySearchCriteria criteria, int boost) {
+			return new Boost(0, 0, 0, boost, criteria.getFieldName());
+		}
+    }
+    
+    private static class AbstractEntityTypeSearchCriteriaParser implements ISearchCriteriaParser<AbstractEntityTypeSearchCriteria> {
+		@Override
+		public String getValue(AbstractEntityTypeSearchCriteria criteria) {
+			for(ISearchCriteria subCriteria:criteria.getCriteria()) {
+				if(subCriteria instanceof CodeSearchCriteria) {
+					return criteriaParsers.get(subCriteria.getClass()).getValue(subCriteria);
+				}
+			}
+			return null;
+		}
+
+		@Override
+		public Boost getBoost(AbstractEntityTypeSearchCriteria criteria, int boost) {
+			return new Boost(0, boost, 0, 0, null);
+		}
+    }
+    
+    //
+    // Scoring algorithm
+    //
+	
+    private boolean hasType(OBJECT o ) {
+    		if(o instanceof Sample) {
+    			return ((Sample)o).getFetchOptions().hasType();
+    		} else if(o instanceof Experiment) {
+    			return ((Experiment)o).getFetchOptions().hasType();
+    		} if(o instanceof DataSet) {
+    			return ((DataSet)o).getFetchOptions().hasType();
+    		} if(o instanceof Material) {
+    			return ((Material)o).getFetchOptions().hasType();
+    		} else {
+    			return false;
+    		}
+    }
+    
+    private boolean hasProperties(OBJECT o ) {
+		if(o instanceof Sample) {
+			return ((Sample)o).getFetchOptions().hasProperties();
+		} else if(o instanceof Experiment) {
+			return ((Experiment)o).getFetchOptions().hasProperties();
+		} if(o instanceof DataSet) {
+			return ((DataSet)o).getFetchOptions().hasProperties();
+		} if(o instanceof Material) {
+			return ((Material)o).getFetchOptions().hasProperties();
+		} else {
+			return false;
+		}
+}
+    
+	private Integer calculateScore(OBJECT o ) {
+		int score = 0;
+		String code = o.getCode();
+		String typeCode = (hasType(o))?o.getType().getCode():null;
+		Map<String, String> properties = (hasProperties(o))?o.getProperties():null;
+		
+		for (int i = 0; i < exactMatchTerms.size(); i++)
+	    {
+	        Pattern partialTerm = partialMatchTerms.get(i);
+	        String exactTerm = exactMatchTerms.get(i);
+	        Boost boost = boosts.get(i);
+	
+	        // 1. Code
+	        if(code != null) {
+	        		if (isPartialMatch(code, partialTerm))
+	            { // If code matches partially
+	                score += partialCodeBoost * boost.getCodeBoost();
+	                if (isExactMatch(code, exactTerm))
+	                { // If code matches exactly
+	                    score += fullCodeBoost * boost.getCodeBoost();
+	                }
+	            }
+	        }
+	
+	        // 2. Entity type code
+	        if(typeCode != null) 
+	        {
+	        		if (isExactMatch(typeCode, exactTerm))
+	            { // If type matches exactly
+	                score += fullTypeBoost * boost.getTypeCodeBoost();
+	            }
+	        }
+	        
+	
+	        // 3. Properties
+	        if(properties != null) {
+	        		if (properties != null && properties.keySet() != null)
+	            {
+	                for (String propertykey : properties.keySet())
+	                {
+	                    String propertyValue = properties.get(propertykey);
+	                    if (isPartialMatch(propertyValue, partialTerm))
+	                    { // If property matches partially
+	                        score += partialPropertyBoost * boost.getPropertyBoost(propertykey);
+	                        if (isExactMatch(propertyValue, exactTerm))
+	                        { // If property matches exactly
+	                            score += fullPropertyBoost * boost.getPropertyBoost(propertykey);
+	                        }
+	                    }
+	                }
+	            }
+	        }
+	    }
+		
+		return score;
+	}
+	
+    //
+    // Helper Methods
+    //
+    
+    private static class Boost
+    {
+        private int codeBoost;
+
+        private int typeCodeBoost;
+
+        private int propertyBoost;
+
+        private int propertyDefaultBoost;
+
+        private String propertyName;
+
+        public Boost(int codeBoost, int typeCodeBoost, int propertyDefaultBoost, int propertyBoost, String propertyName)
+        {
+            super();
+            this.codeBoost = codeBoost;
+            this.typeCodeBoost = typeCodeBoost;
+            this.propertyDefaultBoost = propertyDefaultBoost;
+            this.propertyBoost = propertyBoost;
+            this.propertyName = propertyName;
+        }
+
+        public int getCodeBoost()
+        {
+            return codeBoost;
+        }
+
+        public int getTypeCodeBoost()
+        {
+            return typeCodeBoost;
+        }
+
+        public int getPropertyBoost(String propertyNameToBoost)
+        {
+            if (this.propertyName != null && this.propertyName.equals(propertyNameToBoost))
+            {
+                return propertyBoost;
+            } else
+            {
+                return propertyDefaultBoost;
+            }
+        }
+
+    }
+
+    private Pattern getPartialMatchTerm(String term)
+    {
+        return Pattern.compile(("*" + term + "*").replace("*", ".*").replace("?", ".?"), Pattern.CASE_INSENSITIVE);
+    }
+
+    private String getExactMatchTerm(String term)
+    {
+        return term.replace("*", "").replace("?", "");
+    }
+
+    private boolean isExactMatch(String value, String term)
+    {
+        if (value != null && term != null)
+        {
+            return value.equalsIgnoreCase(term);
+        } else
+        {
+            return false;
+        }
+    }
+
+    private boolean isPartialMatch(String value, Pattern pattern)
+    {
+        if (value != null && pattern != null)
+        {
+            return pattern.matcher(value).matches();
+        } else
+        {
+            return false;
+        }
+    }
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
index 63d9eba47c8a832aaf58eacbccf6945ac0090d39..80afc702f5a927b4b90e14365330e6f6e79ed41f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
@@ -37,6 +37,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.Sorting;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.view.AbstractCollectionView;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.view.ListView;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.view.SetView;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 
 /**
  * @author pkupczyk
@@ -49,7 +50,7 @@ public class SortAndPage
 
     private MethodsCache methodsCache = new MethodsCache();
 
-    public <T, C extends Collection<T>> C sortAndPage(C objects, FetchOptions fo)
+    public <T, C extends Collection<T>> C sortAndPage(C objects, ISearchCriteria c, FetchOptions fo)
     {
         C newObjects = objects;
 
@@ -58,21 +59,21 @@ public class SortAndPage
             newObjects = (C) ((AbstractCollectionView) objects).getOriginalCollection();
         }
 
-        newObjects = (C) sort(newObjects, fo);
+        newObjects = (C) sort(newObjects, c, fo);
         newObjects = (C) page(newObjects, fo);
-        nest(newObjects, fo);
+        nest(newObjects, c, fo);
 
         return newObjects;
     }
-
-    private Collection sort(Collection objects, FetchOptions fo)
+    
+    private Collection sort(Collection objects, ISearchCriteria c, FetchOptions fo)
     {
         if (objects == null || objects.isEmpty())
         {
             return objects;
         }
 
-        Comparator comparator = getComparator(fo);
+        Comparator comparator = getComparator(c, fo);
 
         if (comparator != null)
         {
@@ -135,7 +136,7 @@ public class SortAndPage
         }
     }
 
-    private void nest(Collection objects, FetchOptions fo)
+    private void nest(Collection objects, ISearchCriteria c, FetchOptions fo)
     {
         if (objects == null || objects.isEmpty())
         {
@@ -177,14 +178,14 @@ public class SortAndPage
                         {
                             if (value instanceof Collection)
                             {
-                                Collection newValue = sortAndPage((Collection) value, subFo);
+                                Collection newValue = sortAndPage((Collection) value, c, subFo);
                                 setMethod.invoke(object, newValue);
                             } else if (value instanceof Map)
                             {
-                                sortAndPage(((Map) value).values(), subFo);
+                                sortAndPage(((Map) value).values(), c, subFo);
                             } else
                             {
-                                Collection newValue = sortAndPage(Collections.singleton(value), subFo);
+                                Collection newValue = sortAndPage(Collections.singleton(value), c, subFo);
                                 if (setMethod != null)
                                 {
                                     setMethod.invoke(object, newValue.iterator().next());
@@ -200,7 +201,7 @@ public class SortAndPage
         }
     }
 
-    protected Comparator getComparator(FetchOptions fetchOptions)
+    protected Comparator getComparator(ISearchCriteria criteria, FetchOptions fetchOptions)
     {
         if (fetchOptions == null)
         {
@@ -240,7 +241,7 @@ public class SortAndPage
                             throw new IllegalArgumentException("Comparator factory for sort by " + sortByClass + " not found");
                         }
 
-                        Comparator aComparator = comparatorFactory.getComparator(sorting.getField());
+                        Comparator aComparator = comparatorFactory.getComparator(sorting.getField(), sorting.getParameters(), criteria);
 
                         if (aComparator == null)
                         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java
index 0ae8a397c2f22dac8df5f4c86dc90d9b0e2e8e65..598231a9f07c14cc2f6cae2f2db2af44bfd92b25 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.fetchoptions.TagSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
@@ -37,7 +40,7 @@ public class TagComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<Tag> getComparator(String field)
+    public Comparator<Tag> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (TagSortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java
index a6740821d46a54076e9774439a2cbebe95b88f10..dab46dbd380183d7a2f5ae4c48c2074d0374499c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.vocabulary;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularySortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
@@ -37,7 +40,7 @@ public class VocabularyComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<Vocabulary> getComparator(String field)
+    public Comparator<Vocabulary> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (VocabularySortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java
index 7d4fb30ccc8d5758753674bdaea8ad3b3c2d40c6..35bdb6d11d92c8c323e1ed42e861e2f7620e1780 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.vocabulary;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.VocabularyTerm;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularyTermSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
@@ -36,7 +39,7 @@ public class VocabularyTermComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<VocabularyTerm> getComparator(String field)
+    public Comparator<VocabularyTerm> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (VocabularyTermSortOptions.CODE.equals(field))
         {
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/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
index 5763f56f49b6e009bdd368c5c2625f408b8268a5..03e40acf46d9f60659637643f7d2eefd59712ab5 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
@@ -105,7 +105,7 @@ public class SampleTypeTranslator extends AbstractCachingTranslator<Long, Sample
         {
             Collection<PropertyAssignment> assignments = relations.get(ISamplePropertyAssignmentTranslator.class, typeId);
             List<PropertyAssignment> propertyAssignments = new ArrayList<>(assignments);
-            result.setPropertyAssignments(new SortAndPage().sortAndPage(propertyAssignments, fetchOptions.withPropertyAssignments()));
+            result.setPropertyAssignments(new SortAndPage().sortAndPage(propertyAssignments, null, fetchOptions.withPropertyAssignments()));
         }
 
         if (fetchOptions.hasSemanticAnnotations())
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/common/fetchoptions/EntityWithPropertiesSortOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/EntityWithPropertiesSortOptions.js
index a9b6cce007cc153b3f78df8ad64ab9e32e818a1c..becefb67a87eb5f43cab8edb1b5958b66aa2d990 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/EntityWithPropertiesSortOptions.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/EntityWithPropertiesSortOptions.js
@@ -1,17 +1,29 @@
-define([ "require", "stjs", "as/dto/common/fetchoptions/EntitySortOptions" ], function(require, stjs, EntitySortOptions) {
+define([ "require", "stjs", "as/dto/common/fetchoptions/EntitySortOptions", "as/dto/common/fetchoptions/SortParameter" ], function(require, stjs, EntitySortOptions, SortParameter) {
 	var EntityWithPropertiesSortOptions = function() {
 		EntitySortOptions.call(this);
 	};
 
 	var fields = {
+		FETCHED_FIELDS_SCORE : "FETCHED_FIELDS_SCORE",
 		TYPE : "TYPE",
 		PROPERTY : "PROPERTY"
 	};
-
+    
 	stjs.extend(EntityWithPropertiesSortOptions, EntitySortOptions, [ EntitySortOptions ], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.common.fetchoptions.EntityWithPropertiesSortOptions';
 		constructor.serialVersionUID = 1;
 
+		prototype.fetchedFieldsScore = function() {
+			var parameters = {
+				SortParameter.FULL_CODE_BOOST : 		"1000000",
+				SortParameter.PARTIAL_CODE_BOOST : 	 "100000",
+				SortParameter.FULL_PROPERTY_BOOST :    "10000",
+				SortParameter.FULL_TYPE_BOOST : 		   "1000",
+				SortParameter.PARTIAL_PROPERTY_BOOST :   "100"
+			};
+			return this.getOrCreateSortingWithParameters(fields.FETCHED_FIELDS_SCORE, parameters);
+		};
+		
 		prototype.type = function() {
 			return this.getOrCreateSorting(fields.TYPE);
 		};
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js
index d9dc882cef503ae2f1a601a223fc8bca35e6a283..588341e28fc49f4418a1f61716ad1a390e738c3e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js
@@ -6,14 +6,20 @@ define([ "require", "stjs", "as/dto/common/fetchoptions/SortOrder", "as/dto/comm
 	stjs.extend(SortOptions, null, [], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.common.fetchoptions.SortOptions';
 		constructor.serialVersionUID = 1;
+		
 		prototype.getOrCreateSorting = function(field) {
+			return this.getOrCreateSortingWithParameters(field, null);
+		};
+		
+		prototype.getOrCreateSortingWithParameters = function(field, parameters) {
 			var order = this.getSorting(field);
 			if (order == null) {
 				order = new SortOrder();
-				this.sortings.push(new Sorting(field, order));
+				this.sortings.push(new Sorting(field, order, parameters));
 			}
 			return order;
 		};
+		
 		prototype.getSorting = function(field) {
 			var order = null;
 			this.sortings.forEach(function(sorting) {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortParameter.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortParameter.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd223f6c74c9e0cfb6a0ee77d4f5c5543d188b2d
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortParameter.js
@@ -0,0 +1,12 @@
+/**
+ * @author juanf
+ */
+
+define([ "stjs", "as/dto/common/Enum" ], function(stjs, Enum) {
+	var SortParameter = function() {
+		Enum.call(this, [ "FULL_CODE_BOOST", "PARTIAL_CODE_BOOST", "FULL_PROPERTY_BOOST", "FULL_TYPE_BOOST", "PARTIAL_PROPERTY_BOOST" ]);
+	};
+	stjs.extend(SortParameter, Enum, [ Enum ], function(constructor, prototype) {
+	}, {});
+	return new SortParameter();
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js
index 534db78593281c3681b2b8fbc8b1c64132f661c9..2cd97cffec8146c3b863aca403ccd27cd45cb715 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js
@@ -1,7 +1,8 @@
 define([ "require", "stjs" ], function(require, stjs) {
-	var Sorting = function(field, order) {
+	var Sorting = function(field, order, parameters) {
 		this.field = field;
 		this.order = order;
+		this.parameters = parameters;
 	};
 
 	stjs.extend(Sorting, null, [], function(constructor, prototype) {
@@ -13,6 +14,9 @@ define([ "require", "stjs" ], function(require, stjs) {
 		prototype.getOrder = function() {
 			return this.order;
 		};
+		prototype.getParameters = function() {
+			return this.parameters;
+		};
 	}, {});
 	return Sorting;
 })
\ No newline at end of file
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 e33fd06d74e93abd90f42a3bc18c2c732085763d..54b99d30d62f17649653b6a2f34b21f123d8f6ab 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
@@ -537,6 +537,24 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
+<<<<<<< HEAD
+=======
+		
+		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" ]
+				}
+			});
+		}
+>>>>>>> branch 'master' of https://sissource.ethz.ch/sispub/openbis
 
 		this.createVocabularies = function(creations) {
 			var thisFacade = this;
@@ -552,7 +570,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-
+		
 		this.createVocabularyTerms = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -775,6 +793,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({
@@ -956,7 +985,26 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
+<<<<<<< HEAD
 
+=======
+		
+		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" ]
+				}
+			});
+		}
+		
+>>>>>>> branch 'master' of https://sissource.ethz.ch/sispub/openbis
 		this.getVocabularies = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java
index 45fc0ff733c529039b33f129baef5a799cad2dd0..211a2290c29b8e48ecb2060aa7142a0a18817b65 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java
@@ -16,17 +16,168 @@ import org.testng.annotations.Test;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.Material;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.fetchoptions.MaterialFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.search.MaterialSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.SampleType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.SortAndPage;
 
 public class SortAndPageTest
 {
+	@Test
+    public void testFetchedFieldsScore_CodeScore()
+    {
+		SampleType sampleTypeA = new SampleType();
+		sampleTypeA.setCode("DUMMY_CODE_A");
+		
+		SampleType sampleTypeB = new SampleType();
+		sampleTypeB.setCode("DUMMY_CODE_B");
+		
+		String propertyCode = "DUMMY_PROPERTY";
+		
+		SampleSearchCriteria c = new SampleSearchCriteria();
+		c.withOrOperator();
+        c.withCode().thatEquals("S2");
+        
+		SampleFetchOptions fo = new SampleFetchOptions();
+		fo.withType();
+		fo.withProperties();
+		fo.sortBy().fetchedFieldsScore();
+		
+        Sample sample1 = new Sample();
+        sample1.setType(sampleTypeA);
+        sample1.setCode("S1");
+        sample1.setProperty(propertyCode, "DUMMY_S1");
+        sample1.setFetchOptions(fo);
+
+        Sample sample2 = new Sample();
+        sample2.setType(sampleTypeB);
+        sample2.setCode("S2");
+        sample2.setProperty(propertyCode, "DUMMY_S2");
+        sample2.setFetchOptions(fo);
+
+        Sample sample3 = new Sample();
+        sample3.setType(sampleTypeA);
+        sample3.setCode("S3");
+        sample3.setProperty(propertyCode, "DUMMY_S3");
+        sample3.setFetchOptions(fo);
+
+        List<Sample> samples = new ArrayList<Sample>();
+        samples.add(sample1);
+        samples.add(sample2);
+        samples.add(sample3);
+
+        Collection<Sample> results = new SortAndPage().sortAndPage(samples, c, fo);
+
+        assertEquals(results, list(sample2, sample1, sample3));
+    }
+	
+	@Test
+    public void testFetchedFieldsScore_PropertyScore()
+    {
+		SampleType sampleTypeA = new SampleType();
+		sampleTypeA.setCode("DUMMY_CODE_A");
+		
+		SampleType sampleTypeB = new SampleType();
+		sampleTypeB.setCode("DUMMY_CODE_B");
+		
+		String propertyCode = "DUMMY_PROPERTY";
+		
+		SampleSearchCriteria c = new SampleSearchCriteria();
+		c.withOrOperator();
+        c.withProperty(propertyCode).thatEquals("DUMMY_S3");
+        
+		SampleFetchOptions fo = new SampleFetchOptions();
+		fo.withType();
+		fo.withProperties();
+		fo.sortBy().fetchedFieldsScore();
+		
+        Sample sample1 = new Sample();
+        sample1.setType(sampleTypeA);
+        sample1.setCode("S1");
+        sample1.setProperty(propertyCode, "DUMMY_S1");
+        sample1.setFetchOptions(fo);
+
+        Sample sample2 = new Sample();
+        sample2.setType(sampleTypeB);
+        sample2.setCode("S2");
+        sample2.setProperty(propertyCode, "DUMMY_S2");
+        sample2.setFetchOptions(fo);
+
+        Sample sample3 = new Sample();
+        sample3.setType(sampleTypeA);
+        sample3.setCode("S3");
+        sample3.setProperty(propertyCode, "DUMMY_S3");
+        sample3.setFetchOptions(fo);
+
+        List<Sample> samples = new ArrayList<Sample>();
+        samples.add(sample1);
+        samples.add(sample2);
+        samples.add(sample3);
+
+        Collection<Sample> results = new SortAndPage().sortAndPage(samples, c, fo);
+
+        assertEquals(results, list(sample3, sample1, sample2));
+    }
+	
+	@Test
+    public void testFetchedFieldsScore_TypeScore()
+    {
+		SampleType sampleTypeA = new SampleType();
+		sampleTypeA.setCode("DUMMY_CODE_A");
+		
+		SampleType sampleTypeB = new SampleType();
+		sampleTypeB.setCode("DUMMY_CODE_B");
+		
+		String propertyCode = "DUMMY_PROPERTY";
+		
+		SampleSearchCriteria c = new SampleSearchCriteria();
+		c.withOrOperator();
+        c.withType().withCode().thatEquals(sampleTypeA.getCode());
+        
+		SampleFetchOptions fo = new SampleFetchOptions();
+		fo.withType();
+		fo.withProperties();
+		fo.sortBy().fetchedFieldsScore();
+		
+        Sample sample1 = new Sample();
+        sample1.setType(sampleTypeA);
+        sample1.setCode("S1");
+        sample1.setProperty(propertyCode, "DUMMY_S1");
+        sample1.setFetchOptions(fo);
+
+        Sample sample2 = new Sample();
+        sample2.setType(sampleTypeB);
+        sample2.setCode("S2");
+        sample2.setProperty(propertyCode, "DUMMY_S2");
+        sample2.setFetchOptions(fo);
+
+        Sample sample3 = new Sample();
+        sample3.setType(sampleTypeA);
+        sample3.setCode("S3");
+        sample3.setProperty(propertyCode, "DUMMY_S3");
+        sample3.setFetchOptions(fo);
+
+        List<Sample> samples = new ArrayList<Sample>();
+        samples.add(sample1);
+        samples.add(sample2);
+        samples.add(sample3);
+
+        Collection<Sample> results = new SortAndPage().sortAndPage(samples, c, fo);
+
+        assertEquals(results, list(sample1, sample3, sample2));
+    }
+	
     @Test
     public void testTopLevel()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    		
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.from(1).count(2);
@@ -48,7 +199,7 @@ public class SortAndPageTest
         materials.add(material2);
         materials.add(material3);
 
-        Collection<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        Collection<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2, material1));
     }
@@ -56,6 +207,8 @@ public class SortAndPageTest
     @Test
     public void testSubLevel()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.withTags().from(1).count(1);
@@ -81,7 +234,7 @@ public class SortAndPageTest
         materials.add(material1);
         materials.add(material2);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2, material1));
         assertEquals(results.get(0).getTags(), set(tag3));
@@ -91,6 +244,8 @@ public class SortAndPageTest
     @Test
     public void testSubLevelThroughSingleRelation()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.withTags().from(1).count(1);
@@ -149,7 +304,7 @@ public class SortAndPageTest
         materials.add(material1);
         materials.add(material2);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2, material1));
 
@@ -168,6 +323,8 @@ public class SortAndPageTest
     @Test
     public void testSortByMultipleFields()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.sortBy().registrationDate().asc();
@@ -192,7 +349,7 @@ public class SortAndPageTest
         materials.add(material2);
         materials.add(material3);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results.get(0), material3);
         assertEquals(results.get(1), material2);
@@ -202,6 +359,8 @@ public class SortAndPageTest
     @Test
     public void testSamePageMultipleTimes()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         Tag tag1 = new Tag();
         tag1.setCode("T1");
         Tag tag2 = new Tag();
@@ -227,7 +386,7 @@ public class SortAndPageTest
         materials.add(material1);
         materials.add(material2);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2));
         assertEquals(results.get(0).getTags(), set(tag3));
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/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/common/fetchoptions/EntityWithPropertiesSortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/EntityWithPropertiesSortOptions.java
index 8084b9e430a493eca9d214b9ea94ca3a269c63b3..2141fbeff3a19db1fcdda79f5d1acce0483cbc6e 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/EntityWithPropertiesSortOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/EntityWithPropertiesSortOptions.java
@@ -16,6 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
@@ -34,13 +37,26 @@ public class EntityWithPropertiesSortOptions<OBJECT extends ICodeHolder & IPermI
 {
 
     private static final long serialVersionUID = 1L;
-
+    
+    @JsonIgnore
+    public static final String FETCHED_FIELDS_SCORE = "FETCHED_FIELDS_SCORE";
+    
     @JsonIgnore
     public static final String TYPE = "TYPE";
 
     @JsonIgnore
     public static final String PROPERTY = "PROPERTY";
-
+    
+    public SortOrder fetchedFieldsScore() {
+    		Map<SortParameter, String> parameters = new HashMap<>();
+    		parameters.put(SortParameter.FULL_CODE_BOOST, 	"1000000");
+    		parameters.put(SortParameter.PARTIAL_CODE_BOOST,  "100000");
+    		parameters.put(SortParameter.FULL_PROPERTY_BOOST,  "10000");
+    		parameters.put(SortParameter.FULL_TYPE_BOOST, 	   "1000");
+    		parameters.put(SortParameter.PARTIAL_PROPERTY_BOOST, "100");
+		return getOrCreateSortingWithParameters(FETCHED_FIELDS_SCORE, parameters);
+    }
+    
     public SortOrder type()
     {
         return getOrCreateSorting(TYPE);
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java
index 9859cfa1d08140fdfeb14d6dcfa3eec6a16571f8..9c24777923d40bc8120a1ef2d767ede57012a776 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java
@@ -20,6 +20,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.lang.StringUtils;
 
@@ -37,12 +38,17 @@ public abstract class SortOptions<OBJECT> implements Serializable
     private List<Sorting> sortings = new LinkedList<>();
 
     protected SortOrder getOrCreateSorting(String field)
+    {
+    		return getOrCreateSortingWithParameters(field, null);
+    }
+    
+    protected SortOrder getOrCreateSortingWithParameters(String field, Map<SortParameter, String> parameters)
     {
         SortOrder order = getSorting(field);
         if (order == null)
         {
             order = new SortOrder();
-            sortings.add(new Sorting(field, order));
+            sortings.add(new Sorting(field, order, parameters));
         }
         return order;
     }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortParameter.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortParameter.java
new file mode 100644
index 0000000000000000000000000000000000000000..232fb2678e665aaa58036d85646fb7a7e2b5ced0
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortParameter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * 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.common.fetchoptions;
+
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author juanf
+ */
+@JsonObject("as.dto.common.fetchoptions.SortParameter")
+public enum SortParameter
+{
+
+    FULL_CODE_BOOST, PARTIAL_CODE_BOOST, FULL_TYPE_BOOST, FULL_PROPERTY_BOOST, PARTIAL_PROPERTY_BOOST
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java
index 4f819b261acb16fe341ddadf27bcf567392149cd..11b10ccff584584854bc8fd5ea82bb4c29281843 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java
@@ -17,6 +17,7 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions;
 
 import java.io.Serializable;
+import java.util.Map;
 
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
@@ -32,16 +33,24 @@ public class Sorting implements Serializable
     private String field;
 
     private SortOrder order;
-
+    
+    private Map<SortParameter, String> parameters;
+    
     @SuppressWarnings("unused")
     private Sorting()
     {
     }
 
     public Sorting(String field, SortOrder order)
+    {
+        this(field, order, null);
+    }
+    
+    public Sorting(String field, SortOrder order, Map<SortParameter, String> parameters)
     {
         this.field = field;
         this.order = order;
+        this.parameters = parameters;
     }
 
     public String getField()
@@ -53,6 +62,10 @@ public class Sorting implements Serializable
     {
         return order;
     }
+    
+    public Map<SortParameter, String> getParameters() {
+    		return parameters;
+    }
 
     @Override
     public String toString()
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 f5140b30005515f774515dae1f3b96f5ff3d304b..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
@@ -1902,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
+