From d28efaec4e217fc36b9ee312d353f1f40b258bb9 Mon Sep 17 00:00:00 2001
From: buczekp <buczekp>
Date: Fri, 8 Oct 2010 11:51:40 +0000
Subject: [PATCH] [LMS-1762] added permlinks to context menu in normal view
 mode (first few cases)

SVN: 18220
---
 .../application/MatchingEntitiesPanel.java    | 15 ++-
 .../framework/AbstractTabItemFactory.java     |  8 ++
 .../framework/ComponentProvider.java          |  8 ++
 .../application/framework/MainTabPanel.java   | 94 ++++++++++++++-----
 .../locator/ProjectLocatorResolver.java       |  4 +-
 .../renderer/InternalLinkCellRenderer.java    | 46 ---------
 .../application/renderer/LinkRenderer.java    | 32 +++++--
 .../ui/AuthorizationGroupGrid.java            |  7 +-
 .../ui/PropertyValueRenderers.java            |  6 +-
 .../client/application/ui/TypedTableGrid.java | 36 +++----
 .../ui/columns/framework/LinkExtractor.java   | 22 +++++
 .../ui/data/AbstractExternalDataGrid.java     |  3 +-
 .../ui/experiment/ExperimentBrowserGrid.java  |  6 +-
 .../ProjectSelectionTreeGridContainer.java    | 26 ++---
 .../ui/grid/AbstractBrowserGrid.java          |  7 +-
 .../listener/OpenEntityDetailsTabHelper.java  |  8 +-
 .../ui/material/MaterialBrowserGrid.java      |  3 +-
 .../application/ui/project/ProjectGrid.java   |  6 +-
 .../ui/sample/SampleBrowserGrid.java          | 11 ++-
 .../ui/vocabulary/VocabularyGrid.java         | 11 +--
 .../shared/basic/URLMethodWithParameters.java | 10 ++
 .../ProteinByExperimentBrowserGrid.java       | 18 ++--
 .../SampleAbundanceBrowserGrid.java           |  7 +-
 23 files changed, 232 insertions(+), 162 deletions(-)
 delete mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/InternalLinkCellRenderer.java

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java
index 7f49a6f4fed..da934f66f21 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java
@@ -37,7 +37,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.Matc
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.MatchingEntityModel.MatchingEntityColumnKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.plugin.IClientPlugin;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.plugin.IClientPluginFactory;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.AbstractBrowserGrid;
 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.ICellListener;
@@ -52,9 +51,9 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdAndCodeHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BasicEntityType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MatchingEntity;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 
 /**
  * A {@link LayoutContainer} extension which displays the matching entities.
@@ -67,8 +66,8 @@ final class MatchingEntitiesPanel extends AbstractBrowserGrid<MatchingEntity, Ma
 
     static final String GRID_ID = PREFIX + "grid";
 
-    public static final String SHOW_RELATED_DATASETS_BUTTON_ID =
-            GRID_ID + "_show-related-datasets-button";
+    public static final String SHOW_RELATED_DATASETS_BUTTON_ID = GRID_ID
+            + "_show-related-datasets-button";
 
     private final SearchableEntity searchableEntity;
 
@@ -171,10 +170,10 @@ final class MatchingEntitiesPanel extends AbstractBrowserGrid<MatchingEntity, Ma
     protected ColumnDefsAndConfigs<MatchingEntity> createColumnsDefinition()
     {
         ColumnDefsAndConfigs<MatchingEntity> schema =
-                BaseEntityModel.createColumnConfigs(MatchingEntityModel
-                        .getStaticColumnsDefinition(), viewContext);
-        schema.setGridCellRendererFor(MatchingEntityColumnKind.IDENTIFIER.id(), LinkRenderer
-                .createLinkRenderer());
+                BaseEntityModel.createColumnConfigs(
+                        MatchingEntityModel.getStaticColumnsDefinition(), viewContext);
+        schema.setGridCellRendererFor(MatchingEntityColumnKind.IDENTIFIER.id(),
+                createInternalLinkCellRenderer());
         return schema;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/AbstractTabItemFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/AbstractTabItemFactory.java
index 109c7351b24..647a11f2051 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/AbstractTabItemFactory.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/AbstractTabItemFactory.java
@@ -51,6 +51,14 @@ public abstract class AbstractTabItemFactory
      */
     public abstract String getTabTitle();
 
+    /**
+     * Returns a permlink for this tab item or null if we don't support such a link.
+     */
+    public String tryGetPermlink()
+    {
+        return null;
+    }
+
     /**
      * True if the tab should become active.
      */
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/ComponentProvider.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/ComponentProvider.java
index 966c2fc242b..4a0fe9067a3 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/ComponentProvider.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/ComponentProvider.java
@@ -29,6 +29,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.Authori
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.GroupGrid;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.PersonGrid;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.RoleAssignmentGrid;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.LinkExtractor;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.data.DataSetBatchUpdatePanel;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.data.DataSetSearchHitGrid;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.data.DataSetTypeGrid;
@@ -135,6 +136,13 @@ public final class ComponentProvider
 
                     return getMessage(Dict.SAMPLE_BROWSER);
                 }
+
+                @Override
+                public String tryGetPermlink()
+                {
+                    return LinkExtractor.createSampleBrowserLink(initialGroupOrNull,
+                            initialSampleTypeOrNull);
+                }
             };
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/MainTabPanel.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/MainTabPanel.java
index ef8fbedf4d5..040935582d0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/MainTabPanel.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/MainTabPanel.java
@@ -28,6 +28,7 @@ import com.extjs.gxt.ui.client.event.MenuEvent;
 import com.extjs.gxt.ui.client.event.SelectionListener;
 import com.extjs.gxt.ui.client.event.TabPanelEvent;
 import com.extjs.gxt.ui.client.widget.Component;
+import com.extjs.gxt.ui.client.widget.MessageBox;
 import com.extjs.gxt.ui.client.widget.TabItem;
 import com.extjs.gxt.ui.client.widget.TabPanel;
 import com.extjs.gxt.ui.client.widget.layout.FitLayout;
@@ -40,6 +41,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict;
 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.help.HelpPageIdentifier;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.ConfirmationDialog;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.WindowUtils;
 import ch.systemsx.cisd.openbis.generic.shared.basic.URLMethodWithParameters;
@@ -85,7 +87,7 @@ public class MainTabPanel extends TabPanel implements IMainPanel
         final MainTabItem intro =
                 new MainTabItem(
                         DefaultTabItem.createUnaware(BLANK_TAB_TITLE, mainComponent, false),
-                        mainComponent.getId(), null);
+                        mainComponent.getId(), null, null);
         intro.setClosable(false);
         return intro;
     }
@@ -116,10 +118,12 @@ public class MainTabPanel extends TabPanel implements IMainPanel
             // 'ID_PREFIX'. We want the user to set an unique id.
             assert tabId.startsWith(GenericConstants.ID_PREFIX) : "Unspecified component id.";
             final HelpPageIdentifier helpId = tabItemFactory.getHelpPageIdentifier();
+            final String permlinkOrNull = tabItemFactory.tryGetPermlink();
             assert helpId != null : "Unspecified help identifier";
-            final MainTabItem newTab = new MainTabItem(tabItemFactory.create(), tabId, helpId);
+            final MainTabItem newTab =
+                    new MainTabItem(tabItemFactory.create(), tabId, helpId, permlinkOrNull);
             // WORKAROUND to fix problems when paging toolbar's layout is performed in a hidden tab
-            newTab.setHideMode(HideMode.OFFSETS); 
+            newTab.setHideMode(HideMode.OFFSETS);
             add(newTab);
             openTabs.put(tabId, newTab);
             maybeActivate(newTab, inBackground);
@@ -143,6 +147,12 @@ public class MainTabPanel extends TabPanel implements IMainPanel
         }
     }
 
+    // context menu
+
+    private MenuItem helpMenuItem;
+
+    private MenuItem bookmarkMenuItem;
+
     @Override
     protected void onItemContextMenu(TabItem item, int x, int y)
     {
@@ -159,27 +169,59 @@ public class MainTabPanel extends TabPanel implements IMainPanel
         // refresh the menu
         if (shouldInitializeContextMenu)
         {
-            MenuItem menuItem = new MenuItem("Help", new SelectionListener<MenuEvent>()
-                {
-                    @Override
-                    public void componentSelected(MenuEvent ce)
-                    {
-                        MainTabItem selectedTab = (MainTabItem) ce.getContainer().getData("tab");
-                        URLMethodWithParameters url =
-                                new URLMethodWithParameters(
-                                        GenericConstants.HELP_REDIRECT_SERVLET_NAME);
-                        HelpPageIdentifier helpPageId = selectedTab.getHelpPageIdentifier();
-                        url.addParameter(GenericConstants.HELP_REDIRECT_PAGE_TITLE_KEY,
-                                helpPageId.getHelpPageTitle(viewContext));
-                        url.addParameter(GenericConstants.HELP_REDIRECT_SPECIFIC_KEY,
-                                Boolean.toString(helpPageId.isSpecific()));
-                        WindowUtils.openWindow(URL.encode(url.toString()));
-                    }
-                });
-            closeContextMenu.add(menuItem);
+            helpMenuItem = createHelpMenuItem();
+            bookmarkMenuItem = createBookmarkMenuItem();
+            closeContextMenu.add(helpMenuItem);
+            closeContextMenu.add(bookmarkMenuItem);
             super.onItemContextMenu(item, x, y);
         }
+        boolean bookmarkNotAvailable = ((MainTabItem) item).getPermlinkOrNull() == null;
+        bookmarkMenuItem.setEnabled(bookmarkNotAvailable == false);
+    }
+
+    private MenuItem createHelpMenuItem()
+    {
+        return new MenuItem("Help", new SelectionListener<MenuEvent>()
+            {
+                @Override
+                public void componentSelected(MenuEvent ce)
+                {
+                    MainTabItem selectedTab = (MainTabItem) ce.getContainer().getData("tab");
+                    URLMethodWithParameters url =
+                            new URLMethodWithParameters(GenericConstants.HELP_REDIRECT_SERVLET_NAME);
+                    HelpPageIdentifier helpPageId = selectedTab.getHelpPageIdentifier();
+                    url.addParameter(GenericConstants.HELP_REDIRECT_PAGE_TITLE_KEY,
+                            helpPageId.getHelpPageTitle(viewContext));
+                    url.addParameter(GenericConstants.HELP_REDIRECT_SPECIFIC_KEY,
+                            Boolean.toString(helpPageId.isSpecific()));
+                    WindowUtils.openWindow(URL.encode(url.toString()));
+                }
+            });
+    }
 
+    private MenuItem createBookmarkMenuItem()
+    {
+        return new MenuItem(viewContext.getMessage(Dict.PERMLINK),
+                new SelectionListener<MenuEvent>()
+                    {
+                        @Override
+                        public void componentSelected(MenuEvent ce)
+                        {
+                            MainTabItem selectedTab =
+                                    (MainTabItem) ce.getContainer().getData("tab");
+                            String permlinkToken = selectedTab.getPermlinkOrNull();
+                            assert permlinkToken != null;
+                            URLMethodWithParameters permlink = new URLMethodWithParameters("");
+                            permlink.addHistoryToken(permlinkToken);
+                            String link =
+                                    LinkRenderer.renderAsLinkWithAnchor("link",
+                                            permlink.toString(), false);
+                            MessageBox.info(viewContext.getMessage(Dict.PERMLINK), "Copy this "
+                                    + link
+                                    + " and use it to access openBIS with current tab opened.",
+                                    null);
+                        }
+                    });
     }
 
     //
@@ -191,14 +233,17 @@ public class MainTabPanel extends TabPanel implements IMainPanel
 
         private final String idPrefix;
 
+        private final String permlinkOrNull;
+
         private final HelpPageIdentifier helpPageIdentifier;
 
         public MainTabItem(final ITabItem tabItem, final String idPrefix,
-                final HelpPageIdentifier helpPageIdentifier)
+                final HelpPageIdentifier helpPageIdentifier, String permlinkOrNull)
         {
             this.tabItem = tabItem;
             this.idPrefix = idPrefix;
             this.helpPageIdentifier = helpPageIdentifier;
+            this.permlinkOrNull = permlinkOrNull;
             setId(idPrefix + TAB_SUFFIX);
             setClosable(true);
             setLayout(new FitLayout());
@@ -219,6 +264,11 @@ public class MainTabPanel extends TabPanel implements IMainPanel
             return helpPageIdentifier;
         }
 
+        public String getPermlinkOrNull()
+        {
+            return permlinkOrNull;
+        }
+
         @Override
         public void close()
         {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ProjectLocatorResolver.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ProjectLocatorResolver.java
index f22ba4999eb..6d4d5dca2db 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ProjectLocatorResolver.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ProjectLocatorResolver.java
@@ -3,6 +3,7 @@ package ch.systemsx.cisd.openbis.generic.client.web.client.application.locator;
 import ch.systemsx.cisd.openbis.generic.client.web.client.ICommonClientServiceAsync;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.LinkExtractor;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.listener.OpenEntityDetailsTabHelper;
 import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BasicProjectIdentifier;
@@ -82,7 +83,8 @@ public class ProjectLocatorResolver extends AbstractViewLocatorResolver
         protected final void process(final Project result)
         {
             // TODO 2010-05-03, Piotr Buczek: Project data are loaded twice
-            OpenEntityDetailsTabHelper.open(viewContext, result, false);
+            final String href = LinkExtractor.tryExtract(result);
+            OpenEntityDetailsTabHelper.open(viewContext, result, false, href);
         }
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/InternalLinkCellRenderer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/InternalLinkCellRenderer.java
deleted file mode 100644
index f0f07834993..00000000000
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/InternalLinkCellRenderer.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer;
-
-import com.extjs.gxt.ui.client.store.ListStore;
-import com.extjs.gxt.ui.client.widget.grid.ColumnData;
-import com.extjs.gxt.ui.client.widget.grid.Grid;
-import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
-
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.MultilineHTML;
-
-public class InternalLinkCellRenderer implements GridCellRenderer<BaseEntityModel<?>>
-{
-    private final boolean renderOriginalValueForEmptyToken;
-
-    public InternalLinkCellRenderer(boolean renderOriginalValueForEmptyToken)
-    {
-        this.renderOriginalValueForEmptyToken = renderOriginalValueForEmptyToken;
-    }
-
-    public InternalLinkCellRenderer()
-    {
-        this(false);
-    }
-
-    public Object render(BaseEntityModel<?> model, String property, ColumnData config,
-            int rowIndex, int colIndex, ListStore<BaseEntityModel<?>> store,
-            Grid<BaseEntityModel<?>> grid)
-    {
-        if (model.get(property) == null)
-        {
-            return "";
-        } else
-        { // TODO 2010-05-18, IA: almost the same as LinkRenderer#createLinkRenderer()
-            String originalValue = String.valueOf(model.get(property));
-            String tokenOrNull = model.tryGetLink(property);
-            if (tokenOrNull == null && renderOriginalValueForEmptyToken)
-            {
-                return new MultilineHTML(originalValue).toString();
-            } else
-            {
-                String href = "#" + (tokenOrNull != null ? tokenOrNull : "");
-                return LinkRenderer.renderAsLinkWithAnchor(originalValue, href, false);
-            }
-        }
-    }
-}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/LinkRenderer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/LinkRenderer.java
index 384223dfdba..280d54c135f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/LinkRenderer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/renderer/LinkRenderer.java
@@ -33,6 +33,7 @@ import com.google.gwt.user.client.ui.Widget;
 
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.CommonViewContext.ClientStaticState;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.MultilineHTML;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IDelegatedAction;
 
 /**
@@ -43,23 +44,40 @@ public class LinkRenderer
 {
     private static final String LINK_STYLE = "link-style";
 
-    public static GridCellRenderer<BaseEntityModel<?>> createLinkRenderer()
+    public static GridCellRenderer<BaseEntityModel<?>> createLinkRenderer(
+            final boolean renderOriginalValueForEmptyToken)
     {
         return new GridCellRenderer<BaseEntityModel<?>>()
-            {// TODO 2010-05-18, IA: almost the same as
-             // InternalLinkCellRenderer#createLinkRenderer()
+            {
                 public Object render(BaseEntityModel<?> model, String property, ColumnData config,
                         int rowIndex, int colIndex, ListStore<BaseEntityModel<?>> store,
                         Grid<BaseEntityModel<?>> grid)
                 {
-                    String text = model.get(property).toString();
-                    String tokenOrNull = model.tryGetLink(property);
-                    String href = "#" + (tokenOrNull != null ? tokenOrNull : "");
-                    return LinkRenderer.renderAsLinkWithAnchor(text, href, false);
+                    if (model.get(property) == null)
+                    {
+                        return "";
+                    } else
+                    {
+                        String originalValue = model.get(property).toString();
+                        String tokenOrNull = model.tryGetLink(property);
+                        if (tokenOrNull == null && renderOriginalValueForEmptyToken)
+                        {
+                            return new MultilineHTML(originalValue).toString();
+                        } else
+                        {
+                            String href = "#" + (tokenOrNull != null ? tokenOrNull : "");
+                            return LinkRenderer.renderAsLinkWithAnchor(originalValue, href, false);
+                        }
+                    }
                 }
             };
     }
 
+    public static GridCellRenderer<BaseEntityModel<?>> createLinkRenderer()
+    {
+        return createLinkRenderer(false);
+    }
+
     public static GridCellRenderer<BaseEntityModel<?>> createLinkRenderer(final String text)
     {
         return new GridCellRenderer<BaseEntityModel<?>>()
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AuthorizationGroupGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AuthorizationGroupGrid.java
index 45d062708ba..e69ecabf099 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AuthorizationGroupGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AuthorizationGroupGrid.java
@@ -39,7 +39,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpP
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpPageIdentifier.HelpPageAction;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpPageIdentifier.HelpPageDomain;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.AuthorizationGroupColDefKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.GroupColDefKind;
@@ -69,8 +68,8 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKin
  */
 public class AuthorizationGroupGrid extends AbstractSimpleBrowserGrid<AuthorizationGroup>
 {
-    public static final String BROWSER_ID =
-            GenericConstants.ID_PREFIX + "authorization-group-browser";
+    public static final String BROWSER_ID = GenericConstants.ID_PREFIX
+            + "authorization-group-browser";
 
     public static final String GRID_ID = BROWSER_ID + "_grid";
 
@@ -176,7 +175,7 @@ public class AuthorizationGroupGrid extends AbstractSimpleBrowserGrid<Authorizat
         ColumnDefsAndConfigs<AuthorizationGroup> schema = super.createColumnsDefinition();
         schema.setGridCellRendererFor(GroupColDefKind.DESCRIPTION.id(),
                 createMultilineStringCellRenderer());
-        schema.setGridCellRendererFor(GroupColDefKind.CODE.id(), LinkRenderer.createLinkRenderer());
+        schema.setGridCellRendererFor(GroupColDefKind.CODE.id(), createInternalLinkCellRenderer());
         return schema;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/PropertyValueRenderers.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/PropertyValueRenderers.java
index 199a40ab029..e2e262244a9 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/PropertyValueRenderers.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/PropertyValueRenderers.java
@@ -481,15 +481,15 @@ public final class PropertyValueRenderers
         public Widget getAsWidget(final Project project)
         {
             final String displayText = project.getIdentifier();
+            final String href = LinkExtractor.tryExtract(project);
             final ClickHandler listener = new ClickHandler()
                 {
                     public void onClick(ClickEvent event)
                     {
-                        OpenEntityDetailsTabHelper.open(viewContext, project, WidgetUtils
-                                .ifSpecialKeyPressed(event.getNativeEvent()));
+                        OpenEntityDetailsTabHelper.open(viewContext, project,
+                                WidgetUtils.ifSpecialKeyPressed(event.getNativeEvent()), href);
                     }
                 };
-            String href = LinkExtractor.tryExtract(project);
             final Widget link = LinkRenderer.getLinkWidget(displayText, listener, href, false);
 
             return link;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java
index 763ea38a361..80ca6229718 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java
@@ -31,7 +31,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAs
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.IDisplayTypeIDGenerator;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.RealNumberRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionUI;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.AbstractBrowserGrid;
@@ -49,8 +48,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelColumnHeader;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
 
 /**
- * 
- *
  * @author Franz-Josef Elmer
  */
 public abstract class TypedTableGrid<T extends IsSerializable>
@@ -59,7 +56,7 @@ public abstract class TypedTableGrid<T extends IsSerializable>
 {
     private final Map<String, ICellListenerAndLinkGenerator<T>> listenerLinkGenerators =
             new HashMap<String, ICellListenerAndLinkGenerator<T>>();
-    
+
     private List<TableModelColumnHeader> headers;
 
     private Map<String, IColumnDefinition<TableModelRowWithObject<T>>> columnDefinitions;
@@ -75,7 +72,7 @@ public abstract class TypedTableGrid<T extends IsSerializable>
     {
         super(viewContext, gridId, displayTypeIDGenerator);
     }
-    
+
     /**
      * Lists table rows. Implementations of this method usually call a server method.
      */
@@ -105,7 +102,7 @@ public abstract class TypedTableGrid<T extends IsSerializable>
                 String id = header.getId();
                 if (listenerLinkGenerators.containsKey(id))
                 {
-                    definitions.setGridCellRendererFor(id, LinkRenderer.createLinkRenderer());
+                    definitions.setGridCellRendererFor(id, createInternalLinkCellRenderer());
                 }
                 if (header.getDataType() == DataTypeCode.REAL)
                 {
@@ -122,10 +119,10 @@ public abstract class TypedTableGrid<T extends IsSerializable>
     {
         return new BaseEntityModel<TableModelRowWithObject<T>>(entity, createColDefinitions());
     }
-    
+
     /**
-     * Registers for the specified column a cell listener and link generator.
-     * This method should be called in the constructor. 
+     * Registers for the specified column a cell listener and link generator. This method should be
+     * called in the constructor.
      */
     protected void registerListenerAndLinkGenerator(String columnID,
             final ICellListenerAndLinkGenerator<T> listenerLinkGenerator)
@@ -133,7 +130,7 @@ public abstract class TypedTableGrid<T extends IsSerializable>
         listenerLinkGenerators.put(columnID, listenerLinkGenerator);
         registerLinkClickListenerFor(columnID, listenerLinkGenerator);
     }
-    
+
     private List<IColumnDefinitionUI<TableModelRowWithObject<T>>> createColDefinitions()
     {
         List<IColumnDefinitionUI<TableModelRowWithObject<T>>> list =
@@ -147,7 +144,8 @@ public abstract class TypedTableGrid<T extends IsSerializable>
                 {
                     title = viewContext.getMessage(header.getId());
                 }
-                ICellListenerAndLinkGenerator<T> linkGeneratorOrNull = listenerLinkGenerators.get(header.getId());
+                ICellListenerAndLinkGenerator<T> linkGeneratorOrNull =
+                        listenerLinkGenerators.get(header.getId());
                 list.add(new TypedTableGridColumnDefinitionUI<T>(header, title, linkGeneratorOrNull));
             }
         }
@@ -184,7 +182,7 @@ public abstract class TypedTableGrid<T extends IsSerializable>
     {
         return true;
     }
-    
+
     @Override
     protected void refresh()
     {
@@ -192,15 +190,17 @@ public abstract class TypedTableGrid<T extends IsSerializable>
     }
 
     @Override
-    protected void showEntityViewer(TableModelRowWithObject<T> entity, boolean editMode, boolean inBackground)
+    protected void showEntityViewer(TableModelRowWithObject<T> entity, boolean editMode,
+            boolean inBackground)
     {
     }
 
     @Override
     protected List<IColumnDefinition<TableModelRowWithObject<T>>> getInitialFilters()
     {
-        
-        List<IColumnDefinition<TableModelRowWithObject<T>>> definitions = new ArrayList<IColumnDefinition<TableModelRowWithObject<T>>>();
+
+        List<IColumnDefinition<TableModelRowWithObject<T>>> definitions =
+                new ArrayList<IColumnDefinition<TableModelRowWithObject<T>>>();
         List<String> ids = getColumnIdsOfFilters();
         for (String id : ids)
         {
@@ -212,12 +212,12 @@ public abstract class TypedTableGrid<T extends IsSerializable>
         }
         return definitions;
     }
-    
+
     protected List<String> getColumnIdsOfFilters()
     {
-        return Collections.<String>emptyList();
+        return Collections.<String> emptyList();
     }
-    
+
     public DatabaseModificationKind[] getRelevantModifications()
     {
         return new DatabaseModificationKind[] {};
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/framework/LinkExtractor.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/framework/LinkExtractor.java
index 9fb735834b4..06020b219aa 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/framework/LinkExtractor.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/framework/LinkExtractor.java
@@ -16,8 +16,10 @@
 
 package ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework;
 
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.locator.BrowserLocatorResolver;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.locator.MaterialLocatorResolver;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.locator.ProjectLocatorResolver;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.locator.ViewLocator;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithPermId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.PermlinkUtilities;
@@ -34,6 +36,26 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
 public class LinkExtractor
 {
 
+    // browser links
+
+    public static final String createSampleBrowserLink(String spaceOrNull, String entityTypeOrNull)
+    {
+        URLMethodWithParameters url = new URLMethodWithParameters("");
+        url.addParameter(ViewLocator.ACTION_PARAMETER, BrowserLocatorResolver.BROWSE_ACTION);
+        url.addParameter(ViewLocator.ENTITY_PARAMETER, EntityKind.SAMPLE.name());
+        if (spaceOrNull != null)
+        {
+            url.addParameter(BrowserLocatorResolver.GROUP_PARAMETER_KEY, spaceOrNull);
+        }
+        if (entityTypeOrNull != null)
+        {
+            url.addParameter(BrowserLocatorResolver.TYPE_PARAMETER_KEY, entityTypeOrNull);
+        }
+        return tryPrint(url);
+    }
+
+    // detail view links
+
     public static String tryExtract(IEntityInformationHolderWithPermId entityOrNull)
     {
         if (entityOrNull == null)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java
index 33dbb1581e5..2ab2460fc34 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java
@@ -33,7 +33,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewConte
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.DisplayTypeIDGenerator;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.EntityGridModelFactory;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.data.CommonExternalDataColDefKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.AbstractEntityBrowserGrid;
@@ -321,7 +320,7 @@ public abstract class AbstractExternalDataGrid
     protected ColumnDefsAndConfigs<ExternalData> createColumnsDefinition()
     {
         ColumnDefsAndConfigs<ExternalData> schema = createColumnsSchema();
-        GridCellRenderer<BaseEntityModel<?>> linkRenderer = LinkRenderer.createLinkRenderer();
+        GridCellRenderer<BaseEntityModel<?>> linkRenderer = createInternalLinkCellRenderer();
         schema.setGridCellRendererFor(CommonExternalDataColDefKind.SAMPLE.id(), linkRenderer);
         schema.setGridCellRendererFor(CommonExternalDataColDefKind.SAMPLE_IDENTIFIER.id(),
                 linkRenderer);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java
index 09e14e9d98b..0c7b50938e0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ExperimentBrowserGrid.java
@@ -39,6 +39,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.Base
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.EntityGridModelFactory;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.DisplayedAndSelectedEntities;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.LinkExtractor;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.experiment.CommonExperimentColDefKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.sample.CommonSampleColDefKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.AbstractEntityBrowserGrid;
@@ -151,8 +152,9 @@ public class ExperimentBrowserGrid extends
                     {
                         public void handle(Experiment rowItem, boolean keyPressed)
                         {
-                            OpenEntityDetailsTabHelper.open(viewContext, rowItem.getProject(),
-                                    keyPressed);
+                            final Project project = rowItem.getProject();
+                            final String href = LinkExtractor.tryExtract(project);
+                            OpenEntityDetailsTabHelper.open(viewContext, project, keyPressed, href);
                         }
                     });
         setId(BROWSER_ID);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionTreeGridContainer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionTreeGridContainer.java
index 5ea9ec45f71..4764a35d88c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionTreeGridContainer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/experiment/ProjectSelectionTreeGridContainer.java
@@ -65,9 +65,9 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DefaultResultSetCo
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
 import ch.systemsx.cisd.openbis.generic.shared.basic.ICodeHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Space;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 
 /**
  * {@link LayoutContainer} containing a {@link TreeGrid} with projects loaded from the server. Main
@@ -174,8 +174,8 @@ public final class ProjectSelectionTreeGridContainer extends LayoutContainer imp
     private ColumnConfig createCodeColumn()
     {
         final ColumnConfig columnConfig =
-                new ColumnConfig(ModelDataPropertyNames.CODE, viewContext
-                        .getMessage(Dict.PROJECT_SELECTOR_CODE_COLUMN), 1);
+                new ColumnConfig(ModelDataPropertyNames.CODE,
+                        viewContext.getMessage(Dict.PROJECT_SELECTOR_CODE_COLUMN), 1);
 
         columnConfig.setRenderer(new WidgetTreeGridCellRenderer<ModelData>()
             {
@@ -199,8 +199,8 @@ public final class ProjectSelectionTreeGridContainer extends LayoutContainer imp
                 {
                     final Space space = (Space) model.get(ModelDataPropertyNames.OBJECT);
                     final Widget result = new InlineHTML(space.getCode());
-                    result.setTitle(createTooltipText(viewContext.getMessage(Dict.GROUP), space
-                            .getCode(), space.getDescription()));
+                    result.setTitle(createTooltipText(viewContext.getMessage(Dict.GROUP),
+                            space.getCode(), space.getDescription()));
                     return result;
                 }
 
@@ -208,15 +208,16 @@ public final class ProjectSelectionTreeGridContainer extends LayoutContainer imp
                 {
                     final Project project = (Project) model.get(ModelDataPropertyNames.OBJECT);
 
+                    final String href = LinkExtractor.tryExtract(project);
                     final ClickHandler listener = new ClickHandler()
                         {
                             public void onClick(ClickEvent event)
                             {
-                                OpenEntityDetailsTabHelper.open(viewContext, project, WidgetUtils
-                                        .ifSpecialKeyPressed(event.getNativeEvent()));
+                                OpenEntityDetailsTabHelper.open(viewContext, project,
+                                        WidgetUtils.ifSpecialKeyPressed(event.getNativeEvent()),
+                                        href);
                             }
                         };
-                    String href = LinkExtractor.tryExtract(project);
                     // TODO 2010-06-14, IA: try to adjust the size and use the info icon
                     // AbstractImagePrototype infoIcon =
                     // AbstractImagePrototype.create(viewContext.getImageBundle()
@@ -234,8 +235,8 @@ public final class ProjectSelectionTreeGridContainer extends LayoutContainer imp
 
                     final FlowPanel panel =
                             new FlowPanelWithLinkAppearingOnMouseOver(project, detailsLink);
-                    panel.setTitle(createTooltipText("Project", project.getCode(), project
-                            .getDescription()));
+                    panel.setTitle(createTooltipText("Project", project.getCode(),
+                            project.getDescription()));
                     panel.add(new InlineHTML(project.getCode() + " "));
                     panel.add(detailsLink);
                     return panel;
@@ -396,8 +397,7 @@ public final class ProjectSelectionTreeGridContainer extends LayoutContainer imp
 
     private void selectByIdentifierIfPossible(String projectIdentifier)
     {
-        GWTUtils
-                .setSelectedItem(tree, ModelDataPropertyNames.PROJECT_IDENTIFIER, projectIdentifier);
+        GWTUtils.setSelectedItem(tree, ModelDataPropertyNames.PROJECT_IDENTIFIER, projectIdentifier);
     }
 
     public DatabaseModificationKind[] getRelevantModifications()
@@ -410,7 +410,7 @@ public final class ProjectSelectionTreeGridContainer extends LayoutContainer imp
         refreshTree();
     }
 
-    // 
+    //
     // Helper classes
     //
     private final class ListProjectsCallback extends AbstractAsyncCallback<ResultSet<Project>>
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java
index 4adf6a35a3d..3b6ae3546aa 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java
@@ -80,7 +80,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.plugin.IClientPlugin;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.plugin.IClientPluginFactory;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.InternalLinkCellRenderer;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.MultilineStringCellRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.RealNumberRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.ComponentEventLogger;
@@ -782,7 +782,7 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
             }
             return result;
         }
-        
+
         @Override
         /* Note: we want to differentiate between callbacks in different subclasses of this grid. */
         public String getCallbackId()
@@ -791,7 +791,6 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
         }
     }
 
-
     // wraps this browser into the interface appropriate for the toolbar. If this class would just
     // implement the interface it could be very confusing for the code reader.
     protected final IBrowserGridActionInvoker asActionInvoker()
@@ -1409,7 +1408,7 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
 
     protected final GridCellRenderer<BaseEntityModel<?>> createInternalLinkCellRenderer()
     {
-        return new InternalLinkCellRenderer();
+        return LinkRenderer.createLinkRenderer();
     }
 
     // ------- generic static helpers
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/listener/OpenEntityDetailsTabHelper.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/listener/OpenEntityDetailsTabHelper.java
index 81d5db2fb89..35245561918 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/listener/OpenEntityDetailsTabHelper.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/listener/OpenEntityDetailsTabHelper.java
@@ -91,7 +91,7 @@ public class OpenEntityDetailsTabHelper
     }
 
     public static void open(final IViewContext<?> viewContext, final Project project,
-            boolean keyPressed)
+            boolean keyPressed, final String permlinkOrNull)
     {
         AbstractTabItemFactory tabFactory;
         final TechId projectId = TechId.create(project);
@@ -123,9 +123,13 @@ public class OpenEntityDetailsTabHelper
                     return new HelpPageIdentifier(HelpPageDomain.PROJECT, HelpPageAction.VIEW);
                 }
 
+                @Override
+                public String tryGetPermlink()
+                {
+                    return permlinkOrNull;
+                }
             };
         tabFactory.setInBackground(keyPressed);
         DispatcherHelper.dispatchNaviEvent(tabFactory);
     }
-
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/material/MaterialBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/material/MaterialBrowserGrid.java
index fd8f97beb6c..057f2b8e0be 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/material/MaterialBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/material/MaterialBrowserGrid.java
@@ -31,7 +31,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewConte
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.DisplayTypeIDGenerator;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.EntityGridModelFactory;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.DisplayedAndSelectedEntities;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.material.CommonMaterialColDefKind;
@@ -231,7 +230,7 @@ public class MaterialBrowserGrid extends
                 getColumnsFactory().createColumnsSchema(viewContext,
                         criteria.getListCriteria().getMaterialType());
         schema.setGridCellRendererFor(CommonMaterialColDefKind.CODE.id(),
-                LinkRenderer.createLinkRenderer());
+                createInternalLinkCellRenderer());
         return schema;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/project/ProjectGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/project/ProjectGrid.java
index 00e65a442cc..523c0c905be 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/project/ProjectGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/project/ProjectGrid.java
@@ -40,7 +40,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpP
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpPageIdentifier.HelpPageAction;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpPageIdentifier.HelpPageDomain;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.AbstractRegistrationForm;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.AbstractViewer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
@@ -55,8 +54,8 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteri
 import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
 
 /**
  * Grid displaying projects.
@@ -159,8 +158,7 @@ public class ProjectGrid extends AbstractSimpleBrowserGrid<Project>
     protected ColumnDefsAndConfigs<Project> createColumnsDefinition()
     {
         ColumnDefsAndConfigs<Project> schema = super.createColumnsDefinition();
-        schema.setGridCellRendererFor(ProjectColDefKind.CODE.id(), LinkRenderer
-                .createLinkRenderer());
+        schema.setGridCellRendererFor(ProjectColDefKind.CODE.id(), createInternalLinkCellRenderer());
         schema.setGridCellRendererFor(ProjectColDefKind.DESCRIPTION.id(),
                 createMultilineStringCellRenderer());
         return schema;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleBrowserGrid.java
index 9d384e6bcea..25a03731c93 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleBrowserGrid.java
@@ -42,9 +42,10 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.SampleTypeDisplayID;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.SampleModelFactory;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.InternalLinkCellRenderer;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.DisplayedAndSelectedEntities;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.LinkExtractor;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.sample.AbstractParentSampleColDef;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.sample.CommonSampleColDefKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.AbstractEntityBrowserGrid;
@@ -77,6 +78,7 @@ 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.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListSampleCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
@@ -357,8 +359,9 @@ public class SampleBrowserGrid extends
                     {
                         public void handle(Sample rowItem, boolean keyPressed)
                         {
-                            OpenEntityDetailsTabHelper.open(viewContext, rowItem.getExperiment()
-                                    .getProject(), keyPressed);
+                            final Project project = rowItem.getExperiment().getProject();
+                            final String href = LinkExtractor.tryExtract(project);
+                            OpenEntityDetailsTabHelper.open(viewContext, project, keyPressed, href);
                         }
                     });
         setId(browserId);
@@ -634,7 +637,7 @@ public class SampleBrowserGrid extends
 
     protected final GridCellRenderer<BaseEntityModel<?>> createParentLinkCellRenderer()
     {
-        return new InternalLinkCellRenderer(true);
+        return LinkRenderer.createLinkRenderer(true);
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyGrid.java
index 6b58b11c341..5550a8f8c32 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyGrid.java
@@ -43,7 +43,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpP
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpPageIdentifier.HelpPageAction;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.help.HelpPageIdentifier.HelpPageDomain;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.VocabularyColDefKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.DescriptionField;
@@ -61,8 +60,8 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 
 /**
  * Grid displaying vocabularies.
@@ -188,8 +187,8 @@ public class VocabularyGrid extends AbstractSimpleBrowserGrid<Vocabulary>
     protected ColumnDefsAndConfigs<Vocabulary> createColumnsDefinition()
     {
         ColumnDefsAndConfigs<Vocabulary> schema = super.createColumnsDefinition();
-        schema.setGridCellRendererFor(VocabularyColDefKind.CODE.id(), LinkRenderer
-                .createLinkRenderer());
+        schema.setGridCellRendererFor(VocabularyColDefKind.CODE.id(),
+                createInternalLinkCellRenderer());
         schema.setGridCellRendererFor(VocabularyColDefKind.DESCRIPTION.id(),
                 createMultilineStringCellRenderer());
         return schema;
@@ -245,8 +244,8 @@ public class VocabularyGrid extends AbstractSimpleBrowserGrid<Vocabulary>
                 @Override
                 public String getTabTitle()
                 {
-                    return viewContext.getMessage(Dict.VOCABULARY_TERMS_BROWSER, vocabulary
-                            .getCode());
+                    return viewContext.getMessage(Dict.VOCABULARY_TERMS_BROWSER,
+                            vocabulary.getCode());
                 }
             };
         tabFactory.setInBackground(inBackground);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/URLMethodWithParameters.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/URLMethodWithParameters.java
index 76c698e2658..c443a0c03fb 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/URLMethodWithParameters.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/URLMethodWithParameters.java
@@ -64,6 +64,16 @@ public class URLMethodWithParameters implements IsSerializable
         delim = '#';
     }
 
+    /**
+     * Adds history token (prepends '#' prefix, no encoding)
+     */
+    public void addHistoryToken(String historyToken)
+    {
+        startHistoryToken();
+        builder.append(historyToken);
+        delim = '&';
+    }
+
     /**
      * Adds a parameter with specified name and value with optional encoding.
      */
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
index 476656598a2..e9696fd11fe 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
@@ -36,7 +36,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewConte
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.AbstractTabItemFactory;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.DispatcherHelper;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.RealNumberRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionUI;
@@ -54,8 +53,8 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BasicEntityType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.IPhosphoNetXClientServiceAsync;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.application.columns.InternalAbundanceColumnDefinition;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.application.columns.ProteinColDefKind;
@@ -72,8 +71,8 @@ class ProteinByExperimentBrowserGrid extends AbstractSimpleBrowserGrid<ProteinIn
 {
     private static final String ABUNDANCE_PROPERTY_KEY = "ABUNDANCE";
 
-    private static final String PREFIX =
-            GenericConstants.ID_PREFIX + "protein-by-experiment-browser";
+    private static final String PREFIX = GenericConstants.ID_PREFIX
+            + "protein-by-experiment-browser";
 
     // browser consists of the grid and additional toolbars (paging, filtering)
     public static final String BROWSER_ID = PREFIX + "_main";
@@ -151,8 +150,7 @@ class ProteinByExperimentBrowserGrid extends AbstractSimpleBrowserGrid<ProteinIn
     }
 
     private ProteinByExperimentBrowserGrid(
-            final IViewContext<IPhosphoNetXClientServiceAsync> viewContext,
-            Experiment experiment)
+            final IViewContext<IPhosphoNetXClientServiceAsync> viewContext, Experiment experiment)
     {
         super(viewContext.getCommonViewContext(), BROWSER_ID, GRID_ID, false,
                 PhosphoNetXDisplayTypeIDGenerator.PROTEIN_BY_EXPERIMENT_BROWSER_GRID);
@@ -165,8 +163,8 @@ class ProteinByExperimentBrowserGrid extends AbstractSimpleBrowserGrid<ProteinIn
                         public void handle(ProteinInfo rowItem, boolean keyPressed)
                         {
                             AbstractTabItemFactory tabItemFactory =
-                                    ProteinViewer.createTabItemFactory(viewContext, toolbar
-                                            .getExperimentOrNull(), rowItem);
+                                    ProteinViewer.createTabItemFactory(viewContext,
+                                            toolbar.getExperimentOrNull(), rowItem);
                             tabItemFactory.setInBackground(keyPressed);
                             DispatcherHelper.dispatchNaviEvent(tabItemFactory);
                         }
@@ -229,8 +227,8 @@ class ProteinByExperimentBrowserGrid extends AbstractSimpleBrowserGrid<ProteinIn
             columns.add(columnDefinition);
         }
         definitions.addColumns(columns);
-        definitions.setGridCellRendererFor(ProteinColDefKind.ACCESSION_NUMBER.id(), LinkRenderer
-                .createLinkRenderer());
+        definitions.setGridCellRendererFor(ProteinColDefKind.ACCESSION_NUMBER.id(),
+                createInternalLinkCellRenderer());
         RealNumberRenderer renderer =
                 new RealNumberRenderer(viewContext.getDisplaySettingsManager()
                         .getRealNumberFormatingParameters());
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/SampleAbundanceBrowserGrid.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/SampleAbundanceBrowserGrid.java
index 7051c42ff43..50a882c7cd9 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/SampleAbundanceBrowserGrid.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/SampleAbundanceBrowserGrid.java
@@ -30,7 +30,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericCon
 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.model.BaseEntityModel;
-import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.RealNumberRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.AbstractEntityBrowserGrid;
@@ -48,10 +47,10 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 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.PropertyType;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.IPhosphoNetXClientServiceAsync;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.application.columns.SampleAbundanceColDefKind;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.application.model.SampleAbundanceModelFactory;
@@ -309,8 +308,8 @@ public class SampleAbundanceBrowserGrid
 
         ColumnDefsAndConfigs<SampleWithPropertiesAndAbundance> schema =
                 SampleAbundanceModelFactory.createColumnsSchema(viewContext, propertyTypes);
-        schema.setGridCellRendererFor(SampleAbundanceColDefKind.CODE.id(), LinkRenderer
-                .createLinkRenderer());
+        schema.setGridCellRendererFor(SampleAbundanceColDefKind.CODE.id(),
+                createInternalLinkCellRenderer());
         schema.setGridCellRendererFor(SampleAbundanceColDefKind.ABUNDANCE.id(),
                 new RealNumberRenderer(viewContext.getDisplaySettingsManager()
                         .getRealNumberFormatingParameters()));
-- 
GitLab