From a09ff3ea2a01cbd425b207690d6c98b06d26c7ad Mon Sep 17 00:00:00 2001 From: felmer <felmer> Date: Tue, 22 Mar 2011 15:30:11 +0000 Subject: [PATCH] LMS-2146 HistoryWidget and embedding into welcome page SVN: 20454 --- .../client/web/client/application/Dict.java | 2 + .../framework/WelcomePanelHelper.java | 14 +- .../application/model/EntityVisitModel.java | 41 ++++ .../client/application/ui/AbstractViewer.java | 8 + .../application/ui/widget/HistoryWidget.java | 183 ++++++++++++++++++ .../generic/server/AbstractServer.java | 30 ++- .../openbis/generic/server/CommonServer.java | 3 +- .../EntityVisitComparatorByTimeStamp.java | 20 ++ .../generic/shared/basic/dto/EntityVisit.java | 14 ++ .../cisd/openbis/public/common-dictionary.js | 5 + 10 files changed, 306 insertions(+), 14 deletions(-) create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/EntityVisitModel.java create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/HistoryWidget.java create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/EntityVisitComparatorByTimeStamp.java 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 6332612f305..089742db2fb 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 @@ -1080,5 +1080,7 @@ public abstract class Dict public static final String WARNING_NO_SCRIPT_MESSAGE = "warning_no_script_message"; + public static final String LAST_VISITS = "last_visits"; + // ----- end generic ------------------ } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/WelcomePanelHelper.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/WelcomePanelHelper.java index dd5a2b82392..7a8a5b4be90 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/WelcomePanelHelper.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/framework/WelcomePanelHelper.java @@ -16,11 +16,14 @@ package ch.systemsx.cisd.openbis.generic.client.web.client.application.framework; +import com.extjs.gxt.ui.client.Style.LayoutRegion; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.LayoutContainer; -import com.extjs.gxt.ui.client.widget.layout.FitLayout; +import com.extjs.gxt.ui.client.widget.layout.BorderLayout; +import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData; import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.HistoryWidget; /** * Helper methods for creating a panel with welcome information. @@ -35,10 +38,15 @@ public class WelcomePanelHelper public static final Component createWelcomePanel(IViewContext<?> viewContext, String idPrefix) { - final LayoutContainer layoutContainer = new LayoutContainer(new FitLayout()); + final LayoutContainer layoutContainer = new LayoutContainer(new BorderLayout()); + layoutContainer.setStyleAttribute("background-color", "white"); layoutContainer.setId(idPrefix + "welcome"); + if (viewContext.getDisplaySettingsManager().getVisits().isEmpty() == false) + { + layoutContainer.add(new HistoryWidget(viewContext), new BorderLayoutData(LayoutRegion.WEST, 0.3f)); + } HtmlPage welcomePage = new HtmlPage(getWelcomePageBaseName(viewContext)); - layoutContainer.add(welcomePage); + layoutContainer.add(welcomePage, new BorderLayoutData(LayoutRegion.CENTER, 1f)); return layoutContainer; } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/EntityVisitModel.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/EntityVisitModel.java new file mode 100644 index 00000000000..2ca3cccfc8e --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/model/EntityVisitModel.java @@ -0,0 +1,41 @@ +/* + * Copyright 2011 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.model; + +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityVisit; + +/** + * + * + * @author Franz-Josef Elmer + */ +public class EntityVisitModel extends SimplifiedBaseModel +{ + + private static final long serialVersionUID = 1L; + + public EntityVisitModel(EntityVisit visit) + { + set(ModelDataPropertyNames.CODE, visit.getIdentifier()); + set(ModelDataPropertyNames.OBJECT, visit); + } + + public EntityVisit getVisit() + { + return (EntityVisit) get(ModelDataPropertyNames.OBJECT); + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractViewer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractViewer.java index 39c15fc4eee..3118b209ab4 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractViewer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractViewer.java @@ -47,7 +47,9 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IDele import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IMessageProvider; import ch.systemsx.cisd.openbis.generic.shared.basic.ICodeHolder; import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolder; +import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithIdentifier; import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithProperties; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityVisit; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.api.IManagedProperty; @@ -184,6 +186,12 @@ public abstract class AbstractViewer<D extends IEntityInformationHolder> extends /** Updates data displayed in the browser (needed to open editor view). */ protected void updateOriginalData(D newData) { + if (newData instanceof IEntityInformationHolderWithIdentifier) + { + IEntityInformationHolderWithIdentifier entity = (IEntityInformationHolderWithIdentifier) newData; + EntityVisit entityVisit = new EntityVisit(entity); + viewContext.getDisplaySettingsManager().rememberVisit(entityVisit); + } this.originalData = newData; this.displayIdSuffix = newData.getEntityType().getCode(); updateTitle(getOriginalDataDescription()); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/HistoryWidget.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/HistoryWidget.java new file mode 100644 index 00000000000..292c712b698 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/HistoryWidget.java @@ -0,0 +1,183 @@ +/* + * Copyright 2011 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.widget; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.extjs.gxt.ui.client.data.ModelData; +import com.extjs.gxt.ui.client.store.ListStore; +import com.extjs.gxt.ui.client.store.TreeStore; +import com.extjs.gxt.ui.client.widget.ContentPanel; +import com.extjs.gxt.ui.client.widget.Label; +import com.extjs.gxt.ui.client.widget.grid.ColumnConfig; +import com.extjs.gxt.ui.client.widget.grid.ColumnData; +import com.extjs.gxt.ui.client.widget.grid.ColumnModel; +import com.extjs.gxt.ui.client.widget.grid.Grid; +import com.extjs.gxt.ui.client.widget.layout.FitLayout; +import com.extjs.gxt.ui.client.widget.treegrid.TreeGrid; +import com.extjs.gxt.ui.client.widget.treegrid.WidgetTreeGridCellRenderer; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.Widget; + +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.model.EntityVisitModel; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.ModelDataPropertyNames; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.SimplifiedBaseModel; +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.LinkExtractor; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.listener.OpenEntityDetailsTabHelper; +import ch.systemsx.cisd.openbis.generic.shared.basic.EntityVisitComparatorByTimeStamp; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityVisit; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier; + +/** + * History of last visited detail views. + * + * @author Franz-Josef Elmer + */ +public class HistoryWidget extends ContentPanel +{ + private static final class SimpleModel extends SimplifiedBaseModel + { + private static final long serialVersionUID = 1L; + + public SimpleModel(String item) + { + set(ModelDataPropertyNames.CODE, item); + } + } + + public HistoryWidget(final IViewContext<?> viewContext) + { + setLayout(new FitLayout()); + setHeaderVisible(true); + setHeading(viewContext.getMessage(Dict.LAST_VISITS)); + TreeStore<ModelData> store = createStore(viewContext); + ColumnModel columnModel = createColumnModel(viewContext); + + final TreeGrid<ModelData> treeGrid = new TreeGrid<ModelData>(store, columnModel); + treeGrid.setAutoExpandColumn(ModelDataPropertyNames.CODE); + treeGrid.getTreeView().setForceFit(true); + treeGrid.getTreeView().setSortingEnabled(false); + treeGrid.getStyle().setNodeCloseIcon(null); + treeGrid.getStyle().setNodeOpenIcon(null); + treeGrid.setAutoExpand(true); + treeGrid.setHideHeaders(true); + add(treeGrid); + } + + private TreeStore<ModelData> createStore(final IViewContext<?> viewContext) + { + TreeStore<ModelData> store = new TreeStore<ModelData>(); + List<EntityVisit> visits = viewContext.getDisplaySettingsManager().getVisits(); + Collections.sort(visits, new EntityVisitComparatorByTimeStamp()); + EntityKind[] values = EntityKind.values(); + for (EntityKind entityKind : values) + { + String entityKindAsString = entityKind.toString(); + SimpleModel item = new SimpleModel(entityKind.getDescription() + "s"); + store.add(item, true); + Map<String, List<EntityVisit>> typeToVisitMap = new HashMap<String, List<EntityVisit>>(); + for (EntityVisit visit : visits) + { + if (entityKindAsString.equals(visit.getEntityKind())) + { + List<EntityVisit> list = typeToVisitMap.get(visit.getEntityTypeCode()); + if (list == null) + { + list = new ArrayList<EntityVisit>(); + typeToVisitMap.put(visit.getEntityTypeCode(), list); + } + list.add(visit); + } + } + String[] types = typeToVisitMap.keySet().toArray(new String[0]); + Arrays.sort(types); + for (String type : types) + { + SimpleModel typeModel = new SimpleModel(type); + store.add(item, typeModel, true); + List<EntityVisit> list = typeToVisitMap.get(type); + Set<String> permIds = new HashSet<String>(); + for (EntityVisit visit : list) + { + String permID = visit.getPermID(); + if (permIds.contains(permID) == false) + { + permIds.add(permID); + store.add(typeModel, new EntityVisitModel(visit), false); + } + } + } + } + return store; + } + + private ColumnModel createColumnModel(final IViewContext<?> viewContext) + { + ColumnConfig columnConfig = new ColumnConfig(ModelDataPropertyNames.CODE, "", 1); + columnConfig.setRenderer(new WidgetTreeGridCellRenderer<ModelData>() + { + @Override + public Widget getWidget(ModelData model, String property, ColumnData config, + int rowIndex, int colIndex, ListStore<ModelData> store, Grid<ModelData> grid) + { + if (model instanceof EntityVisitModel) + { + EntityVisitModel evm = (EntityVisitModel) model; + + EntityVisit visit = evm.getVisit(); + final String displayText = visit.getIdentifier(); + final EntityKind entityKind = EntityKind.valueOf(visit.getEntityKind()); + final String permID = visit.getPermID(); + final String href; + if (entityKind == EntityKind.MATERIAL) + { + href = + LinkExtractor.tryExtract(MaterialIdentifier + .tryParseIdentifier(permID)); + } else + { + href = LinkExtractor.createPermlink(entityKind, permID); + } + final ClickHandler listener = new ClickHandler() + { + public void onClick(ClickEvent event) + { + OpenEntityDetailsTabHelper.open(viewContext, entityKind, + permID, false); + } + }; + return LinkRenderer.getLinkWidget(displayText, listener, href, false); + } + return new Label(model.get(property).toString()); + } + }); + columnConfig.setMenuDisabled(true); + return new ColumnModel(Arrays.asList(columnConfig)); + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java index df590950ab2..1ad976e90de 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java @@ -18,7 +18,8 @@ package ch.systemsx.cisd.openbis.generic.server; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -42,6 +43,7 @@ import ch.systemsx.cisd.openbis.generic.shared.IRemoteHostValidator; import ch.systemsx.cisd.openbis.generic.shared.IServer; import ch.systemsx.cisd.openbis.generic.shared.ResourceNames; import ch.systemsx.cisd.openbis.generic.shared.authorization.validator.ExpressionValidator; +import ch.systemsx.cisd.openbis.generic.shared.basic.EntityVisitComparatorByTimeStamp; import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DisplaySettings; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind; @@ -362,15 +364,7 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp if (person != null) { List<EntityVisit> visits = joinVisits(displaySettings, person); - Collections.sort(visits, new Comparator<EntityVisit>() - { - public int compare(EntityVisit o1, EntityVisit o2) - { - long t1 = o1.getTimeStamp(); - long t2 = o2.getTimeStamp(); - return t1 < t2 ? 1 : (t1 > t2 ? -1 : 0); - } - }); + sortAndRemoveMultipleVisits(visits); for (int i = visits.size() - 1; i >= maxEntityVisits; i--) { visits.remove(i); @@ -384,6 +378,22 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp } } + private void sortAndRemoveMultipleVisits(List<EntityVisit> visits) + { + Collections.sort(visits, new EntityVisitComparatorByTimeStamp()); + Set<String> permIds = new HashSet<String>(); + for (Iterator<EntityVisit> iterator = visits.iterator(); iterator.hasNext();) + { + EntityVisit entityVisit = iterator.next(); + String permID = entityVisit.getPermID(); + if (permIds.contains(permID)) + { + iterator.remove(); + } + permIds.add(permID); + } + } + @SuppressWarnings("deprecation") private List<EntityVisit> joinVisits(DisplaySettings displaySettings, PersonPE person) { 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 d021068b88f..babb64cb943 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 @@ -1467,7 +1467,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt return createInformationHolder(entityKind, permId, getDAOFactory().getPermIdDAO() .tryToFindByPermId(permId, DtoConverters.convertEntityKind(entityKind))); case MATERIAL: - break; + MaterialIdentifier identifier = MaterialIdentifier.tryParseIdentifier(permId); + return getMaterialInformationHolder(sessionToken, identifier); } throw UserFailureException.fromTemplate("Operation not available for " + entityKind.getDescription() + "s"); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/EntityVisitComparatorByTimeStamp.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/EntityVisitComparatorByTimeStamp.java new file mode 100644 index 00000000000..eecf0089fcc --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/EntityVisitComparatorByTimeStamp.java @@ -0,0 +1,20 @@ +package ch.systemsx.cisd.openbis.generic.shared.basic; + +import java.util.Comparator; + +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityVisit; + +/** + * Comparator between {@link EntityVisit} instances. Newer visit comes before older visit. + * + * @author Franz-Josef Elmer + */ +public class EntityVisitComparatorByTimeStamp implements Comparator<EntityVisit> +{ + public int compare(EntityVisit o1, EntityVisit o2) + { + long t1 = o1.getTimeStamp(); + long t2 = o2.getTimeStamp(); + return t1 < t2 ? 1 : (t1 > t2 ? -1 : 0); + } +} \ No newline at end of file diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EntityVisit.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EntityVisit.java index 5f4db6546f1..4ba5f2e5fb6 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EntityVisit.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/EntityVisit.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.openbis.generic.shared.basic.dto; +import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithIdentifier; import ch.systemsx.cisd.openbis.generic.shared.basic.ISerializable; /** @@ -37,6 +38,19 @@ public class EntityVisit implements ISerializable private String permID; private long timeStamp; + + public EntityVisit() + { + } + + public EntityVisit(IEntityInformationHolderWithIdentifier entity) + { + setEntityKind(entity.getEntityKind().toString()); + setEntityTypeCode(entity.getEntityType().getCode()); + setIdentifier(entity.getIdentifier()); + setPermID(entity.getPermId()); + setTimeStamp(System.currentTimeMillis()); + } public String getEntityKind() { 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 453d1caa100..c93d7c65c91 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 @@ -743,6 +743,11 @@ show_details: "Show", evaluation_in_progress: "Evaluation in progress...", warning_no_script_title: "Empty script", warning_no_script_message: "No script provided", + +// +// History Widget +// +last_visits: "Last Visited Entities", // LAST LINE: KEEP IT AT THE END lastline: "" // we need a line without a comma -- GitLab