From 23005c7301ef520444b43308bc0d41bba7a35a54 Mon Sep 17 00:00:00 2001
From: izabel <izabel>
Date: Tue, 17 Mar 2009 14:51:32 +0000
Subject: [PATCH] [LMS-780] allow to change project (1)

SVN: 10265
---
 .../web/client/ICommonClientService.java      | 12 +++--
 .../web/client/ICommonClientServiceAsync.java |  9 ++--
 .../model/ModelDataPropertyNames.java         |  2 +
 .../ui/experiment/ExperimentBrowserGrid.java  |  2 +-
 .../ui/experiment/ProjectSelectionWidget.java | 28 ++++++++++
 .../web/server/CommonClientService.java       |  7 ++-
 .../openbis/generic/server/CommonServer.java  |  5 +-
 .../generic/server/CommonServerLogger.java    |  8 +--
 .../server/business/bo/ExperimentBO.java      | 22 +++++++-
 .../server/business/bo/IExperimentBO.java     |  3 +-
 .../openbis/generic/shared/ICommonServer.java | 52 +++++++++----------
 .../shared/basic/dto/EditableExperiment.java  | 11 +++-
 .../generic/shared/dto/ExperimentPE.java      |  2 +-
 .../experiment/GenericExperimentEditForm.java | 32 ++++++++++--
 .../shared/ICommonServer.java.expected        | 52 +++++++++----------
 15 files changed, 166 insertions(+), 81 deletions(-)

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
index 87c73759f25..e8e8fb81878 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
@@ -260,9 +260,9 @@ public interface ICommonClientService extends IClientService
             throws UserFailureException;
 
     /**
-     * Assumes that preparation of the export ({@link #prepareExportSamples(TableExportCriteria)} or
-     * {@link #prepareExportExperiments(TableExportCriteria)} has been invoked before and returned
-     * with an exportDataKey passed here as a parameter.
+     * Assumes that preparation of the export ({@link #prepareExportSamples(TableExportCriteria)}
+     * or {@link #prepareExportExperiments(TableExportCriteria)} has been invoked before and
+     * returned with an exportDataKey passed here as a parameter.
      */
     public String getExportTable(String exportDataKey, String lineSeparator)
             throws UserFailureException;
@@ -349,7 +349,8 @@ public interface ICommonClientService extends IClientService
      * Updates experiment.
      */
     public void updateExperiment(String attachmentSessionKey, final String experimentIdentifier,
-            List<ExperimentProperty> properties) throws UserFailureException;
+            List<ExperimentProperty> properties, String newProjectIdentifierOrNull)
+            throws UserFailureException;
 
     /**
      * Updates material.
@@ -364,6 +365,7 @@ public interface ICommonClientService extends IClientService
             throws UserFailureException;
 
     /** Deletes the specified data sets. */
-    public void deleteDataSets(List<String> dataSetCodes, String reason) throws UserFailureException;
+    public void deleteDataSets(List<String> dataSetCodes, String reason)
+            throws UserFailureException;
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
index 934e8abbe11..8e67325334a 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
@@ -302,11 +302,11 @@ public interface ICommonClientServiceAsync extends IClientServiceAsync
             final AsyncCallback<Void> asyncCallback);
 
     /**
-     * @see ICommonClientService#updateExperiment(String, String, List)
+     * @see ICommonClientService#updateExperiment(String, String, List,String)
      */
     public void updateExperiment(String attachmentSessionKey, final String experimentIdentifier,
-            List<ExperimentProperty> properties, final AsyncCallback<Void> asyncCallback)
-            throws UserFailureException;
+            List<ExperimentProperty> properties, String newProjectIdentifier,
+            final AsyncCallback<Void> asyncCallback) throws UserFailureException;
 
     /**
      * @see ICommonClientService#updateMaterial(String, List)
@@ -321,5 +321,6 @@ public interface ICommonClientServiceAsync extends IClientServiceAsync
             final AsyncCallback<Void> asyncCallback) throws UserFailureException;
 
     /** @see ICommonClientService#deleteDataSets(List, String) */
-    public void deleteDataSets(List<String> dataSetCodes, String reason, AsyncCallback<Void> asyncCallback);
+    public void deleteDataSets(List<String> dataSetCodes, String reason,
+            AsyncCallback<Void> asyncCallback);
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/ModelDataPropertyNames.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/ModelDataPropertyNames.java
index 6a89734fcb0..3f769e1a3c7 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/ModelDataPropertyNames.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/ModelDataPropertyNames.java
@@ -94,6 +94,8 @@ public final class ModelDataPropertyNames
 
     public static final String IS_MANAGED_INTERNALLY = "isManagedInternally";
 
+    public static final String PROJECT_IDENTIFIER = "projectIdentifier";
+
     private ModelDataPropertyNames()
     {
         // Can not be instantiated.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java
index 326a1bf4fcd..6b8d03f0331 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java
@@ -134,7 +134,7 @@ public final class ExperimentBrowserGrid extends AbstractBrowserGrid<Experiment,
     {
         return new EditableExperiment(selectedType.getExperimentTypePropertyTypes(), experiment
                 .getProperties(), selectedType, experiment.getIdentifier(), experiment.getId(),
-                experiment.getModificationDate());
+                experiment.getModificationDate(), experiment.getProject().getIdentifier());
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionWidget.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionWidget.java
index bcb192fd167..b4e684c2bde 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionWidget.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionWidget.java
@@ -29,6 +29,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewConte
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.AppEvents;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.ModelDataPropertyNames;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.sample.DropDownList;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.GWTUtils;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DefaultResultSetConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.Project;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
@@ -55,6 +56,7 @@ public final class ProjectSelectionWidget extends
         public ProjectComboModel(Project project)
         {
             set(DISPLAY_COLUMN_ID, renderProjectWithGroup(project));
+            set(ModelDataPropertyNames.PROJECT_IDENTIFIER, project.getIdentifier());
             set(ModelDataPropertyNames.OBJECT, project);
         }
 
@@ -68,11 +70,20 @@ public final class ProjectSelectionWidget extends
 
     private final IViewContext<?> viewContext;
 
+    private final String initialProjectIdentifier;
+
     public ProjectSelectionWidget(final IViewContext<?> viewContext, final String idSuffix)
+    {
+        this(viewContext, idSuffix, null);
+    }
+
+    public ProjectSelectionWidget(final IViewContext<?> viewContext, final String idSuffix,
+            String initialProjectIdentifier)
     {
         super(viewContext, SUFFIX + idSuffix, Dict.PROJECT, DISPLAY_COLUMN_ID, CHOOSE_SUFFIX,
                 EMPTY_RESULT_SUFFIX);
         this.viewContext = viewContext;
+        this.initialProjectIdentifier = initialProjectIdentifier;
     }
 
     /**
@@ -108,8 +119,19 @@ public final class ProjectSelectionWidget extends
                 setReadOnly(true);
             }
             applyEmptyText();
+            if (initialProjectIdentifier != null)
+            {
+                trySelectByIdentifier(initialProjectIdentifier);
+                updateOriginalValue();
+            }
             fireEvent(AppEvents.CALLBACK_FINISHED);
         }
+
+    }
+
+    public void updateOriginalValue()
+    {
+        setOriginalValue(getValue());
     }
 
     @Override
@@ -129,4 +151,10 @@ public final class ProjectSelectionWidget extends
         DefaultResultSetConfig<String, Project> config = DefaultResultSetConfig.createFetchAll();
         viewContext.getCommonService().listProjects(config, new ListProjectsCallback(viewContext));
     }
+
+    public void trySelectByIdentifier(String projectIdentifier)
+    {
+        GWTUtils
+                .setSelectedItem(this, ModelDataPropertyNames.PROJECT_IDENTIFIER, projectIdentifier);
+    }
 }
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
index d35fa5e82de..55d9da4cddb 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
@@ -974,7 +974,7 @@ public final class CommonClientService extends AbstractClientService implements
     }
 
     public void updateExperiment(String sessionKey, String experimentIdentifier,
-            List<ExperimentProperty> properties)
+            List<ExperimentProperty> properties, String newProjectIdentifier)
             throws ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException
     {
 
@@ -997,7 +997,10 @@ public final class CommonClientService extends AbstractClientService implements
             }
             final ExperimentIdentifier identifier =
                     new ExperimentIdentifierFactory(experimentIdentifier).createIdentifier();
-            commonServer.editExperiment(sessionToken, identifier, properties, attachments);
+            final ProjectIdentifier project =
+                    newProjectIdentifier == null ? null : new ProjectIdentifierFactory(
+                            newProjectIdentifier).createIdentifier();
+            commonServer.editExperiment(sessionToken, identifier, properties, attachments, project);
         } catch (final UserFailureException e)
         {
             throw UserFailureExceptionTranslator.translate(e);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index 6fc15a6082a..520b9e258a2 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -642,11 +642,12 @@ public final class CommonServer extends AbstractServer<ICommonServer> implements
     }
 
     public void editExperiment(String sessionToken, ExperimentIdentifier identifier,
-            List<ExperimentProperty> properties, List<AttachmentPE> attachments)
+            List<ExperimentProperty> properties, List<AttachmentPE> attachments,
+            ProjectIdentifier newProjectIdentifierOrNull)
     {
         final Session session = getSessionManager().getSession(sessionToken);
         final IExperimentBO experimentBO = businessObjectFactory.createExperimentBO(session);
-        experimentBO.edit(identifier, properties, attachments);
+        experimentBO.edit(identifier, properties, attachments, newProjectIdentifierOrNull);
         experimentBO.save();
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
index dc7e1cc8e11..1a5563cbc60 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
@@ -312,10 +312,12 @@ final class CommonServerLogger extends AbstractServerLogger implements ICommonSe
     }
 
     public void editExperiment(String sessionToken, ExperimentIdentifier experimentIdentifier,
-            List<ExperimentProperty> properties, List<AttachmentPE> attachments)
+            List<ExperimentProperty> properties, List<AttachmentPE> attachments,
+            ProjectIdentifier newProjectIdentifierOrNull)
     {
-        logTracking(sessionToken, "edit_experiment", "EXPERIMENT(%s) ATTACHMENTS_ADDED(%s)",
-                experimentIdentifier, attachments.size());
+        logTracking(sessionToken, "edit_experiment",
+                "EXPERIMENT(%s) ATTACHMENTS_ADDED(%s) NEW_PROJECT(%s)", experimentIdentifier,
+                attachments.size(), newProjectIdentifierOrNull);
     }
 
     public void deleteDataSets(String sessionToken, List<String> dataSetCodes, String reason)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
index c611ea76aff..e61ff86b096 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
@@ -39,6 +39,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
 
@@ -291,10 +292,11 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
     }
 
     public void edit(ExperimentIdentifier identifier, List<ExperimentProperty> properties,
-            List<AttachmentPE> newAttachments)
+            List<AttachmentPE> newAttachments, ProjectIdentifier newProjectIdentifierOrNull)
     {
         loadByExperimentIdentifier(identifier);
         updateProperties(properties);
+        updateProject(newProjectIdentifierOrNull);
         for (AttachmentPE a : newAttachments)
         {
             addAttachment(a);
@@ -302,6 +304,24 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
         dataChanged = true;
     }
 
+    private void updateProject(ProjectIdentifier newProjectIdentifierOrNull)
+    {
+        if (newProjectIdentifierOrNull != null)
+        {
+            ProjectPE project =
+                    getProjectDAO().tryFindProject(
+                            newProjectIdentifierOrNull.getDatabaseInstanceCode(),
+                            newProjectIdentifierOrNull.getGroupCode(),
+                            newProjectIdentifierOrNull.getProjectCode());
+            if (project == null)
+            {
+                throw UserFailureException.fromTemplate(ERR_PROJECT_NOT_FOUND,
+                        newProjectIdentifierOrNull);
+            }
+            experiment.setProject(project);
+        }
+    }
+
     private void updateProperties(List<ExperimentProperty> properties)
     {
         final ArrayList<ExperimentPropertyPE> existingProperties =
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExperimentBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExperimentBO.java
index 74b3e7553db..e9154b16db9 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExperimentBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExperimentBO.java
@@ -23,6 +23,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier;
 
 /**
  * A generic experiment <i>Business Object</i>.
@@ -64,5 +65,5 @@ public interface IExperimentBO extends IBusinessObject
      * Changes given experiment. Currently allowed changes: properties.
      */
     public void edit(ExperimentIdentifier identifier, List<ExperimentProperty> properties,
-            List<AttachmentPE> attachments);
+            List<AttachmentPE> attachments, ProjectIdentifier newProjectIdentifierOrNull);
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
index 48f017687e4..f98a3546fee 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
@@ -131,11 +131,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.GROUP_ADMIN)
-    public void registerGroupRole(
-            String sessionToken,
-            RoleCode roleCode,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier identifier,
-            String person);
+    public void registerGroupRole(String sessionToken, RoleCode roleCode,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            GroupIdentifier identifier, String person);
 
     /**
      * Registers a new instance role.
@@ -149,11 +147,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.GROUP_ADMIN)
-    public void deleteGroupRole(
-            String sessionToken,
-            RoleCode roleCode,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier groupIdentifier,
-            String person);
+    public void deleteGroupRole(String sessionToken, RoleCode roleCode,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            GroupIdentifier groupIdentifier, String person);
 
     /**
      * Deletes role described by given role code and user id.
@@ -188,10 +184,10 @@ public interface ICommonServer extends IServer
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.OBSERVER)
-    public List<ExperimentPE> listExperiments(
-            final String sessionToken,
+    public List<ExperimentPE> listExperiments(final String sessionToken,
             ExperimentTypePE experimentType,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier project);
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier project);
 
     /**
      * For given {@link SampleIdentifier} returns the corresponding list of {@link ExternalDataPE}.
@@ -200,9 +196,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.OBSERVER)
-    public List<ExternalDataPE> listExternalData(
-            final String sessionToken,
-            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) final SampleIdentifier identifier);
+    public List<ExternalDataPE> listExternalData(final String sessionToken,
+            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class)
+            final SampleIdentifier identifier);
 
     /**
      * For given {@link ExperimentIdentifier} returns the corresponding list of
@@ -212,9 +208,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.OBSERVER)
-    public List<ExternalDataPE> listExternalData(
-            final String sessionToken,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) final ExperimentIdentifier identifier);
+    public List<ExternalDataPE> listExternalData(final String sessionToken,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            final ExperimentIdentifier identifier);
 
     /**
      * Performs an <i>Hibernate Search</i> based on given parameters.
@@ -290,10 +286,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.GROUP_ADMIN)
-    public void registerProject(
-            String sessionToken,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier projectIdentifier,
-            String description, String leaderId);
+    public void registerProject(String sessionToken,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier projectIdentifier, String description, String leaderId);
 
     /**
      * Performs an <i>Hibernate Search</i> based on given parameters.
@@ -360,7 +355,9 @@ public interface ICommonServer extends IServer
     public void editExperiment(String sessionToken,
             @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
             ExperimentIdentifier experimentIdentifier, List<ExperimentProperty> properties,
-            List<AttachmentPE> attachments);
+            List<AttachmentPE> attachments,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier newProjectIdentifierOrNull);
 
     /**
      * Saves changed material.
@@ -375,10 +372,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.USER)
-    public void editSample(
-            String sessionToken,
-            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) SampleIdentifier identifier,
-            List<SampleProperty> properties);
+    public void editSample(String sessionToken,
+            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class)
+            SampleIdentifier identifier, List<SampleProperty> properties);
 
     /** Lists vocabulary terms of a given vocabulary. Includes terms usage statistics. */
     @Transactional
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EditableExperiment.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EditableExperiment.java
index 92991923c4c..64232ca2861 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EditableExperiment.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EditableExperiment.java
@@ -19,7 +19,6 @@ package ch.systemsx.cisd.openbis.generic.shared.basic.dto;
 import java.util.Date;
 import java.util.List;
 
-
 /**
  * @author Izabela Adamczyk
  */
@@ -27,11 +26,19 @@ public class EditableExperiment extends
         EditableEntity<ExperimentType, ExperimentTypePropertyType, ExperimentProperty>
 {
 
+    private final String project;
+
     public EditableExperiment(List<ExperimentTypePropertyType> etpts,
             List<ExperimentProperty> properties, ExperimentType type, String identifier, Long id,
-            Date modificationDate)
+            Date modificationDate, String project)
     {
         super(EntityKind.EXPERIMENT, etpts, properties, type, identifier, id, modificationDate);
+        this.project = project;
+    }
+
+    public String getProjectIdentifier()
+    {
+        return project;
     }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java
index 7fd37eadc7c..f66bff73372 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java
@@ -202,7 +202,7 @@ public class ExperimentPE implements IEntityPropertiesHolder<ExperimentPropertyP
 
     @ManyToOne(fetch = FetchType.EAGER)
     @NotNull(message = ValidationMessages.PROJECT_NOT_NULL_MESSAGE)
-    @JoinColumn(name = ColumnNames.PROJECT_COLUMN, updatable = false)
+    @JoinColumn(name = ColumnNames.PROJECT_COLUMN, updatable = true)
     @IndexedEmbedded(prefix = SearchFieldConstants.PREFIX_PROJECT)
     public ProjectPE getProject()
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentEditForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentEditForm.java
index 45d09cd9f93..1ac2ea4d5e4 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentEditForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentEditForm.java
@@ -32,6 +32,8 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.ICommonClientServiceAs
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.InfoBoxCallbackListener;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.experiment.ProjectSelectionWidget;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.FieldUtil;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EditableExperiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentProperty;
@@ -63,12 +65,20 @@ public final class GenericExperimentEditForm
 
     private Html attachmentsInfo;
 
+    private ProjectSelectionWidget projectChooser;
+
+    private String originalProjectIdentifier;
+
     public GenericExperimentEditForm(IViewContext<IGenericClientServiceAsync> viewContext,
             EditableExperiment entity, boolean editMode)
     {
         super(viewContext, entity, editMode);
         this.viewContext = viewContext;
         sessionKey = createSimpleId(EntityKind.EXPERIMENT, entity.getId() + "");
+        originalProjectIdentifier = entity.getProjectIdentifier();
+        projectChooser =
+                new ProjectSelectionWidget(viewContext, sessionKey, originalProjectIdentifier);
+        FieldUtil.markAsMandatory(projectChooser);
         attachmentManager =
                 new AttachmentManager(sessionKey, DEFAULT_NUMBER_OF_ATTACHMENTS, "New Attachment");
         addUploadFeatures(formPanel, sessionKey);
@@ -115,8 +125,22 @@ public final class GenericExperimentEditForm
     private void save()
     {
         final List<ExperimentProperty> properties = extractProperties();
-        viewContext.getCommonService().updateExperiment(sessionKey, entity.getIdentifier(),
-                properties, new RegisterExperimentCallback(viewContext));
+        final String newProjectIdentifierOrNull = extractIdentifier();
+        viewContext.getCommonService()
+                .updateExperiment(sessionKey, entity.getIdentifier(), properties,
+                        newProjectIdentifierOrNull, new RegisterExperimentCallback(viewContext));
+    }
+
+    private String extractIdentifier()
+    {
+        final String newIdentifier = projectChooser.tryGetSelectedProject().getIdentifier();
+        if (originalProjectIdentifier.equals(newIdentifier))
+        {
+            return null;
+        } else
+        {
+            return newIdentifier;
+        }
     }
 
     @Override
@@ -149,7 +173,7 @@ public final class GenericExperimentEditForm
     protected PropertiesEditor<ExperimentType, ExperimentTypePropertyType, ExperimentProperty> createPropertiesEditor(
             List<ExperimentTypePropertyType> entityTypesPropertyTypes,
             List<ExperimentProperty> properties, String id,
-            IViewContext<ICommonClientServiceAsync>  context)
+            IViewContext<ICommonClientServiceAsync> context)
     {
         return new ExperimentPropertyEditor(entityTypesPropertyTypes, properties, id, context);
     }
@@ -158,6 +182,7 @@ public final class GenericExperimentEditForm
     protected List<Field<?>> getEntitySpecificFormFields()
     {
         List<Field<?>> fields = new ArrayList<Field<?>>();
+        fields.add(projectChooser);
         for (FileUploadField f : attachmentManager.getFields())
         {
             fields.add(f);
@@ -176,6 +201,7 @@ public final class GenericExperimentEditForm
     @Override
     protected void updateCheckPageWidgets()
     {
+        projectChooser.updateOriginalValue();
         attachmentsInfo.setHtml(getAttachmentInfoText(attachmentManager.attachmentsDefined()));
     }
 
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
index b61efa711ed..674440954b1 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
@@ -131,11 +131,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.GROUP_ADMIN)
-    public void registerGroupRole(
-            String sessionToken,
-            RoleCode roleCode,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier identifier,
-            String person);
+    public void registerGroupRole(String sessionToken, RoleCode roleCode,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            GroupIdentifier identifier, String person);
 
     /**
      * Registers a new instance role.
@@ -149,11 +147,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.GROUP_ADMIN)
-    public void deleteGroupRole(
-            String sessionToken,
-            RoleCode roleCode,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier groupIdentifier,
-            String person);
+    public void deleteGroupRole(String sessionToken, RoleCode roleCode,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            GroupIdentifier groupIdentifier, String person);
 
     /**
      * Deletes role described by given role code and user id.
@@ -188,10 +184,10 @@ public interface ICommonServer extends IServer
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.OBSERVER)
-    public List<ExperimentPE> listExperiments(
-            final String sessionToken,
+    public List<ExperimentPE> listExperiments(final String sessionToken,
             ExperimentTypePE experimentType,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier project);
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier project);
 
     /**
      * For given {@link SampleIdentifier} returns the corresponding list of {@link ExternalDataPE}.
@@ -200,9 +196,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.OBSERVER)
-    public List<ExternalDataPE> listExternalData(
-            final String sessionToken,
-            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) final SampleIdentifier identifier);
+    public List<ExternalDataPE> listExternalData(final String sessionToken,
+            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class)
+            final SampleIdentifier identifier);
 
     /**
      * For given {@link ExperimentIdentifier} returns the corresponding list of
@@ -212,9 +208,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.OBSERVER)
-    public List<ExternalDataPE> listExternalData(
-            final String sessionToken,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) final ExperimentIdentifier identifier);
+    public List<ExternalDataPE> listExternalData(final String sessionToken,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            final ExperimentIdentifier identifier);
 
     /**
      * Performs an <i>Hibernate Search</i> based on given parameters.
@@ -290,10 +286,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.GROUP_ADMIN)
-    public void registerProject(
-            String sessionToken,
-            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier projectIdentifier,
-            String description, String leaderId);
+    public void registerProject(String sessionToken,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier projectIdentifier, String description, String leaderId);
 
     /**
      * Performs an <i>Hibernate Search</i> based on given parameters.
@@ -360,7 +355,9 @@ public interface ICommonServer extends IServer
     public void editExperiment(String sessionToken,
             @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
             ExperimentIdentifier experimentIdentifier, List<ExperimentProperty> properties,
-            List<AttachmentPE> attachments);
+            List<AttachmentPE> attachments,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier newProjectIdentifierOrNull);
 
     /**
      * Saves changed material.
@@ -375,10 +372,9 @@ public interface ICommonServer extends IServer
      */
     @Transactional
     @RolesAllowed(RoleSet.USER)
-    public void editSample(
-            String sessionToken,
-            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) SampleIdentifier identifier,
-            List<SampleProperty> properties);
+    public void editSample(String sessionToken,
+            @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class)
+            SampleIdentifier identifier, List<SampleProperty> properties);
 
     /** Lists vocabulary terms of a given vocabulary. Includes terms usage statistics. */
     @Transactional
-- 
GitLab