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 dec028453e76e0892af7fc5a0dfa91d637c13a44..41af5925927d7392870bfcbd1f11925dc568f212 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
@@ -31,6 +31,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IUpdateResult;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListEntityHistoryCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListExperimentsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMaterialDisplayCriteria;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMetaprojectsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListPersonsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria2;
@@ -88,6 +89,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MatchingEntity;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAuthorizationGroup;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewColumnOrFilter;
@@ -146,6 +148,12 @@ public interface ICommonClientService extends IClientService
     public TypedTableResultSet<Script> listScripts(ListScriptsCriteria criteria)
             throws UserFailureException;
 
+    /**
+     * Returns a list of all metaprojects.
+     */
+    public TypedTableResultSet<Metaproject> listMetaprojects(ListMetaprojectsCriteria criteria)
+            throws UserFailureException;
+
     /**
      * Like {@link #prepareExportSamples(TableExportCriteria)}, but for scripts.
      */
@@ -153,6 +161,13 @@ public interface ICommonClientService extends IClientService
             final TableExportCriteria<TableModelRowWithObject<Script>> criteria)
             throws UserFailureException;
 
+    /**
+     * Like {@link #prepareExportSamples(TableExportCriteria)}, but for metaprojects.
+     */
+    public String prepareExportMetaprojects(
+            final TableExportCriteria<TableModelRowWithObject<Metaproject>> criteria)
+            throws UserFailureException;
+
     /**
      * Like {@link #prepareExportSamples(TableExportCriteria)}, but for spaces.
      */
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 f7038879ee2574894f84f2dc4baad6c065a2562c..1c7562bd0d9fce67eb9c8d0dbf1215aaeb0c8995 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
@@ -33,6 +33,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IUpdateResult;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListEntityHistoryCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListExperimentsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMaterialDisplayCriteria;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMetaprojectsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListPersonsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria2;
@@ -90,6 +91,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MatchingEntity;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAuthorizationGroup;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewColumnOrFilter;
@@ -134,11 +136,20 @@ public interface ICommonClientServiceAsync extends IClientServiceAsync
     public void listScripts(ListScriptsCriteria criteria,
             final AsyncCallback<TypedTableResultSet<Script>> asyncCallback);
 
+    /** @see ICommonClientService#listMetaprojects(ListMetaprojectsCriteria) */
+    public void listMetaprojects(ListMetaprojectsCriteria criteria,
+            final AsyncCallback<TypedTableResultSet<Metaproject>> asyncCallback);
+
     /** @see ICommonClientService#prepareExportScripts(TableExportCriteria) */
     public void prepareExportScripts(
             TableExportCriteria<TableModelRowWithObject<Script>> exportCriteria,
             AsyncCallback<String> callback);
 
+    /** @see ICommonClientService#prepareExportMetaprojects(TableExportCriteria) */
+    public void prepareExportMetaprojects(
+            TableExportCriteria<TableModelRowWithObject<Metaproject>> exportCriteria,
+            AsyncCallback<String> callback);
+
     /** @see ICommonClientService#prepareExportSpaces(TableExportCriteria) */
     public void prepareExportSpaces(
             TableExportCriteria<TableModelRowWithObject<Space>> exportCriteria,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
index 0e14ddb9a88573b1ebf21553d03c47a93598f882..412f656059d4b395402054f03aaecc7f4dab6cfd 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
@@ -80,6 +80,14 @@ public abstract class Dict
 
     public static final String BREADCRUMBS_SEPARATOR = "breadcrumbs_separator";
 
+    public static final String METAPROJECTS = "metaprojects";
+
+    public static final String METAPROJECTS_HINT = "metaprojects_hint";
+
+    public static final String ADD_METAPROJECT = "add_metaproject";
+
+    public static final String CHOOSE_METAPROJECT = "choose_metaproject";
+
     //
     // Form
     //
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/DisplayTypeIDGenerator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/DisplayTypeIDGenerator.java
index e3b480d074414611624bace46fdd41cd4a18b26c..5568763d63ec878f3c6b561a0cc48df4324c1728 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/DisplayTypeIDGenerator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/DisplayTypeIDGenerator.java
@@ -80,6 +80,8 @@ public enum DisplayTypeIDGenerator implements IDisplayTypeIDGenerator
 
     SAMPLE_TYPE_BROWSER("sample-type-browser"),
 
+    METAPROJECT_CHOOSER_GRID("metaproject-chooser-grid"),
+
     // -------------- Sections
 
     ATTACHMENT_SECTION("attachment-section"),
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/field/MetaprojectArea.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/field/MetaprojectArea.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b8ba83927cafec557b7b96a67e416ec5deebb51
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/field/MetaprojectArea.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2009 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.systemsx.cisd.openbis.generic.client.web.client.application.ui.field;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IMessageProvider;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
+
+/**
+ * {@link MultilineItemsField} extension to specify metaprojects.
+ * 
+ * @author pkupczyk
+ */
+public class MetaprojectArea extends MultilineItemsField
+{
+
+    public static final String ID_SUFFIX = "_metaprojects";
+
+    public MetaprojectArea(IMessageProvider messageProvider, String idPrefix)
+    {
+        super("", false);
+        setFieldLabel(messageProvider.getMessage(Dict.METAPROJECTS));
+        setEmptyText(messageProvider.getMessage(Dict.METAPROJECTS_HINT));
+        setId(idPrefix + ID_SUFFIX);
+    }
+
+    public void setMetaprojects(Collection<Metaproject> metaprojects)
+    {
+        List<String> names = new ArrayList<String>();
+
+        if (metaprojects != null)
+        {
+            for (Metaproject metaproject : metaprojects)
+            {
+                names.add(metaproject.getName());
+            }
+        }
+
+        setMetaprojects(names);
+    }
+
+    public final void setMetaprojects(List<String> namesOrIdentifiers)
+    {
+        setItems(namesOrIdentifiers);
+    }
+
+    public final String[] tryGetMetaprojects()
+    {
+        return tryGetModifiedItemList();
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/field/MetaprojectChooserButton.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/field/MetaprojectChooserButton.java
new file mode 100644
index 0000000000000000000000000000000000000000..438126063acdd1d30095220dd74145f394bcf826
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/field/MetaprojectChooserButton.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2008 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.systemsx.cisd.openbis.generic.client.web.client.application.ui.field;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.extjs.gxt.ui.client.event.ButtonEvent;
+import com.extjs.gxt.ui.client.event.SelectionListener;
+import com.extjs.gxt.ui.client.widget.button.Button;
+import com.extjs.gxt.ui.client.widget.form.AdapterField;
+import com.extjs.gxt.ui.client.widget.form.Field;
+
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.DisposableEntityChooser;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.metaproject.MetaprojectGrid;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
+
+/**
+ * ` A button for selecting a metaproject from a list.
+ * 
+ * @author pkupczyk
+ */
+public class MetaprojectChooserButton extends Button implements
+        IChosenEntitiesSetter<TableModelRowWithObject<Metaproject>>
+{
+
+    public static final String ID_SUFFIX = "_metaproject_chooser_button";
+
+    private Field<?> field;
+
+    private final List<IChosenEntitiesListener<TableModelRowWithObject<Metaproject>>> listeners =
+            new ArrayList<IChosenEntitiesListener<TableModelRowWithObject<Metaproject>>>();
+
+    public MetaprojectChooserButton(final IViewContext<?> viewContext, final String idPrefix)
+    {
+        super(viewContext.getMessage(Dict.ADD_METAPROJECT));
+
+        setId(idPrefix + ID_SUFFIX);
+        addSelectionListener(new SelectionListener<ButtonEvent>()
+            {
+
+                @Override
+                public void componentSelected(ButtonEvent ce)
+                {
+                    DisposableEntityChooser<TableModelRowWithObject<Metaproject>> chooserGrid =
+                            MetaprojectGrid.createChooser(viewContext);
+
+                    new EntityChooserDialog<TableModelRowWithObject<Metaproject>>(chooserGrid,
+                            MetaprojectChooserButton.this,
+                            viewContext.getMessage(Dict.CHOOSE_METAPROJECT), viewContext).show();
+                }
+
+            });
+    }
+
+    @Override
+    public void setChosenEntities(List<TableModelRowWithObject<Metaproject>> entities)
+    {
+        if (entities == null)
+        {
+            return;
+        }
+        for (IChosenEntitiesListener<TableModelRowWithObject<Metaproject>> listener : listeners)
+        {
+            listener.entitiesChosen(entities);
+        }
+    }
+
+    public void addChosenEntityListener(
+            IChosenEntitiesListener<TableModelRowWithObject<Metaproject>> listener)
+    {
+        listeners.add(listener);
+    }
+
+    public Field<?> getField()
+    {
+        if (field == null)
+        {
+            field = new AdapterField(this);
+            field.setLabelSeparator("");
+        }
+        return field;
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/metaproject/MetaprojectGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/metaproject/MetaprojectGrid.java
new file mode 100644
index 0000000000000000000000000000000000000000..faf258708a273590c72542351f88ca7be83230a2
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/metaproject/MetaprojectGrid.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2012 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.systemsx.cisd.openbis.generic.client.web.client.application.ui.metaproject;
+
+import java.util.Arrays;
+import java.util.List;
+
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.DisplayTypeIDGenerator;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.TypedTableGrid;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.ColumnDefsAndConfigs;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.DisposableEntityChooser;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DefaultResultSetConfig;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMetaprojectsCriteria;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.MetaprojectGridColumnIDs;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TypedTableResultSet;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
+
+/**
+ * Grid displaying metaprojects.
+ * 
+ * @author pkupczyk
+ */
+public class MetaprojectGrid extends TypedTableGrid<Metaproject>
+{
+
+    public static final String METAPROJECT_CHOOSER_GRID_ID = GenericConstants.ID_PREFIX
+            + "metaproject-chooser" + TypedTableGrid.GRID_POSTFIX;
+
+    public static DisposableEntityChooser<TableModelRowWithObject<Metaproject>> createChooser(
+            final IViewContext<?> viewContext)
+    {
+        final MetaprojectGrid grid =
+                new MetaprojectGrid(viewContext, METAPROJECT_CHOOSER_GRID_ID,
+                        DisplayTypeIDGenerator.METAPROJECT_CHOOSER_GRID);
+        grid.allowMultipleSelection();
+        return grid.asDisposableWithoutToolbar();
+    }
+
+    private MetaprojectGrid(IViewContext<?> viewContext, String gridId,
+            DisplayTypeIDGenerator gridDisplayTypeId)
+    {
+        super(viewContext.getCommonViewContext(), gridId, true, gridDisplayTypeId);
+    }
+
+    @Override
+    protected String translateColumnIdToDictionaryKey(String columnID)
+    {
+        return columnID.toLowerCase();
+    }
+
+    @Override
+    protected ColumnDefsAndConfigs<TableModelRowWithObject<Metaproject>> createColumnsDefinition()
+    {
+        ColumnDefsAndConfigs<TableModelRowWithObject<Metaproject>> schema =
+                super.createColumnsDefinition();
+        schema.setGridCellRendererFor(MetaprojectGridColumnIDs.DESCRIPTION,
+                createMultilineStringCellRenderer());
+        return schema;
+    }
+
+    @Override
+    protected void listTableRows(
+            DefaultResultSetConfig<String, TableModelRowWithObject<Metaproject>> resultSetConfig,
+            AbstractAsyncCallback<TypedTableResultSet<Metaproject>> callback)
+    {
+        ListMetaprojectsCriteria criteria = new ListMetaprojectsCriteria();
+        viewContext.getService().listMetaprojects(criteria, callback);
+    }
+
+    @Override
+    protected void prepareExportEntities(
+            TableExportCriteria<TableModelRowWithObject<Metaproject>> exportCriteria,
+            AbstractAsyncCallback<String> callback)
+    {
+        viewContext.getService().prepareExportMetaprojects(exportCriteria, callback);
+    }
+
+    @Override
+    protected List<String> getColumnIdsOfFilters()
+    {
+        return Arrays.asList(MetaprojectGridColumnIDs.NAME);
+    }
+
+    @Override
+    public DatabaseModificationKind[] getRelevantModifications()
+    {
+        return new DatabaseModificationKind[] {};
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ListMetaprojectsCriteria.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ListMetaprojectsCriteria.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9c426bad6b4e07efe6b13aa78f46f6c42fa2de0
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ListMetaprojectsCriteria.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012 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.systemsx.cisd.openbis.generic.client.web.client.dto;
+
+import com.google.gwt.user.client.rpc.IsSerializable;
+
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
+
+/**
+ * @author pkupczyk
+ */
+public class ListMetaprojectsCriteria extends
+        DefaultResultSetConfig<String, TableModelRowWithObject<Metaproject>> implements
+        IsSerializable
+{
+
+    public ListMetaprojectsCriteria()
+    {
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/MetaprojectGridColumnIDs.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/MetaprojectGridColumnIDs.java
new file mode 100644
index 0000000000000000000000000000000000000000..b41f08c7a5d6bb29a285e444135cb939fcab40ba
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/MetaprojectGridColumnIDs.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012 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.systemsx.cisd.openbis.generic.client.web.client.dto;
+
+/**
+ * @author pkupczyk
+ */
+public class MetaprojectGridColumnIDs
+{
+
+    public static final String NAME = "NAME";
+
+    public static final String DESCRIPTION = "DESCRIPTION";
+
+    public static final String CREATION_DATE = "CREATION_DATE";
+
+}
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 7297eedad01a9a63c404cca6d94c949074821492..a13d5709b09d8762ac45fdc9afe71ae6c5ef7eeb 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
@@ -59,6 +59,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListEntityHistoryCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListExperimentsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMaterialDisplayCriteria;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMetaprojectsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListPersonsCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria2;
@@ -90,6 +91,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.GridCustomFi
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IOriginalDataProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IResultSet;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.MatchingEntitiesProvider;
+import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.MetaprojectProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.PersonsProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.ProjectsProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.PropertyTypeProvider;
@@ -158,6 +160,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MatchingEntity;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAuthorizationGroup;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewColumnOrFilter;
@@ -520,6 +523,13 @@ public final class CommonClientService extends AbstractClientService implements
         return prepareExportEntities(criteria);
     }
 
+    @Override
+    public String prepareExportMetaprojects(
+            TableExportCriteria<TableModelRowWithObject<Metaproject>> criteria)
+    {
+        return prepareExportEntities(criteria);
+    }
+
     @Override
     public String prepareExportSpaces(TableExportCriteria<TableModelRowWithObject<Space>> criteria)
     {
@@ -672,6 +682,15 @@ public final class CommonClientService extends AbstractClientService implements
         return listEntities(scriptProvider, criteria);
     }
 
+    @Override
+    public TypedTableResultSet<Metaproject> listMetaprojects(ListMetaprojectsCriteria criteria)
+            throws UserFailureException
+    {
+        MetaprojectProvider metaprojectProvider =
+                new MetaprojectProvider(commonServer, getSessionToken());
+        return listEntities(metaprojectProvider, criteria);
+    }
+
     @Override
     public List<AuthorizationGroup> listAuthorizationGroups()
             throws ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/MetaprojectProvider.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/MetaprojectProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..13dc090607767e44b82e26b9c674a85bf75ea746
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/MetaprojectProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 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.systemsx.cisd.openbis.generic.client.web.server.resultset;
+
+import static ch.systemsx.cisd.openbis.generic.client.web.client.dto.MetaprojectGridColumnIDs.CREATION_DATE;
+import static ch.systemsx.cisd.openbis.generic.client.web.client.dto.MetaprojectGridColumnIDs.DESCRIPTION;
+import static ch.systemsx.cisd.openbis.generic.client.web.client.dto.MetaprojectGridColumnIDs.NAME;
+
+import java.util.List;
+
+import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TypedTableModel;
+import ch.systemsx.cisd.openbis.generic.shared.util.TypedTableModelBuilder;
+
+/**
+ * @author pkupczyk
+ */
+public class MetaprojectProvider extends AbstractCommonTableModelProvider<Metaproject>
+{
+
+    public MetaprojectProvider(ICommonServer commonServer, String sessionToken)
+    {
+        super(commonServer, sessionToken);
+    }
+
+    @Override
+    protected TypedTableModel<Metaproject> createTableModel()
+    {
+        List<Metaproject> metaprojects = commonServer.listMetaprojects(sessionToken);
+        TypedTableModelBuilder<Metaproject> builder = new TypedTableModelBuilder<Metaproject>();
+        builder.addColumn(NAME);
+        builder.addColumn(DESCRIPTION);
+        builder.addColumn(CREATION_DATE).withDefaultWidth(300);
+        for (Metaproject metaproject : metaprojects)
+        {
+            builder.addRow(metaproject);
+            builder.column(NAME).addString(metaproject.getName());
+            builder.column(DESCRIPTION).addString(metaproject.getDescription());
+            builder.column(CREATION_DATE).addDate(metaproject.getCreationDate());
+        }
+        return builder.getModel();
+    }
+
+}
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 2293d12d2629a0f905baef1f113c588fda214b8c..1866d80d0072f4944b1eeb5246db1cd9a960d5b5 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
@@ -2174,11 +2174,13 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
     @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN)
     @Capability("WRITE_MATERIAL")
     public Date updateMaterial(String sessionToken, TechId materialId,
-            List<IEntityProperty> properties, Date version)
+            List<IEntityProperty> properties, String[] metaprojects, Date version)
     {
         final Session session = getSession(sessionToken);
         final IMaterialBO materialBO = businessObjectFactory.createMaterialBO(session);
-        materialBO.update(new MaterialUpdateDTO(materialId, properties, version));
+        MaterialUpdateDTO updates = new MaterialUpdateDTO(materialId, properties, version);
+        updates.setMetaprojectsOrNull(metaprojects);
+        materialBO.update(updates);
         materialBO.save();
         return materialBO.getMaterial().getModificationDate();
     }
@@ -3328,7 +3330,7 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
                 getDAOFactory().getMaterialDAO().tryGetByTechId(entityId).getModificationDate();
         Map<String, String> properties = createPropertiesMap(modifiedProperties);
         updateMaterial(sessionToken, entityId,
-                EntityHelper.translatePropertiesMapToList(properties), modificationDate);
+                EntityHelper.translatePropertiesMapToList(properties), null, modificationDate);
     }
 
     private Map<String, String> createPropertiesMap(List<PropertyUpdates> updates)
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 6d6116d90c22837db87c7d9ca38891b833eda7cb..38303d6adb560b60ea6ecbe8d7ab6f31ac9dd8b1 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
@@ -984,7 +984,7 @@ final class CommonServerLogger extends AbstractServerLogger implements ICommonSe
 
     @Override
     public Date updateMaterial(String sessionToken, TechId materialId,
-            List<IEntityProperty> properties, Date version)
+            List<IEntityProperty> properties, String[] metaprojects, Date version)
     {
         logTracking(sessionToken, "edit_material", "MATERIAL(%s)", materialId);
         return null;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
index 28342fe3d615aaceaa0fe96577c6dab94162d38f..cc0446ca78aeeccea82607c16fb6c3e8bddbfb85 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
@@ -72,6 +72,8 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Identifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.IHasMetaprojectsPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SpacePE;
@@ -198,6 +200,61 @@ abstract class AbstractBusinessObject implements IDAOFactory
                 registrator, propertiesToUpdate);
     }
 
+    protected void setMetaprojects(IHasMetaprojectsPE entity, String[] metaprojectsOrNull)
+    {
+        if (entity == null)
+        {
+            throw new IllegalArgumentException("Entity cannot be null");
+        }
+        if (metaprojectsOrNull == null)
+        {
+            return;
+        }
+
+        Set<MetaprojectPE> currentMetaprojects = entity.getMetaprojects();
+        Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+
+        for (String metaprojectsOrNullItem : metaprojectsOrNull)
+        {
+            MetaprojectPE metaproject =
+                    getMetaprojectDAO().tryFindByOwnerAndName(session.getUserName(),
+                            metaprojectsOrNullItem);
+            if (metaproject == null)
+            {
+                throw new UserFailureException("Metaproject '" + metaprojectsOrNullItem
+                        + "' couldn't be found.");
+            }
+            metaprojects.add(metaproject);
+        }
+
+        Set<MetaprojectPE> metaprojectsToAdd = new HashSet<MetaprojectPE>();
+        Set<MetaprojectPE> metaprojectsToRemove = new HashSet<MetaprojectPE>();
+
+        for (MetaprojectPE metaproject : metaprojects)
+        {
+            if (currentMetaprojects.contains(metaproject) == false)
+            {
+                metaprojectsToAdd.add(metaproject);
+            }
+        }
+        for (MetaprojectPE currentMetaproject : currentMetaprojects)
+        {
+            if (metaprojects.contains(currentMetaproject) == false)
+            {
+                metaprojectsToRemove.add(currentMetaproject);
+            }
+        }
+
+        for (MetaprojectPE metaprojectToAdd : metaprojectsToAdd)
+        {
+            entity.addMetaproject(metaprojectToAdd);
+        }
+        for (MetaprojectPE metaprojectToRemove : metaprojectsToRemove)
+        {
+            entity.removeMetaproject(metaprojectToRemove);
+        }
+    }
+
     //
     // IDAOFactory
     //
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java
index 4dac8985356e41c1f9cc4af184eff93b2db87691..c8ea4f2a6b741dfe68c13ed4cac78fdcdbe082ad 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java
@@ -158,6 +158,7 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
             setParents(samplePE, parents, newSample.getDefaultSpaceIdentifier());
         }
         samplePE.setPermId(getOrCreatePermID(newSample));
+        setMetaprojects(samplePE, newSample.getMetaprojectsOrNull());
         return samplePE;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java
index bf5e6d91ae6b3a06f292dac5ce02c90eeebce6b4..c8733a2b59125b8fcd96fd44ae0f4e3d553a0fc2 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java
@@ -597,6 +597,7 @@ public class DataBO extends AbstractDataSetBusinessObject implements IDataBO
         }
 
         setParents(data, asListOrNull(updates.getModifiedParentDatasetCodesOrNull()));
+        setMetaprojects(data, updates.getMetaprojectsOrNull());
         updateContainer(updates.getModifiedContainerDatasetCodeOrNull());
         updateComponents(updates.getModifiedContainedDatasetCodesOrNull());
         updateFileFormatType(data, updates.getFileFormatTypeCode());
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 b824b7fede2e1a2ba37a634324f74bd359f82367..9ceda6cdc94ceea536b38da6c61983b3332c965d 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
@@ -341,6 +341,7 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
                 newExperiment.getProperties(), registrator);
         defineExperimentProject(newExperiment, experimentIdentifier);
         experiment.setPermId(getOrCreatePermID(newExperiment));
+        setMetaprojects(experiment, newExperiment.getMetaprojectsOrNull());
         dataChanged = true;
     }
 
@@ -473,6 +474,8 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
         }
         updateSamples(updates);
 
+        setMetaprojects(experiment, updates.getMetaprojectsOrNull());
+
         dataChanged = true;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java
index fcb96e4fd22344a91426bbdefe450ac85ea27b9d..7adcbbe2053a9a3546fcad556154e1a59a408835 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java
@@ -140,6 +140,7 @@ public final class MaterialBO extends AbstractMaterialBusinessObject implements
         {
             throwModifiedEntityException("Material");
         }
+        setMetaprojects(material, materialUpdate.getMetaprojectsOrNull());
         updateProperties(materialUpdate.getProperties());
         dataChanged = true;
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MetaprojectBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MetaprojectBO.java
index c0aaea09eebaf309f00d8f24fcd6fee27bc95c9e..bfec70986677927eb0351289887fbe7b8bfac2a2 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MetaprojectBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MetaprojectBO.java
@@ -44,7 +44,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.EventPE.EntityType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EventType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectAssignmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -241,9 +240,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
                 throw new IllegalArgumentException("Experiment for id: " + experimentId
                         + " doesn't exist.");
             }
-            MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-            metaprojectAssignmentPE.setExperiment(experimentPE);
-            metaproject.addAssignment(metaprojectAssignmentPE);
+            experimentPE.addMetaproject(metaproject);
             addToAddedEntities(ExperimentPE.class, experimentPE.getId());
         }
 
@@ -260,9 +257,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
             {
                 throw new IllegalArgumentException("Sample for id: " + sampleId + " doesn't exist.");
             }
-            MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-            metaprojectAssignmentPE.setSample(samplePE);
-            metaproject.addAssignment(metaprojectAssignmentPE);
+            samplePE.addMetaproject(metaproject);
             addToAddedEntities(SamplePE.class, samplePE.getId());
         }
 
@@ -280,9 +275,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
                 throw new IllegalArgumentException("Data set for id: " + dataSetId
                         + " doesn't exist.");
             }
-            MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-            metaprojectAssignmentPE.setDataSet(dataPE);
-            metaproject.addAssignment(metaprojectAssignmentPE);
+            dataPE.addMetaproject(metaproject);
             addToAddedEntities(DataPE.class, dataPE.getId());
         }
 
@@ -300,9 +293,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
                 throw new IllegalArgumentException("Material for id: " + materialId
                         + " doesn't exist.");
             }
-            MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-            metaprojectAssignmentPE.setMaterial(materialPE);
-            metaproject.addAssignment(metaprojectAssignmentPE);
+            materialPE.addMetaproject(metaproject);
             addToAddedEntities(MaterialPE.class, materialPE.getId());
         }
 
@@ -317,9 +308,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
             ExperimentPE experimentPE = experimentBO.tryFindByExperimentId(experimentId);
             if (experimentPE != null)
             {
-                MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-                metaprojectAssignmentPE.setExperiment(experimentPE);
-                metaproject.removeAssignment(metaprojectAssignmentPE);
+                experimentPE.removeMetaproject(metaproject);
                 addToRemovedEntities(ExperimentPE.class, experimentPE.getId());
             }
         }
@@ -335,9 +324,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
             SamplePE samplePE = sampleBO.tryFindBySampleId(sampleId);
             if (samplePE != null)
             {
-                MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-                metaprojectAssignmentPE.setSample(samplePE);
-                metaproject.removeAssignment(metaprojectAssignmentPE);
+                samplePE.removeMetaproject(metaproject);
                 addToRemovedEntities(SamplePE.class, samplePE.getId());
             }
         }
@@ -353,9 +340,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
             DataPE dataPE = dataBO.tryFindByDataSetId(dataSetId);
             if (dataPE != null)
             {
-                MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-                metaprojectAssignmentPE.setDataSet(dataPE);
-                metaproject.removeAssignment(metaprojectAssignmentPE);
+                dataPE.removeMetaproject(metaproject);
                 addToRemovedEntities(DataPE.class, dataPE.getId());
             }
         }
@@ -371,9 +356,7 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
             MaterialPE materialPE = materialBO.tryFindByMaterialId(materialId);
             if (materialPE != null)
             {
-                MetaprojectAssignmentPE metaprojectAssignmentPE = createAssignement();
-                metaprojectAssignmentPE.setMaterial(materialPE);
-                metaproject.removeAssignment(metaprojectAssignmentPE);
+                materialPE.removeMetaproject(metaproject);
                 addToRemovedEntities(MaterialPE.class, materialPE.getId());
             }
         }
@@ -381,13 +364,6 @@ public class MetaprojectBO extends AbstractBusinessObject implements IMetaprojec
         dataChanged = true;
     }
 
-    private MetaprojectAssignmentPE createAssignement()
-    {
-        MetaprojectAssignmentPE metaprojectAssignmentPE = new MetaprojectAssignmentPE();
-        metaprojectAssignmentPE.setMetaproject(metaproject);
-        return metaprojectAssignmentPE;
-    }
-
     private void initEntitiesMaps()
     {
         addedEntitiesIds = new HashMap<Class<?>, List<Long>>();
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
index 7ad0dfc3909bd365f8f5cef927fbec3123ec4275..cee8ba7a1782ca2077ade23593c933feeb80b4ba 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
@@ -329,6 +329,8 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
             addAttachment(AttachmentTranslator.translate(attachment));
         }
         updateParents(updates);
+        setMetaprojects(sample, updates.getMetaprojectsOrNull());
+
         dataChanged = true;
     }
 
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 7f4178ca878723e369139f7b8d9c9135cc7af0a7..a421077214c2ba3f10be6419b243827cbe2b51fb 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
@@ -977,7 +977,7 @@ public interface ICommonServer extends IServer
     @Transactional
     @DatabaseUpdateModification(value = ObjectKind.MATERIAL)
     public Date updateMaterial(String sessionToken, TechId materialId,
-            List<IEntityProperty> properties, Date version);
+            List<IEntityProperty> properties, String[] metaprojects, Date version);
 
     /**
      * Returns file template available during batch operation of entity of given type.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java
index 44ddf04634952fa09ab646351b6cb4af6ffcda63..8292dd783323f84f5c8e75c1f79d28836d0e08b6 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java
@@ -40,6 +40,8 @@ public class BasicDataSetUpdates implements Serializable
 
     private String[] modifiedParentDatasetCodesOrNull;
 
+    private String[] metaprojectsOrNull;
+
     // Optional:
 
     // 1. external data (non-virtual)
@@ -156,4 +158,14 @@ public class BasicDataSetUpdates implements Serializable
         this.externalCode = externalCode;
     }
 
+    public String[] getMetaprojectsOrNull()
+    {
+        return metaprojectsOrNull;
+    }
+
+    public void setMetaprojectsOrNull(String[] metaprojectsOrNull)
+    {
+        this.metaprojectsOrNull = metaprojectsOrNull;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicExperimentUpdates.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicExperimentUpdates.java
index 8513df57d74a548a41b2def39833518d07bf3090..610ee016645851360617ef152f007314c12013c5 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicExperimentUpdates.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicExperimentUpdates.java
@@ -49,6 +49,8 @@ public class BasicExperimentUpdates implements Serializable
 
     private List<NewSamplesWithTypes> newSamples;
 
+    private String[] metaprojectsOrNull;
+
     public int getVersion()
     {
         return version;
@@ -99,4 +101,15 @@ public class BasicExperimentUpdates implements Serializable
     {
         return registerSamples;
     }
+
+    public String[] getMetaprojectsOrNull()
+    {
+        return metaprojectsOrNull;
+    }
+
+    public void setMetaprojectsOrNull(String[] metaprojectsOrNull)
+    {
+        this.metaprojectsOrNull = metaprojectsOrNull;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicSampleUpdates.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicSampleUpdates.java
index 853d3a48eedd4d4aa0872d3864eb442e4ec67a70..77494f457377ffc9665a0310d2964a7ddf24c945 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicSampleUpdates.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicSampleUpdates.java
@@ -46,6 +46,8 @@ public class BasicSampleUpdates implements Serializable
     // If some previously assigned parent sample is missing on this list, it will be unassigned.
     private String[] modifiedParentCodesOrNull;
 
+    private String[] metaprojectsOrNull;
+
     public String getParentIdentifierOrNull()
     {
         if (modifiedParentCodesOrNull == null || modifiedParentCodesOrNull.length == 0)
@@ -122,4 +124,14 @@ public class BasicSampleUpdates implements Serializable
         this.modifiedParentCodesOrNull = modifiedParentCodesOrNull;
     }
 
+    public String[] getMetaprojectsOrNull()
+    {
+        return metaprojectsOrNull;
+    }
+
+    public void setMetaprojectsOrNull(String[] metaprojectsOrNull)
+    {
+        this.metaprojectsOrNull = metaprojectsOrNull;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewExperiment.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewExperiment.java
index ac4db541a9bde2bd749994e352dd38dc9e5dcd75..6c3cd90d5cdd5eb2bf52af90b47122809ae98560 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewExperiment.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewExperiment.java
@@ -43,6 +43,8 @@ public final class NewExperiment extends Identifier<NewExperiment> implements IP
 
     private List<NewAttachment> attachments;
 
+    private String[] metaprojectsOrNull;
+
     public NewExperiment()
     {
     }
@@ -141,4 +143,14 @@ public final class NewExperiment extends Identifier<NewExperiment> implements IP
         this.attachments = attachments;
     }
 
+    public String[] getMetaprojectsOrNull()
+    {
+        return metaprojectsOrNull;
+    }
+
+    public void setMetaprojectsOrNull(String[] metaprojectsOrNull)
+    {
+        this.metaprojectsOrNull = metaprojectsOrNull;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java
index d436113e9cd0a16e01c8491347750e635f3bd934..679fd72d6a8c32f584f011376e4916764b9487d7 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java
@@ -89,6 +89,8 @@ public class NewSample extends Identifier<NewSample> implements Comparable<NewSa
 
     private List<NewAttachment> attachments;
 
+    private String[] metaprojectsOrNull;
+
     public NewSample()
     {
     }
@@ -265,6 +267,16 @@ public class NewSample extends Identifier<NewSample> implements Comparable<NewSa
         this.properties = properties;
     }
 
+    public String[] getMetaprojectsOrNull()
+    {
+        return metaprojectsOrNull;
+    }
+
+    public void setMetaprojectsOrNull(String[] metaprojectsOrNull)
+    {
+        this.metaprojectsOrNull = metaprojectsOrNull;
+    }
+
     //
     // Object
     //
@@ -313,4 +325,5 @@ public class NewSample extends Identifier<NewSample> implements Comparable<NewSa
                         + that.getContainerIdentifier() + that.getCurrentContainerIdentifier();
         return thisCombinedIdentifier.equals(thatCombinedIdentifier);
     }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
index 98bce2d45b4cf161431d4d090dfe74835366ece0..8a8d6cb9d7f18f4858935c5247eb48bbf5e8de1c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
@@ -32,8 +32,6 @@ import javax.persistence.Id;
 import javax.persistence.Inheritance;
 import javax.persistence.InheritanceType;
 import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.SequenceGenerator;
@@ -82,7 +80,8 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
 @Inheritance(strategy = InheritanceType.JOINED)
 @Indexed(index = "DataPE")
 public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
-        IEntityInformationWithPropertiesHolder, IMatchingEntity, IIdentifierHolder, IDeletablePE
+        IEntityInformationWithPropertiesHolder, IMatchingEntity, IIdentifierHolder, IDeletablePE,
+        IHasMetaprojectsPE
 {
     private static final long serialVersionUID = IServer.VERSION;
 
@@ -119,7 +118,8 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
 
     private List<DataPE> containedDataSets = new ArrayList<DataPE>();
 
-    private Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+    private Set<MetaprojectAssignmentPE> metaprojectAssignments =
+            new HashSet<MetaprojectAssignmentPE>();
 
     /**
      * Deletion information.
@@ -815,23 +815,61 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
         this.deletion = deletion;
     }
 
-    // used only by Hibernate Search
-    @SuppressWarnings("unused")
-    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
-    @Fetch(FetchMode.SUBSELECT)
-    @JoinTable(name = TableNames.METAPROJECT_ASSIGNMENTS_VIEW, joinColumns =
-        { @JoinColumn(name = ColumnNames.DATA_ID_COLUMN) }, inverseJoinColumns =
-        { @JoinColumn(name = ColumnNames.METAPROJECT_ID_COLUMN) })
+    @Override
+    public void addMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setDataSet(this);
+
+        getMetaprojectAssignmentsInternal().add(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().add(assignmentPE);
+    }
+
+    @Override
+    public void removeMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setDataSet(this);
+
+        getMetaprojectAssignmentsInternal().remove(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().remove(assignmentPE);
+    }
+
+    @Override
+    @Transient
     @IndexedEmbedded(prefix = SearchFieldConstants.PREFIX_METAPROJECT)
-    private Set<MetaprojectPE> getMetaprojects()
+    public Set<MetaprojectPE> getMetaprojects()
+    {
+        Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+        for (MetaprojectAssignmentPE assignment : getMetaprojectAssignmentsInternal())
+        {
+            metaprojects.add(assignment.getMetaproject());
+        }
+        return new UnmodifiableSetDecorator<MetaprojectPE>(metaprojects);
+    }
+
+    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "dataSet")
+    @Fetch(FetchMode.SUBSELECT)
+    private Set<MetaprojectAssignmentPE> getMetaprojectAssignmentsInternal()
     {
-        return this.metaprojects;
+        return this.metaprojectAssignments;
     }
 
     @SuppressWarnings("unused")
-    private void setMetaprojects(Set<MetaprojectPE> metaprojects)
+    private void setMetaprojectAssignmentsInternal(
+            Set<MetaprojectAssignmentPE> metaprojectAssignments)
     {
-        this.metaprojects = metaprojects;
+        this.metaprojectAssignments = metaprojectAssignments;
     }
 
 }
\ No newline at end of file
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 3f274055b220a9010e5d645f84f1dfaa76603ed9..f1893e85aab3daf21f62839c7dae8cfb333fa4a7 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
@@ -31,8 +31,6 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.SequenceGenerator;
@@ -91,7 +89,7 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
     { AttachmentPE.class, ProjectPE.class })
 public class ExperimentPE extends AttachmentHolderPE implements
         IEntityInformationWithPropertiesHolder, IIdAndCodeHolder, Comparable<ExperimentPE>,
-        IModifierAndModificationDateBean, IMatchingEntity, IDeletablePE, Serializable
+        IModifierAndModificationDateBean, IMatchingEntity, IDeletablePE, IHasMetaprojectsPE, Serializable
 {
     private static final long serialVersionUID = IServer.VERSION;
 
@@ -117,7 +115,8 @@ public class ExperimentPE extends AttachmentHolderPE implements
 
     private ExperimentIdentifier experimentIdentifier;
 
-    private Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+    private Set<MetaprojectAssignmentPE> metaprojectAssignments =
+            new HashSet<MetaprojectAssignmentPE>();
 
     /**
      * Person who registered this entity.
@@ -576,23 +575,61 @@ public class ExperimentPE extends AttachmentHolderPE implements
         this.permId = permId;
     }
 
-    // used only by Hibernate Search
-    @SuppressWarnings("unused")
-    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
-    @Fetch(FetchMode.SUBSELECT)
-    @JoinTable(name = TableNames.METAPROJECT_ASSIGNMENTS_VIEW, joinColumns =
-        { @JoinColumn(name = ColumnNames.EXPERIMENT_COLUMN) }, inverseJoinColumns =
-        { @JoinColumn(name = ColumnNames.METAPROJECT_ID_COLUMN) })
+    @Override
+    public void addMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setExperiment(this);
+
+        getMetaprojectAssignmentsInternal().add(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().add(assignmentPE);
+    }
+
+    @Override
+    public void removeMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setExperiment(this);
+
+        getMetaprojectAssignmentsInternal().remove(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().remove(assignmentPE);
+    }
+
+    @Override
+    @Transient
     @IndexedEmbedded(prefix = SearchFieldConstants.PREFIX_METAPROJECT)
-    private Set<MetaprojectPE> getMetaprojects()
+    public Set<MetaprojectPE> getMetaprojects()
+    {
+        Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+        for (MetaprojectAssignmentPE assignment : getMetaprojectAssignmentsInternal())
+        {
+            metaprojects.add(assignment.getMetaproject());
+        }
+        return new UnmodifiableSetDecorator<MetaprojectPE>(metaprojects);
+    }
+
+    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "experiment")
+    @Fetch(FetchMode.SUBSELECT)
+    private Set<MetaprojectAssignmentPE> getMetaprojectAssignmentsInternal()
     {
-        return this.metaprojects;
+        return this.metaprojectAssignments;
     }
 
     @SuppressWarnings("unused")
-    private void setMetaprojects(Set<MetaprojectPE> metaprojects)
+    private void setMetaprojectAssignmentsInternal(
+            Set<MetaprojectAssignmentPE> metaprojectAssignments)
     {
-        this.metaprojects = metaprojects;
+        this.metaprojectAssignments = metaprojectAssignments;
     }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/IHasMetaprojectsPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/IHasMetaprojectsPE.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7c16776d8abf9aab5e3fd813736d345e34dc6e6
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/IHasMetaprojectsPE.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 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.systemsx.cisd.openbis.generic.shared.dto;
+
+import java.util.Set;
+
+/**
+ * @author pkupczyk
+ */
+public interface IHasMetaprojectsPE
+{
+
+    public void addMetaproject(MetaprojectPE metaprojectPE);
+
+    public void removeMetaproject(MetaprojectPE metaprojectPE);
+
+    public Set<MetaprojectPE> getMetaprojects();
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialPE.java
index 540a4b8e2dd748584b320aacf7a14d7b9dbc6683..6a71899b205a90f7e31315b1157d4b5863f25ef0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialPE.java
@@ -29,8 +29,6 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.SequenceGenerator;
@@ -78,7 +76,7 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
             ColumnNames.DATABASE_INSTANCE_COLUMN }))
 @Indexed(index = "MaterialPE")
 public class MaterialPE implements IIdAndCodeHolder, Comparable<MaterialPE>,
-        IEntityInformationWithPropertiesHolder, Serializable, IMatchingEntity
+        IEntityInformationWithPropertiesHolder, Serializable, IMatchingEntity, IHasMetaprojectsPE
 {
     private static final long serialVersionUID = IServer.VERSION;
 
@@ -94,7 +92,8 @@ public class MaterialPE implements IIdAndCodeHolder, Comparable<MaterialPE>,
 
     private Set<MaterialPropertyPE> properties = new HashSet<MaterialPropertyPE>();
 
-    private Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+    private Set<MetaprojectAssignmentPE> metaprojectAssignments =
+            new HashSet<MetaprojectAssignmentPE>();
 
     /**
      * NOTE: Materials do not have permanent ids stored in the database.
@@ -382,23 +381,61 @@ public class MaterialPE implements IIdAndCodeHolder, Comparable<MaterialPE>,
         return createPermId(code, materialType.getCode());
     }
 
-    // used only by Hibernate Search
-    @SuppressWarnings("unused")
-    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
-    @Fetch(FetchMode.SUBSELECT)
-    @JoinTable(name = TableNames.METAPROJECT_ASSIGNMENTS_VIEW, joinColumns =
-        { @JoinColumn(name = ColumnNames.MATERIAL_COLUMN) }, inverseJoinColumns =
-        { @JoinColumn(name = ColumnNames.METAPROJECT_ID_COLUMN) })
+    @Override
+    public void addMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setMaterial(this);
+
+        getMetaprojectAssignmentsInternal().add(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().add(assignmentPE);
+    }
+
+    @Override
+    public void removeMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setMaterial(this);
+
+        getMetaprojectAssignmentsInternal().remove(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().remove(assignmentPE);
+    }
+
+    @Override
+    @Transient
     @IndexedEmbedded(prefix = SearchFieldConstants.PREFIX_METAPROJECT)
-    private Set<MetaprojectPE> getMetaprojects()
+    public Set<MetaprojectPE> getMetaprojects()
+    {
+        Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+        for (MetaprojectAssignmentPE assignment : getMetaprojectAssignmentsInternal())
+        {
+            metaprojects.add(assignment.getMetaproject());
+        }
+        return new UnmodifiableSetDecorator<MetaprojectPE>(metaprojects);
+    }
+
+    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "material")
+    @Fetch(FetchMode.SUBSELECT)
+    private Set<MetaprojectAssignmentPE> getMetaprojectAssignmentsInternal()
     {
-        return this.metaprojects;
+        return this.metaprojectAssignments;
     }
 
     @SuppressWarnings("unused")
-    private void setMetaprojects(Set<MetaprojectPE> metaprojects)
+    private void setMetaprojectAssignmentsInternal(
+            Set<MetaprojectAssignmentPE> metaprojectAssignments)
     {
-        this.metaprojects = metaprojects;
+        this.metaprojectAssignments = metaprojectAssignments;
     }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialUpdateDTO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialUpdateDTO.java
index 2240d9b0669735438d2c7c25a3aff468faa37a7c..130fa31193885b43b28506f22c8a9915c7e7ac14 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialUpdateDTO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MaterialUpdateDTO.java
@@ -22,6 +22,8 @@ public class MaterialUpdateDTO extends AbstractHashable implements Serializable
 
     private final List<IEntityProperty> properties;
 
+    private String[] metaprojectsOrNull;
+
     private final Date version;
 
     public MaterialUpdateDTO(TechId materialId, List<IEntityProperty> properties, Date version)
@@ -41,6 +43,16 @@ public class MaterialUpdateDTO extends AbstractHashable implements Serializable
         return properties;
     }
 
+    public String[] getMetaprojectsOrNull()
+    {
+        return metaprojectsOrNull;
+    }
+
+    public void setMetaprojectsOrNull(String[] metaprojectsOrNull)
+    {
+        this.metaprojectsOrNull = metaprojectsOrNull;
+    }
+
     public Date getVersion()
     {
         return version;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java
index f26c2d78a5a0ae209b0afa41f5c89c2a2977becd..eed7d587365352162ada49afa7d6fc13858d9204 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java
@@ -166,7 +166,7 @@ public class MetaprojectPE implements Serializable, IIdHolder
 
     @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "metaproject", orphanRemoval = true)
     @Fetch(FetchMode.SUBSELECT)
-    private Set<MetaprojectAssignmentPE> getAssignmentsInternal()
+    Set<MetaprojectAssignmentPE> getAssignmentsInternal()
     {
         return assignments;
     }
@@ -183,18 +183,6 @@ public class MetaprojectPE implements Serializable, IIdHolder
         return Collections.unmodifiableSet(getAssignmentsInternal());
     }
 
-    public void addAssignment(MetaprojectAssignmentPE metaprojectAssignment)
-    {
-        metaprojectAssignment.setMetaproject(this);
-        getAssignmentsInternal().add(metaprojectAssignment);
-    }
-
-    public void removeAssignment(MetaprojectAssignmentPE metaprojectAssignment)
-    {
-        getAssignmentsInternal().remove(metaprojectAssignment);
-        metaprojectAssignment.setMetaproject(null);
-    }
-
     @Override
     public final boolean equals(final Object obj)
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
index 0e3e80c6c9d8f354ad3cca27284b0d4f81b0257c..4d59efc5e90e8f95d9219deb1e434f1fc1f735c4 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
@@ -34,8 +34,6 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.SequenceGenerator;
@@ -89,7 +87,8 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
         + " IS NULL AND " + ColumnNames.SPACE_COLUMN + " IS NOT NULL)")
 @Indexed(index = "SamplePE")
 public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Comparable<SamplePE>,
-        IEntityInformationWithPropertiesHolder, IMatchingEntity, IDeletablePE, Serializable
+        IEntityInformationWithPropertiesHolder, IMatchingEntity, IDeletablePE, IHasMetaprojectsPE,
+        Serializable
 {
     private static final long serialVersionUID = IServer.VERSION;
 
@@ -119,7 +118,8 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co
 
     private Set<SampleRelationshipPE> childRelationships = new HashSet<SampleRelationshipPE>();
 
-    private Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+    private Set<MetaprojectAssignmentPE> metaprojectAssignments =
+            new HashSet<MetaprojectAssignmentPE>();
 
     @OneToMany(fetch = FetchType.LAZY, mappedBy = "parentSample")
     @Fetch(FetchMode.SUBSELECT)
@@ -812,23 +812,61 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co
         return map;
     }
 
-    // used only by Hibernate Search
-    @SuppressWarnings("unused")
-    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
-    @Fetch(FetchMode.SUBSELECT)
-    @JoinTable(name = TableNames.METAPROJECT_ASSIGNMENTS_VIEW, joinColumns =
-        { @JoinColumn(name = ColumnNames.SAMPLE_COLUMN) }, inverseJoinColumns =
-        { @JoinColumn(name = ColumnNames.METAPROJECT_ID_COLUMN) })
+    @Override
+    public void addMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setSample(this);
+
+        getMetaprojectAssignmentsInternal().add(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().add(assignmentPE);
+    }
+
+    @Override
+    public void removeMetaproject(MetaprojectPE metaprojectPE)
+    {
+        if (metaprojectPE == null)
+        {
+            throw new IllegalArgumentException("Metaproject cannot be null");
+        }
+        MetaprojectAssignmentPE assignmentPE = new MetaprojectAssignmentPE();
+        assignmentPE.setMetaproject(metaprojectPE);
+        assignmentPE.setSample(this);
+
+        getMetaprojectAssignmentsInternal().remove(assignmentPE);
+        metaprojectPE.getAssignmentsInternal().remove(assignmentPE);
+    }
+
+    @Override
+    @Transient
     @IndexedEmbedded(prefix = SearchFieldConstants.PREFIX_METAPROJECT)
-    private Set<MetaprojectPE> getMetaprojects()
+    public Set<MetaprojectPE> getMetaprojects()
+    {
+        Set<MetaprojectPE> metaprojects = new HashSet<MetaprojectPE>();
+        for (MetaprojectAssignmentPE assignment : getMetaprojectAssignmentsInternal())
+        {
+            metaprojects.add(assignment.getMetaproject());
+        }
+        return new UnmodifiableSetDecorator<MetaprojectPE>(metaprojects);
+    }
+
+    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "sample")
+    @Fetch(FetchMode.SUBSELECT)
+    private Set<MetaprojectAssignmentPE> getMetaprojectAssignmentsInternal()
     {
-        return this.metaprojects;
+        return this.metaprojectAssignments;
     }
 
     @SuppressWarnings("unused")
-    private void setMetaprojects(Set<MetaprojectPE> metaprojects)
+    private void setMetaprojectAssignmentsInternal(
+            Set<MetaprojectAssignmentPE> metaprojectAssignments)
     {
-        this.metaprojects = metaprojects;
+        this.metaprojectAssignments = metaprojectAssignments;
     }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java
index 87b8453a58fdcf8907d478dae71b8ef4f939eca4..3c5bf2774d983b3306684412102301a73a329a97 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java
@@ -201,13 +201,19 @@ public final class SampleTranslator
     {
         final SampleParentWithDerived sampleGeneration = new SampleParentWithDerived();
 
+        Collection<Metaproject> parentMetaprojects =
+                MetaprojectTranslator.translate(sampleGenerationDTO.getParent().getMetaprojects());
+
         sampleGeneration.setParent(SampleTranslator.translate(sampleGenerationDTO.getParent(),
-                baseIndexURL, null));
+                baseIndexURL, parentMetaprojects));
 
         final List<Sample> generated = new ArrayList<Sample>();
         for (SamplePE samplePE : sampleGenerationDTO.getDerived())
         {
-            generated.add(SampleTranslator.translate(samplePE, baseIndexURL, false, false, null));
+            Collection<Metaproject> derivedMetaprojects =
+                    MetaprojectTranslator.translate(samplePE.getMetaprojects());
+            generated.add(SampleTranslator.translate(samplePE, baseIndexURL, false, false,
+                    derivedMetaprojects));
         }
         sampleGeneration.setDerived(generated.toArray(new Sample[generated.size()]));
         return sampleGeneration;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientService.java
index 1ebf3c689b8848ab83a6dc0845d3ef18c2555996..a1d1d97c09de85f74659d129fa022499b44a4bd3 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientService.java
@@ -153,7 +153,7 @@ public interface IGenericClientService extends IClientService
      * Updates material.
      */
     public Date updateMaterial(final TechId materialId, List<IEntityProperty> properties,
-            Date version) throws UserFailureException;
+            String[] metaprojects, Date version) throws UserFailureException;
 
     /**
      * Updates sample.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java
index de524f2dc8f71bcdcc254496a33a2233fcc7d0c3..96795032980a65b161bfbe6a1d5e4a1151557a55 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java
@@ -121,10 +121,11 @@ public interface IGenericClientServiceAsync extends IClientServiceAsync
             final AsyncCallback<ExperimentUpdateResult> asyncCallback) throws UserFailureException;
 
     /**
-     * @see IGenericClientService#updateMaterial(TechId, List, Date)
+     * @see IGenericClientService#updateMaterial(TechId, List, String[], Date)
      */
     public void updateMaterial(final TechId materialId, List<IEntityProperty> properties,
-            Date version, final AsyncCallback<Date> asyncCallback) throws UserFailureException;
+            String[] metaprojects, Date version, final AsyncCallback<Date> asyncCallback)
+            throws UserFailureException;
 
     /**
      * @see IGenericClientService#updateSample(SampleUpdates)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/AbstractGenericEntityRegistrationForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/AbstractGenericEntityRegistrationForm.java
index 4f9fea55b834df20ba093b8f5fbe30108775538d..8b2cc4de8eb9d317d16d3530185405a6c91e33c3 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/AbstractGenericEntityRegistrationForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/AbstractGenericEntityRegistrationForm.java
@@ -35,6 +35,9 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.IDatabaseModificationObserver;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.AbstractRegistrationForm;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.CodeFieldWithGenerator;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.IChosenEntitiesListener;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.MetaprojectArea;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.MetaprojectChooserButton;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdAndCodeHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
@@ -42,6 +45,8 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityTypePropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.api.IManagedInputWidgetDescription;
 import ch.systemsx.cisd.openbis.plugin.generic.client.web.client.IGenericClientServiceAsync;
 import ch.systemsx.cisd.openbis.plugin.generic.client.web.client.application.experiment.PropertiesEditor;
@@ -71,6 +76,10 @@ public abstract class AbstractGenericEntityRegistrationForm<T extends EntityType
 
     protected PropertiesEditor<T, S> propertiesEditor;
 
+    protected MetaprojectArea metaprojectArea;
+
+    protected MetaprojectChooserButton metaprojectChooserButton;
+
     private final Map<String, List<IManagedInputWidgetDescription>> inputWidgetDescriptions;
 
     // ---------------------------------------------------------------------------------------------
@@ -208,6 +217,22 @@ public abstract class AbstractGenericEntityRegistrationForm<T extends EntityType
         {
             formPanel.addDirtyCheckIgnoredField(codeField);
         }
+
+        metaprojectArea = new MetaprojectArea(viewContext, getId());
+        metaprojectChooserButton = new MetaprojectChooserButton(viewContext, getId());
+        metaprojectChooserButton
+                .addChosenEntityListener(new IChosenEntitiesListener<TableModelRowWithObject<Metaproject>>()
+                    {
+                        @Override
+                        public void entitiesChosen(
+                                List<TableModelRowWithObject<Metaproject>> entities)
+                        {
+                            for (TableModelRowWithObject<Metaproject> entity : entities)
+                            {
+                                metaprojectArea.appendItem(entity.getObjectOrNull().getName());
+                            }
+                        }
+                    });
     }
 
     /**
@@ -251,6 +276,8 @@ public abstract class AbstractGenericEntityRegistrationForm<T extends EntityType
         {
             fields.add(specificField);
         }
+        fields.add(DatabaseModificationAwareField.wrapUnaware(metaprojectArea));
+        fields.add(DatabaseModificationAwareField.wrapUnaware(metaprojectChooserButton.getField()));
         return fields;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java
index 058ca8d1387e28f6e9563d085780ca6837a6bdc1..7cf9d8dfeba01676dfbd1de01308eccbec7b4b9a 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java
@@ -141,6 +141,7 @@ public final class GenericDataSetEditForm extends
             result.setExperimentIdentifierOrNull(extractExperimentIdentifier());
         }
         result.setModifiedParentDatasetCodesOrNull(extractParentDatasetCodes());
+        result.setMetaprojectsOrNull(metaprojectArea.tryGetMetaprojects());
         builder.fillUpdates(result);
         return result;
     }
@@ -199,6 +200,7 @@ public final class GenericDataSetEditForm extends
         connectedWithSampleCheckbox.updateOriginalValue(connectedWithSampleCheckbox.getValue());
         sampleChooser.updateOriginalValue();
         experimentChooser.updateOriginalValue();
+        updateFieldOriginalValue(metaprojectArea);
         builder.updateOriginalValues(result);
     }
 
@@ -297,6 +299,7 @@ public final class GenericDataSetEditForm extends
         propertiesEditor.initWithProperties(originalDataSet.getDataSetType()
                 .getAssignedPropertyTypes(), originalDataSet.getProperties());
         codeField.setValue(originalDataSet.getCode());
+        metaprojectArea.setMetaprojects(originalDataSet.getMetaprojects());
         // data set fields are initialized when they are created
         parentsArea.setValue(viewContext.getMessage(Dict.LOAD_IN_PROGRESS));
         builder.initializeFormFields();
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 9ae79c0dc70f681d6442410772001e579b464ab9..ef8e78bf509175b3cdb32c45b188aa5afc3023cd 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
@@ -101,6 +101,7 @@ public final class GenericExperimentEditForm extends AbstractGenericExperimentRe
         updates.setGenerateCodes(autoGenerateCodes.getValue().booleanValue());
         updates.setRegisterSamples(existingSamplesRadio.getValue() == false);
         updates.setSamplesSessionKey(samplesSessionKey);
+        updates.setMetaprojectsOrNull(metaprojectArea.tryGetMetaprojects());
         viewContext.getService().updateExperiment(updates,
                 new UpdateExperimentCallback(viewContext));
     }
@@ -135,6 +136,7 @@ public final class GenericExperimentEditForm extends AbstractGenericExperimentRe
         updatePropertyFieldsOriginalValues();
         updateFieldOriginalValue(projectChooser);
         samplesArea.setSampleCodes(samples);
+        updateFieldOriginalValue(metaprojectArea);
     }
 
     private void setOriginalExperiment(Experiment experiment)
@@ -154,6 +156,7 @@ public final class GenericExperimentEditForm extends AbstractGenericExperimentRe
         codeField.setValue(originalExperiment.getCode());
         projectChooser.selectProjectAndUpdateOriginal(originalExperiment.getProject()
                 .getIdentifier());
+        metaprojectArea.setMetaprojects(originalExperiment.getMetaprojects());
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentRegistrationForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentRegistrationForm.java
index dce9786ba709254c44e45ced78c42fdb02d2c3ec..052f7b35ec4cd0b310eb86486255a78e4645ffaa 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentRegistrationForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/GenericExperimentRegistrationForm.java
@@ -104,6 +104,7 @@ public final class GenericExperimentRegistrationForm extends
         newExp.setGenerateCodes(autoGenerateCodes.getValue().booleanValue());
         newExp.setRegisterSamples(existingSamplesRadio.getValue() == false);
         newExp.setAttachments(attachmentsManager.extractAttachments());
+        newExp.setMetaprojectsOrNull(metaprojectArea.tryGetMetaprojects());
         viewContext.getService().registerExperiment(attachmentsSessionKey, samplesSessionKey,
                 newExp, new RegisterExperimentCallback(viewContext));
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/material/GenericMaterialEditForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/material/GenericMaterialEditForm.java
index 5f08018d6fc73eac86a2b01a262faa000e963aed..b6a6fccf2f591b4295bbd190f0e5068f2fd2a56c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/material/GenericMaterialEditForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/material/GenericMaterialEditForm.java
@@ -67,7 +67,8 @@ public final class GenericMaterialEditForm extends
     public final void submitValidForm()
     {
         viewContext.getService().updateMaterial(techIdOrNull, extractProperties(),
-                originalMaterial.getModificationDate(), new UpdateMaterialCallback(viewContext));
+                metaprojectArea.tryGetMetaprojects(), originalMaterial.getModificationDate(),
+                new UpdateMaterialCallback(viewContext));
     }
 
     private final class UpdateMaterialCallback extends
@@ -98,6 +99,7 @@ public final class GenericMaterialEditForm extends
     public void updateOriginalValues()
     {
         updatePropertyFieldsOriginalValues();
+        updateFieldOriginalValue(metaprojectArea);
     }
 
     @Override
@@ -127,6 +129,7 @@ public final class GenericMaterialEditForm extends
         propertiesEditor.initWithProperties(originalMaterial.getMaterialType()
                 .getAssignedPropertyTypes(), originalMaterial.getProperties());
         codeField.setValue(originalMaterial.getCode());
+        metaprojectArea.setMetaprojects(originalMaterial.getMetaprojects());
     }
 
     private void setOriginalMaterial(Material material)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/metaproject/.gitignore b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/metaproject/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleEditForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleEditForm.java
index e5a8f3b18227a5599440788cac84a8f9de1c4d8d..db871fa537617af8dfe648886bc9440b9a8ef31c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleEditForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleEditForm.java
@@ -70,10 +70,14 @@ public final class GenericSampleEditForm extends AbstractGenericSampleRegisterEd
                 experimentField != null ? experimentField.tryToGetValue() : null;
         final String containerOrNull = StringUtils.trimToNull(container.getValue());
         final String[] parents = getParents();
-        viewContext.getService().updateSample(
+
+        SampleUpdates sampleUpdates =
                 new SampleUpdates(attachmentsSessionKey, techIdOrNull, properties, attachments,
                         experimentIdent, originalSample.getModificationDate(),
-                        createSampleIdentifier(), containerOrNull, parents),
+                        createSampleIdentifier(), containerOrNull, parents);
+        sampleUpdates.setMetaprojectsOrNull(metaprojectArea.tryGetMetaprojects());
+
+        viewContext.getService().updateSample(sampleUpdates,
                 enrichWithPostRegistration(new UpdateSampleCallback(viewContext)));
     }
 
@@ -108,6 +112,7 @@ public final class GenericSampleEditForm extends AbstractGenericSampleRegisterEd
         updateFieldOriginalValue(groupSelectionWidget);
         container.updateOriginalValue();
         parentsArea.setSamples(parents);
+        updateFieldOriginalValue(metaprojectArea);
     }
 
     private void setOriginalSample(Sample sample)
@@ -126,6 +131,7 @@ public final class GenericSampleEditForm extends AbstractGenericSampleRegisterEd
         ExperimentIdentifier originalExperiment =
                 experiment == null ? null : ExperimentIdentifier.createIdentifier(experiment);
         experimentField.updateValue(originalExperiment);
+        metaprojectArea.setMetaprojects(originalSample.getMetaprojects());
         initializeGroup();
         initializeContainedInParent();
         initializeParents();
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java
index 9f8fc217c4f2b34d9be6ce659f7121440a17d46d..b8084927924af200ed4899224e72f72d1a6f3226 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java
@@ -110,6 +110,8 @@ public final class GenericSampleRegistrationForm extends AbstractGenericSampleRe
         newSample.setProperties(properties.toArray(IEntityProperty.EMPTY_ARRAY));
         newSample.setAttachments(attachmentsManager.extractAttachments());
         newSample.setExperimentIdentifier(experimentIdentifier);
+        newSample.setMetaprojectsOrNull(metaprojectArea.tryGetMetaprojects());
+
         viewContext.getService().registerSample(attachmentsSessionKey, newSample,
                 enrichWithPostRegistration(new RegisterSampleCallback(viewContext)));
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java
index f223a651ea9617deeb02623859af8462e791f310..926400048d557b55fb00af5010287258387bf10e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java
@@ -528,14 +528,18 @@ public class GenericClientService extends AbstractClientService implements IGene
                                 new SampleIdentifierFactory(updates.getSampleIdentifier())
                                         .createIdentifier();
                     }
+
+                    SampleUpdatesDTO updatesDTO =
+                            new SampleUpdatesDTO(updates.getSampleIdOrNull(),
+                                    updates.getProperties(), convExperimentIdentifierOrNull,
+                                    attachments, updates.getVersion(), sampleOwner,
+                                    updates.getContainerIdentifierOrNull(),
+                                    updates.getModifiedParentCodesOrNull());
+                    updatesDTO.setMetaprojectsOrNull(updates.getMetaprojectsOrNull());
+
                     SampleUpdateResult updateResult =
-                            genericServer.updateSample(
-                                    sessionToken,
-                                    new SampleUpdatesDTO(updates.getSampleIdOrNull(), updates
-                                            .getProperties(), convExperimentIdentifierOrNull,
-                                            attachments, updates.getVersion(), sampleOwner, updates
-                                                    .getContainerIdentifierOrNull(), updates
-                                                    .getModifiedParentCodesOrNull()));
+                            genericServer.updateSample(sessionToken, updatesDTO);
+
                     result.copyFrom(updateResult);
                 }
             }.process(updates.getSessionKey(), getHttpSession(), updates.getAttachments());
@@ -543,12 +547,14 @@ public class GenericClientService extends AbstractClientService implements IGene
     }
 
     @Override
-    public Date updateMaterial(TechId materialId, List<IEntityProperty> properties, Date version)
+    public Date updateMaterial(TechId materialId, List<IEntityProperty> properties,
+            String[] metaprojects, Date version)
     {
         try
         {
             final String sessionToken = getSessionToken();
-            return genericServer.updateMaterial(sessionToken, materialId, properties, version);
+            return genericServer.updateMaterial(sessionToken, materialId, properties, metaprojects,
+                    version);
         } catch (final ch.systemsx.cisd.common.exception.UserFailureException e)
         {
             throw UserFailureExceptionTranslator.translate(e);
@@ -606,6 +612,7 @@ public class GenericClientService extends AbstractClientService implements IGene
         updatesDTO.setRegisterSamples(updates.isRegisterSamples());
         updatesDTO.setNewSamples(updates.getNewSamples());
         updatesDTO.setSampleType(updates.getSampleType());
+        updatesDTO.setMetaprojectsOrNull(updates.getMetaprojectsOrNull());
         return updatesDTO;
     }
 
@@ -637,6 +644,7 @@ public class GenericClientService extends AbstractClientService implements IGene
         updatesDTO.setExternalCode(updates.getExternalCode());
         updatesDTO.setExternalDataManagementSystemCode(updates
                 .getExternalDataManagementSystemCode());
+        updatesDTO.setMetaprojectsOrNull(updates.getMetaprojectsOrNull());
         return updatesDTO;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
index 92dc138c878ef8e1a90a39e769ee7ff3d35470a0..4e10887991ec2107353eaebfcf75bbd0b4c8c71f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
@@ -738,9 +738,10 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen
     @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN)
     @Capability("WRITE_MATERIAL")
     public Date updateMaterial(String sessionToken, TechId materialId,
-            List<IEntityProperty> properties, Date version)
+            List<IEntityProperty> properties, String[] metaprojects, Date version)
     {
-        return commonServer.updateMaterial(sessionToken, materialId, properties, version);
+        return commonServer.updateMaterial(sessionToken, materialId, properties, metaprojects,
+                version);
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerLogger.java
index 500667b5dfc5a6fd6f91768fbb61452a4191298c..5c6e64c5023ee7448b78a8632fd1faba2014a11b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerLogger.java
@@ -173,7 +173,7 @@ final class GenericServerLogger extends AbstractServerLogger implements IGeneric
 
     @Override
     public Date updateMaterial(String sessionToken, TechId materialId,
-            List<IEntityProperty> properties, Date version)
+            List<IEntityProperty> properties, String[] metaprojects, Date version)
     {
         logTracking(sessionToken, "edit_material", "MATERIAL(%s)", materialId);
         return null;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/shared/IGenericServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/shared/IGenericServer.java
index 9598b753dcfe8702ba68c255776cf4375302d280..778fccf7e685224074a0172cc01aafdb6623d33c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/shared/IGenericServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/shared/IGenericServer.java
@@ -228,7 +228,7 @@ public interface IGenericServer extends IServer
     @Transactional
     @DatabaseUpdateModification(value = ObjectKind.MATERIAL)
     public Date updateMaterial(String sessionToken, TechId materialId,
-            List<IEntityProperty> properties, Date version);
+            List<IEntityProperty> properties, String[] metaprojects, Date version);
 
     /**
      * Saves changed sample.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
index dd5c05b7689473c5aa160aec5a1b1f42c3650823..991432d36d275a8123db7ada4d4f446467604b4d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
@@ -21,6 +21,7 @@ var common = {
   modifier: "Modifier",
   registration_date: "Registration Date",
   modification_date: "Modification Date",
+  creation_date: "Creation Date",
   filter: "Filter",
   filters: "Filters",
   not_implemented: "Sorry, feature has not been implemented yet!",
@@ -59,7 +60,11 @@ var common = {
   add_unofficial_vocabulary_term_dialog_title: "Add Ad Hoc Term",
   breadcrumbs_separator: "»",
   storage_confirmation: "Storage Confirmed",
-
+  metaprojects: "Metaprojects",
+  metaprojects_hint: "List of metaprojects names separated by commas (\",\") or one metaproject name per line.",
+  add_metaproject: "Add Metaproject...",
+  choose_metaproject: "Choose a Metaproject",
+  
   //
   // Form
   // 
diff --git a/openbis/source/sql/postgresql/124/function-124.sql b/openbis/source/sql/postgresql/124/function-124.sql
index 92b16e89c4a474f3a5c29e2c4749c5569d4c2303..5dc867cfcc788a0a8d948ab705e035f3461b8759 100644
--- a/openbis/source/sql/postgresql/124/function-124.sql
+++ b/openbis/source/sql/postgresql/124/function-124.sql
@@ -1026,6 +1026,7 @@ CREATE OR REPLACE RULE METAPROJECT_ASSIGNMENTS_INSERT AS
 			   SAMP_ID,
 			   DATA_ID,
 			   MATE_ID,
+			   DEL_ID,
 			   CREATION_DATE
        ) VALUES (
          NEW.ID, 
@@ -1034,6 +1035,7 @@ CREATE OR REPLACE RULE METAPROJECT_ASSIGNMENTS_INSERT AS
 			   NEW.SAMP_ID,
 			   NEW.DATA_ID,
 			   NEW.MATE_ID,
+			   NEW.DEL_ID,
 			   NEW.CREATION_DATE
        );
 
@@ -1047,6 +1049,7 @@ CREATE OR REPLACE RULE METAPROJECT_ASSIGNMENTS_UPDATE AS
 			   		SAMP_ID = NEW.SAMP_ID,
 			   		DATA_ID = NEW.DATA_ID,
 			   		MATE_ID = NEW.MATE_ID,
+			   		DEL_ID = NEW.DEL_ID,
 			   		CREATION_DATE = NEW.CREATION_DATE
           WHERE ID = NEW.ID;
           
diff --git a/openbis/source/sql/postgresql/125/function-125.sql b/openbis/source/sql/postgresql/125/function-125.sql
index 6f86e7a2f49f34aaefd2373eebdd147f7bb34c54..7e36c0f97ef34f6cb313e5d2d156f64a662f4c27 100644
--- a/openbis/source/sql/postgresql/125/function-125.sql
+++ b/openbis/source/sql/postgresql/125/function-125.sql
@@ -1044,6 +1044,7 @@ CREATE OR REPLACE RULE METAPROJECT_ASSIGNMENTS_INSERT AS
 			   SAMP_ID,
 			   DATA_ID,
 			   MATE_ID,
+			   DEL_ID,
 			   CREATION_DATE
        ) VALUES (
          NEW.ID, 
@@ -1052,6 +1053,7 @@ CREATE OR REPLACE RULE METAPROJECT_ASSIGNMENTS_INSERT AS
 			   NEW.SAMP_ID,
 			   NEW.DATA_ID,
 			   NEW.MATE_ID,
+			   NEW.DEL_ID,
 			   NEW.CREATION_DATE
        );
 
@@ -1065,6 +1067,7 @@ CREATE OR REPLACE RULE METAPROJECT_ASSIGNMENTS_UPDATE AS
 			   		SAMP_ID = NEW.SAMP_ID,
 			   		DATA_ID = NEW.DATA_ID,
 			   		MATE_ID = NEW.MATE_ID,
+			   		DEL_ID = NEW.DEL_ID,
 			   		CREATION_DATE = NEW.CREATION_DATE
           WHERE ID = NEW.ID;
           
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java
index 0ec57aef3dfb655340b021e724f5d81d16b42c67..83dbb8d2eeb3923de7ea31006ecc04e75a26d94f 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java
@@ -713,13 +713,13 @@ public final class GenericServerTest extends AbstractServerTestCase
         context.checking(new Expectations()
             {
                 {
-                    one(commonServer)
-                            .updateMaterial(SESSION_TOKEN, materialId, properties, version);
+                    one(commonServer).updateMaterial(SESSION_TOKEN, materialId, properties, null,
+                            version);
                     will(returnValue(newModificationDate));
                 }
             });
         assertEquals(newModificationDate,
-                createServer().updateMaterial(SESSION_TOKEN, materialId, properties, version));
+                createServer().updateMaterial(SESSION_TOKEN, materialId, properties, null, version));
         context.assertIsSatisfied();
     }