Changeset 8083
- Timestamp:
- Oct 20, 2022, 2:56:36 PM (11 months ago)
- Location:
- trunk
- Files:
-
- 75 edited
- 6 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk
- Property svn:mergeinfo changed
/branches/3.19-stable merged: 8044,8046-8080 /tags/3.19.4 (added) merged: 8081
- Property svn:mergeinfo changed
-
trunk/doc/src/docbook/user/webclient.xml
r7982 r8083 2379 2379 <variablelist> 2380 2380 <varlistentry> 2381 <term><guilabel> Item type</guilabel></term>2381 <term><guilabel>Parent/child</guilabel></term> 2382 2382 <listitem> 2383 2383 <para> … … 2393 2393 <para> 2394 2394 This list will automatically be updated with all available subtypes 2395 for the selected main item type. It is not possible to select items2396 without a subtype.2395 for the selected parent or child item type. It is not possible to select 2396 items without a subtype. 2397 2397 </para> 2398 2398 </listitem> 2399 2399 </varlistentry> 2400 2400 2401 2401 <varlistentry> 2402 <term><guilabel>Column</guilabel></term> 2403 <listitem> 2404 <para> 2405 Once the subtype has been selected, this list will display all possible 2406 columns that can be added to the table. Most regular properties such as 2402 <term><guilabel>with link to</guilabel></term> 2403 <listitem> 2404 <para> 2405 This is an optional selection that is only available if a parent item type has 2406 been selected in the left list. In this list is possible to select 2407 an item type that should be a child type of the selected parent type. 2408 A subtype for the child item must also be selected. 2409 </para> 2410 </listitem> 2411 </varlistentry> 2412 2413 <varlistentry> 2414 <term><guilabel>Columns/Annotations/Extensions</guilabel></term> 2415 <listitem> 2416 <para> 2417 When the parent/child selection contains a valid combination, 2418 the table will display all possible columns that can be selected 2419 and added to the table. Most regular properties such as 2407 2420 <guilabel>Name</guilabel> and <guilabel>Registration date</guilabel> 2408 can be selected, as well as all annotation types that has been defined 2409 and linked to the selected subtype (marked with <guilabel>[A]</guilabel>). 2421 can be selected and are displayed in the <guilabel>Columns</guilabel> 2422 column. All annotation types that has been defined and linked to the 2423 selected subtype are displayed in the <guilabel>Annotations</guilabel> 2424 column. It is also possible for external extensions to add more columns 2425 that can be selected in the <guilabel>Extensions</guilabel> column. 2410 2426 </para> 2411 2427 </listitem> … … 2428 2444 <para> 2429 2445 Use the <guibutton>Add</guibutton> button to add columns to the table. 2430 It is possible change the main item type and subtype to add more columns.2431 When you are done, use the &gbClose; button to close the dialog.2446 It is possible change the parent/child item type and subtype to add more 2447 columns. When you are done, use the &gbClose; button to close the dialog. 2432 2448 </para> 2433 2449 -
trunk/src/clients/web/net/sf/basedb/clients/web/Base.java
r7982 r8083 37 37 import net.sf.basedb.core.Platform; 38 38 import net.sf.basedb.core.PlatformVariant; 39 import net.sf.basedb.core.Project; 39 40 import net.sf.basedb.core.Protocol; 40 41 import net.sf.basedb.core.ReporterList; … … 655 656 cc.getSelected().add(Values.getInt(itemId)); 656 657 } 657 658 String allColumns = cc.getSetting("columns");659 Set<String> visibleParentCols = new HashSet<>();660 if (allColumns != null)661 {662 for (String col : allColumns.split(","))663 {664 if (col.startsWith("/")) visibleParentCols.add(col);665 }666 }667 658 668 659 while (names.hasMoreElements()) … … 684 675 property = property.substring(0, split); 685 676 } 686 if (property.startsWith("/") && value != null)687 {688 // If the filter is on a parent item and the column is no689 // longer visible, we need to remove the filter, since it690 // will get very confusing otherwise691 // There are some variants when column is the name of a parent item692 boolean isOkFilter = visibleParentCols.contains(property)693 || visibleParentCols.contains(property.replace("/name", "/."))694 || visibleParentCols.contains(property.replace(".name", ""));695 if (!isOkFilter) value = null;696 }697 677 if (value != null) 698 678 { … … 1521 1501 // Modified annotations 1522 1502 String modified = Values.getStringOrNull(request.getParameter("modifiedAnnotations")); 1503 int projectId = Values.getInt(request.getParameter("project_id"), -1); 1504 Project project = projectId > 0 ? Project.getById(dc, projectId) : null; 1523 1505 if (newItem == null) newItem = oldItem; 1524 1506 if (modified != null) … … 1560 1542 if (annotationId != null) 1561 1543 { 1562 a = Annotation.getById(dc, annotationId.intValue() );1544 a = Annotation.getById(dc, annotationId.intValue(), projectId >= 0); 1563 1545 1564 1546 if ("DELETE".equals(modifyType)) … … 1566 1548 if (source == Annotation.Source.PRIMARY) 1567 1549 { 1568 newAs.removeAnnotation(at); 1550 if (projectId >= 0) 1551 { 1552 newAs.removeProjectAnnotation(at, project); 1553 } 1554 else 1555 { 1556 newAs.removeAnnotation(at); 1557 } 1569 1558 } 1570 1559 else … … 1599 1588 if (!"DELETE".equals(modifyType)) 1600 1589 { 1601 a = newAs.getAnnotation(at);1590 a = projectId >= 0 ? newAs.getProjectAnnotation(at, project) : newAs.getAnnotation(at); 1602 1591 } 1603 1592 } -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/JspContext.java
r8045 r8083 200 200 201 201 /** 202 Add several scripts in one go. If the collection is null or empty 203 nothing is done. 204 @since 3.19.4 205 */ 206 public void addScripts(Collection<String> scriptsToAdd) 207 { 208 if (scriptsToAdd == null || scriptsToAdd.isEmpty()) return; 209 if (scripts == null) scripts = new HashMap<>(); 210 getSet(scripts, null).addAll(scriptsToAdd); 211 if (needResourcesPerExtension) 212 { 213 getSet(scripts, getCurrentExtension().getId()).addAll(scriptsToAdd); 214 } 215 } 216 217 /** 202 218 Get the Set that is stored under the given key. If 203 219 no Set exists it is created and stored in the map. … … 241 257 } 242 258 } 259 260 /** 261 Add several stylesheets in one go. If the collection is null or empty 262 nothing is done. 263 @since 3.19.4 264 */ 265 public void addStylesheets(Collection<String> stylesheetsToAdd) 266 { 267 if (stylesheetsToAdd == null || stylesheetsToAdd.isEmpty()) return; 268 if (stylesheets == null) stylesheets = new HashMap<>(); 269 getSet(stylesheets, null).addAll(stylesheetsToAdd); 270 if (needResourcesPerExtension) 271 { 272 getSet(stylesheets, getCurrentExtension().getId()).addAll(stylesheetsToAdd); 273 } 274 } 243 275 244 276 /** -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/AbstractListColumnBean.java
r7908 r8083 21 21 */ 22 22 package net.sf.basedb.clients.web.extensions.list; 23 24 import java.util.Collection; 23 25 24 26 import net.sf.basedb.core.DbControl; … … 65 67 private Enumeration<String, String> enumeration; 66 68 private Formatter<? super V> formatter; 69 private Formatter<Collection<? super V>> collectionFormatter; 67 70 68 71 public AbstractListColumnBean() … … 272 275 { 273 276 this.exportFormatter = exportFormatter; 277 } 278 279 @Override 280 public Formatter<Collection<? super V>> getCollectionFormatter() 281 { 282 return collectionFormatter == null ? ListColumnAction.super.getCollectionFormatter() : collectionFormatter; 283 } 284 public void setCollectionFormatter(Formatter<Collection<? super V>> collectionFormatter) 285 { 286 this.collectionFormatter = collectionFormatter; 274 287 } 275 288 -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/AnyLinkColumnActionFactory.java
r7874 r8083 8 8 import net.sf.basedb.clients.web.extensions.fileviewer.FileViewerContext; 9 9 import net.sf.basedb.clients.web.extensions.fileviewer.FileViewerUtil; 10 import net.sf.basedb.clients.web.extensions.list.RelatedItemColumn.Specification; 10 11 import net.sf.basedb.core.BasicItem; 11 12 import net.sf.basedb.core.DbControl; … … 47 48 String subContext = Values.getString(guiContext.getSubContext(), ""); 48 49 49 ItemContext cc = sc.getCurrentContext(item, subContext, null); 50 if (cc == null) return false; 51 String allColumns = cc.getSetting("columns"); 50 String allColumns = null; 51 if (RelatedItemExtensionColumn.SUBCONTEXT.equals(subContext)) 52 { 53 // The current item should be a Specification instance that may specify 54 // the column. Otherwise we load the columns from the regular list context 55 // which makes it possible to select linked item columns from a parent/child 56 // that are currently available 57 Specification spec = jspContext.getCurrentItem(); 58 if (spec != null) allColumns = spec.actionId; 59 subContext = ""; 60 } 61 if (allColumns == null) 62 { 63 ItemContext cc = sc.getCurrentContext(item, subContext, null); 64 if (cc == null) return false; 65 allColumns = cc.getSetting("columns"); 66 } 52 67 if (allColumns == null || !allColumns.contains("|")) return false; 53 68 … … 66 81 Item item = guiContext.getItem(); 67 82 String subContext = Values.getString(guiContext.getSubContext(), ""); 68 69 ItemContext cc = sc.getCurrentContext(item, subContext, null); 70 if (cc == null) return null; 71 72 String allColumns = cc.getSetting("columns"); 83 String allColumns = null; 84 if (RelatedItemExtensionColumn.SUBCONTEXT.equals(subContext)) 85 { 86 // The current item should be a Specification instance that may specify 87 // the column. Otherwise we load the columns from the regular list context 88 Specification spec = jspContext.getCurrentItem(); 89 if (spec != null) allColumns = spec.actionId; 90 subContext = ""; 91 } 92 if (allColumns == null) 93 { 94 ItemContext cc = sc.getCurrentContext(item, subContext, null); 95 if (cc == null) return null; 96 allColumns = cc.getSetting("columns"); 97 } 73 98 if (allColumns == null || !allColumns.contains("|")) return null; 74 99 -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/ListColumnAction.java
r7908 r8083 22 22 package net.sf.basedb.clients.web.extensions.list; 23 23 24 import java.util.Collection; 25 24 26 import net.sf.basedb.core.DbControl; 25 27 import net.sf.basedb.core.Type; 26 28 import net.sf.basedb.util.Enumeration; 27 29 import net.sf.basedb.util.extensions.Action; 30 import net.sf.basedb.util.formatter.CollectionFormatter; 28 31 import net.sf.basedb.util.formatter.Formatter; 29 32 … … 209 212 210 213 /** 214 Get a formatter that is intended to format a collection of values 215 that have been retrieved from this action. The default implementation 216 will return a formatter that creates a comma-separated string of 217 the individual values where each value is formatted with the formatter 218 from {@link #getFormatter()}. 219 @since 3.19.4 220 */ 221 @SuppressWarnings({ "rawtypes", "unchecked" }) 222 public default Formatter<Collection<? super V>> getCollectionFormatter() 223 { 224 return new CollectionFormatter(getFormatter()); 225 } 226 227 /** 211 228 Get the value that should be displayed in the column. This method is 212 229 called once for every item that is listed in the table. The returned -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/ListColumnUtil.java
r7605 r8083 37 37 import net.sf.basedb.core.plugin.GuiContext; 38 38 import net.sf.basedb.util.extensions.ExtensionPointBean; 39 import net.sf.basedb.util.extensions.ExtensionsFilter; 39 40 import net.sf.basedb.util.extensions.ExtensionsInvoker; 40 41 import net.sf.basedb.util.extensions.Registry; … … 95 96 96 97 /** 98 @see #useExtensions(JspContext, ExtensionsFilter) 99 */ 100 public static <I> ExtensionsInvoker<ListColumnAction<I, ?>> useExtensions(JspContext jspContext) 101 { 102 return useExtensions(jspContext, null); 103 } 104 105 /** 97 106 Use list column extensions for a given gui context. This method 98 107 will assemble all specific and generic extension points and 99 then call {@link ExtensionsControl#useExtensions(JspContext, String...)}. 108 then call {@link ExtensionsControl#useExtensions(JspContext, ExtensionsFilter, String...)} 109 or {@link ExtensionsControl#useExtensions(JspContext, String...)} (if the filter is null) 100 110 101 111 @param jspContext The current jsp context 112 @param filter Optional filter for the extensions 102 113 @return An invoker instance 114 @since 3.19.4 103 115 */ 104 public static <I> ExtensionsInvoker<ListColumnAction<I, ?>> useExtensions(JspContext jspContext )116 public static <I> ExtensionsInvoker<ListColumnAction<I, ?>> useExtensions(JspContext jspContext, ExtensionsFilter filter) 105 117 { 106 118 String[] ep = new String[6]; // 6 is the maximum number of extension points … … 130 142 } 131 143 ep[index++] = EP_PREFIX + itemType.name().toLowerCase(); 132 return ExtensionsControl.useExtensions(jspContext, ep);144 return filter == null ? ExtensionsControl.useExtensions(jspContext, ep) : ExtensionsControl.useExtensions(jspContext, filter, ep); 133 145 } 134 146 -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/RelatedItemAnnotationColumn.java
r7893 r8083 5 5 import java.util.Set; 6 6 7 import net.sf.basedb.clients.web.formatter.FormatterFactory; 7 8 import net.sf.basedb.clients.web.util.HTML; 8 9 import net.sf.basedb.core.Annotatable; 9 10 import net.sf.basedb.core.AnnotationType; 10 import net.sf.basedb.core.Item; 11 import net.sf.basedb.core.SyncFilter.SourceItemTransform; 12 import net.sf.basedb.core.query.Restriction; 11 import net.sf.basedb.core.DbControl; 13 12 import net.sf.basedb.core.snapshot.AnnotationSnapshot; 14 13 import net.sf.basedb.core.snapshot.AnnotationTypeFilter; 15 14 import net.sf.basedb.util.Enumeration; 16 15 import net.sf.basedb.util.filter.Filter; 16 import net.sf.basedb.util.formatter.CollectionFormatter; 17 import net.sf.basedb.util.listable.SourceItemTransformer; 17 18 18 19 /** … … 28 29 private final Filter<AnnotationSnapshot> atFilter; 29 30 30 RelatedItemAnnotationColumn(int index, String id, Item sourceType, Item targetType, SourceItemTransform direction, Restriction targetRestriction, RelatedItemHelper helper, AnnotationType at) 31 @SuppressWarnings({ "unchecked", "rawtypes" }) 32 RelatedItemAnnotationColumn(DbControl dc, int index, Specification spec, RelatedItemHelper helper, AnnotationType at) 31 33 { 32 super( index, id, sourceType, targetType, direction, targetRestriction, helper);34 super(dc, index, spec, helper); 33 35 this.at = at; 34 36 this.atFilter = new AnnotationTypeFilter(at); 37 setTitle(spec.generateTitle(at.getName() + " [A]")); 38 setTooltip(spec.generateTooltip(at.getName())); 39 setValueType(at.getValueType()); 40 setExportFormatter(FormatterFactory.getTypeFormatter(dc.getSessionControl(), at.getValueType())); 41 if (!helper.lazy) setFormatter(new CollectionFormatter(getExportFormatter())); 42 35 43 if (at.isEnumeration()) 36 44 { … … 48 56 49 57 @Override 50 public Object getValue(RelatedItemHelper helper, Annotatable item )58 public Object getValue(RelatedItemHelper helper, Annotatable item, SourceItemTransformer preTransform) 51 59 { 52 Set<Annotatable> items = getRelatedItems(item );60 Set<Annotatable> items = getRelatedItems(item, preTransform); 53 61 if (items.size() == 0) return null; 54 62 -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/RelatedItemColumn.java
r7893 r8083 2 2 3 3 import java.util.Collections; 4 import java.util.HashMap;5 import java.util.Map;6 4 import java.util.Set; 7 5 import java.util.TreeSet; 8 6 9 import net.sf.basedb.clients.web.Base;10 import net.sf.basedb.clients.web.formatter.FormatterFactory;11 import net.sf.basedb.clients.web.formatter.LinkedItemFormatter;12 7 import net.sf.basedb.core.Annotatable; 13 8 import net.sf.basedb.core.AnnotationType; … … 15 10 import net.sf.basedb.core.Item; 16 11 import net.sf.basedb.core.ItemList; 17 import net.sf.basedb.core.ItemQuery;18 12 import net.sf.basedb.core.ItemSubtype; 19 13 import net.sf.basedb.core.Metadata; 20 14 import net.sf.basedb.core.RawDataTypes; 21 import net.sf.basedb.core. Type;15 import net.sf.basedb.core.StringUtil; 22 16 import net.sf.basedb.core.query.Expressions; 23 17 import net.sf.basedb.core.query.Hql; … … 27 21 import net.sf.basedb.core.RawDataType; 28 22 import net.sf.basedb.core.SyncFilter.SourceItemTransform; 29 import net.sf.basedb.util.Enumeration;30 23 import net.sf.basedb.util.NameableComparator; 31 24 import net.sf.basedb.util.Values; 25 import net.sf.basedb.util.extensions.ExtensionsInvoker; 32 26 import net.sf.basedb.util.filter.Filter; 33 import net.sf.basedb.util.formatter.CollectionFormatter;34 import net.sf.basedb.util.formatter.NameableFormatter;35 import net.sf.basedb.util.formatter.ToStringFormatter;36 27 import net.sf.basedb.util.listable.ListableUtil; 37 28 import net.sf.basedb.util.listable.RestrictionTransformer; … … 51 42 52 43 /** 53 For now, we assume that 'expr' is /DIRECTION/ITEMTYPE/subtype-id/#annotationtype-id or 44 For now, we assume that 'expr' is one of: 45 /DIRECTION/ITEMTYPE/subtype-id/#annotationtype-id 46 /DIRECTION/ITEMTYPE/subtype-id/!extension-id#action-id 54 47 /DIRECTION/ITEMTYPE/subtype-id/property 55 48 … … 58 51 */ 59 52 @SuppressWarnings({ "unchecked", "rawtypes" }) 60 public static RelatedItemColumn create(DbControl dc, int index, Item sourceType, String expr, RelatedItemHelper helper)53 public static RelatedItemColumn create(DbControl dc, int index, Specification spec, RelatedItemHelper helper) 61 54 { 62 55 RelatedItemColumn col = null; 63 56 try 64 57 { 65 String[] tmp = expr.split("/", 5); 66 SourceItemTransform direction = SourceItemTransform.CHILD_TO_PARENT; 67 int baseIndex = 1; 68 if (tmp.length == 5) 69 { 70 baseIndex = 2; 71 direction = "CHILD".equals(tmp[1]) ? SourceItemTransform.PARENT_TO_CHILD : SourceItemTransform.CHILD_TO_PARENT; 72 } 73 Item targetType = Item.valueOf(tmp[baseIndex]); 74 Filter<? super ItemList> itemListFilter = null; 75 Restriction targetRestriction = null; 76 String subtypeName = null; 77 if (targetType == Item.RAWBIOASSAY) 78 { 79 RawDataType rdt = RawDataTypes.getSafeRawDataType(tmp[baseIndex+1]); 80 subtypeName = rdt.getName(); 81 targetRestriction = Restrictions.eq(Hql.property("rawDataType"), Expressions.string(rdt.getId())); 82 itemListFilter = new RawDataTypeFilter(rdt); 58 if (spec.property.startsWith("/")) 59 { 60 // This is a multi-hop path: child --> parent --> child 61 if (spec.direction != SourceItemTransform.CHILD_TO_PARENT) 62 { 63 throw new RuntimeException("First multi-hop must be to /PARENT: " + spec.expression); 64 } 65 Specification nextSpec = Specification.parse(spec.targetType, spec.property); 66 if (nextSpec.property.startsWith("/")) 67 { 68 throw new RuntimeException("More than two PARENT/CHILD hops are not allowed: " + spec.expression); 69 } 70 if (nextSpec.direction != SourceItemTransform.PARENT_TO_CHILD) 71 { 72 throw new RuntimeException("Last multi-hop must be to /CHILD: " + spec.expression); 73 } 74 RelatedItemColumn nextHop = create(dc, index, nextSpec, helper); 75 col = new RelatedItemMultiHopColumn(dc, index, spec, helper, nextHop); 76 } 77 else if (spec.property.startsWith("#")) 78 { 79 AnnotationType at = AnnotationType.getById(dc, spec.annotationId); 80 col = new RelatedItemAnnotationColumn(dc, index, spec, helper, at); 81 } 82 else if (spec.property.startsWith("!")) 83 { 84 ListColumnAction <Annotatable, ?> action = null; 85 // Get the invoker that was saved by the RelatedItemColumnActionFactory.prepareContext() method 86 ExtensionsInvoker<ListColumnAction<Annotatable, ?>> invoker = helper.jspContext.getAttribute("RelatedItemInvoker."+spec.expression); 87 88 if (invoker != null) 89 { 90 invoker.getClientContext().setCurrentItem(spec); 91 for (ListColumnAction<Annotatable, ?> lca : invoker) 92 { 93 if (spec.actionId.equals(lca.getId()) || spec.actionId.equals(lca.getFilterProperty())) 94 { 95 action = lca; 96 break; 97 } 98 } 99 } 100 if (action == null) throw new RuntimeException("Extension not found: "+Values.getString(spec.extensionId)+"#"+spec.actionId); 101 col = new RelatedItemExtensionColumn(dc, index, spec, helper, action); 102 } 103 else if (spec.property.equals("§itemLists")) 104 { 105 Filter<? super ItemList> itemListFilter = null; 106 if (spec.targetType == Item.RAWBIOASSAY) 107 { 108 itemListFilter = new RawDataTypeFilter(spec.getRawDataType()); 109 } 110 else 111 { 112 itemListFilter = new SubtypeFilter(spec.getItemSubtype(dc)); 113 } 114 col = new RelatedItemListColumn(dc, index, spec, itemListFilter, helper); 83 115 } 84 116 else 85 117 { 86 ItemSubtype subtype = ItemSubtype.getById(dc, Values.getInt(tmp[baseIndex+1]));87 subtypeName = subtype.getName();88 targetRestriction = Restrictions.eq(Hql.property("itemSubtype"), Hql.entity(subtype));89 itemListFilter = new SubtypeFilter(subtype);90 }91 String propSpec = tmp[baseIndex+2];92 if (propSpec.startsWith("#"))93 {94 AnnotationType at = AnnotationType.getById(dc, Values.getInt(propSpec.substring(1)));95 col = new RelatedItemAnnotationColumn(index, expr, sourceType, targetType, direction, targetRestriction, helper, at);96 col.setTitle(subtypeName+"."+at.getName() + " [A]");97 col.setTooltip(subtypeName + " › "+at.getName());98 col.setValueType(at.getValueType());99 col.setClazz("relateditemcol");100 col.setExportFormatter(FormatterFactory.getTypeFormatter(dc.getSessionControl(), at.getValueType()));101 if (!helper.lazy) col.setFormatter(new CollectionFormatter(col.getExportFormatter()));102 }103 else if (propSpec.equals("§itemLists"))104 {105 col = new RelatedItemListColumn(dc, index, expr, sourceType, targetType, direction, targetRestriction, itemListFilter, helper);106 col.setTitle(subtypeName+".Item list");107 col.setValueType(Type.INT);108 col.setClazz("relateditemcol");109 col.setExportFormatter(new NameableFormatter());110 if (!helper.lazy) col.setFormatter(new CollectionFormatter(new LinkedItemFormatter()));111 }112 else113 {114 118 PropertyPath property = null; 115 if (".".equals(propSpec)) // special case '.' is equal to the "parent" item 116 { 117 col = new RelatedItemPropertyColumn(index, expr, sourceType, targetType, direction, targetRestriction, helper, null); 118 col.setTitle(subtypeName); 119 col.setTooltip(subtypeName); 120 } 121 else 122 { 123 Metadata m = Metadata.getInstance(targetType.getItemClass()); 124 property = m.getPropertyPath(propSpec, false); 125 col = new RelatedItemPropertyColumn(index, expr, sourceType, targetType, direction, targetRestriction, helper, property); 126 col.setTitle(subtypeName+"."+pathToTitle(propSpec)); 127 col.setTooltip(subtypeName + " › "+propSpec); 128 } 129 col.setClazz("relateditemcol"); 130 if (property == null || property.getHibernateType().isEntityType()) 131 { 132 col.setExportFormatter(new NameableFormatter()); 133 if (!helper.lazy) col.setFormatter(new CollectionFormatter(new LinkedItemFormatter())); 134 if ("job.itemSubtype".equals(propSpec)) 135 { 136 ItemQuery<ItemSubtype> jobSubtypesQuery = Base.getSubtypesQuery(Item.JOB); 137 col.setEnumeration(Enumeration.fromItems(jobSubtypesQuery.list(dc), null)); 138 col.setValueType(Type.INT); 139 } 140 else 141 { 142 if (col.getFilterProperty()==null) 143 { 144 // Add '.name' to make filtering work 145 String nameProperty = col.getProperty() + ".name"; 146 if (nameProperty.contains("/..")) nameProperty = nameProperty.replace("/..", "/"); 147 col.setFilterProperty(nameProperty); 148 } 149 col.setValueType(Type.STRING); 150 } 151 } 152 else 153 { 154 Type colType = Type.fromHibernateType(property.getHibernateType()); 155 if (colType != null) 156 { 157 col.setValueType(colType); 158 col.setExportFormatter(FormatterFactory.getTypeFormatter(dc.getSessionControl(), colType)); 159 } 160 else 161 { 162 // Last fallback -- if we get here the Type.fromHibernateType() should be update with special cases 163 col.setValueType(Type.STRING); 164 col.setExportFormatter(new ToStringFormatter<>()); 165 } 166 if (!helper.lazy) col.setFormatter(new CollectionFormatter(col.getExportFormatter())); 167 } 168 } 169 col.setClazz(col.getClazz() + (direction == SourceItemTransform.CHILD_TO_PARENT ? " parentitem" : " childitem")); 170 col.setTooltip((direction == SourceItemTransform.CHILD_TO_PARENT ? "Parent › " : "Child › ")+col.getTooltip()); 119 if (!".".equals(spec.property)) // special case '.' is equal to the "related" item 120 { 121 Metadata m = Metadata.getInstance(spec.targetType.getItemClass()); 122 property = m.getPropertyPath(spec.property, false); 123 } 124 col = new RelatedItemPropertyColumn(dc, index, spec, helper, property); 125 } 171 126 } 172 127 catch (RuntimeException ex) 173 128 { 174 129 ex.printStackTrace(System.out); 175 }176 177 130 col = new RelatedItemErrorColumn(index, spec, helper, ex.getMessage()); 131 132 } 178 133 return col; 179 134 } 180 135 181 private static Map<String, String> pathTitles = new HashMap<>();182 183 static184 {185 // See also add_relateditem_columns.js186 pathTitles.put("externalId", "External ID");187 pathTitles.put("bioWell.bioPlate", "Bioplate");188 pathTitles.put("entryDate", "Registered");189 pathTitles.put("remainingQuantity", "Remaining quantity");190 pathTitles.put("originalQuantity", "Original quantity");191 pathTitles.put("creationEvent.entryDate", "Registration date");192 pathTitles.put("creationEvent.eventDate", "Creation date");193 pathTitles.put("creationEvent.protocol", "Protocol");194 pathTitles.put("creationEvent.hardware", "Hardware");195 pathTitles.put("protocol", "Protocol");196 pathTitles.put("hardware", "Hardware");197 pathTitles.put("software", "Software");198 pathTitles.put("tag", "Tag");199 pathTitles.put("job.itemSubtype", "Job type");200 pathTitles.put("$itemLists", "Item lists");201 }202 203 /**204 Convert some paths to a more user-friendly title instead.205 */206 private static String pathToTitle(String path)207 {208 String title = pathTitles.get(path);209 return title == null ? path.substring(0, 1).toUpperCase() + path.substring(1) : title;210 }211 212 136 private final int index; 213 private final Item sourceType; 214 private final Item targetType; 137 private final Specification spec; 215 138 private final RelatedItemHelper helper; 216 private final SourceItemTransformerFactory transformerFactory;217 139 private final SourceItemTransformer transformer; 218 140 219 RelatedItemColumn( int index, String id, Item sourceType, Item targetType, SourceItemTransform direction, Restriction targetRestriction, RelatedItemHelper helper)141 RelatedItemColumn(DbControl dc, int index, Specification spec, RelatedItemHelper helper) 220 142 { 221 143 this.index = index; 222 144 this.helper = helper; 223 this.sourceType = sourceType; 224 this.targetType = targetType; 225 this.transformerFactory = ListableUtil.getTransformerFactory(targetType); 226 SourceItemTransformer tmp = transformerFactory.create(sourceType, direction); 227 this.transformer = targetRestriction == null ? tmp : new RestrictionTransformer(tmp, targetRestriction); 228 setId(id); 229 setProperty(id); 145 this.spec = spec; 146 if (dc != null) 147 { 148 SourceItemTransformerFactory transformerFactory = ListableUtil.getTransformerFactory(spec.targetType); 149 SourceItemTransformer tmp = transformerFactory.create(spec.sourceType, spec.direction); 150 Restriction targetRestriction = spec.createTargetTypeRestriction(dc); 151 if (targetRestriction != null) tmp = new RestrictionTransformer(tmp, targetRestriction); 152 this.transformer = tmp; 153 } 154 else 155 { 156 this.transformer = null; 157 } 158 setId(spec.expression); 159 setProperty(spec.expression); 230 160 setExportable(true); 231 161 setFilterable(true); 232 } 233 162 setClazz("relateditemcol" + 163 (spec.direction == SourceItemTransform.CHILD_TO_PARENT ? " parentitem" : " childitem")+ 164 (spec.multiHop ? " multihop" : "") 165 ); 166 } 167 168 /** 169 Is this column lazy-loaded? The default implementation 170 use the requested laza flag from the helper instance. 171 @since 3.19.4 172 */ 173 protected boolean isLazy() 174 { 175 return helper.lazy; 176 } 177 178 /** 179 Get the transformer implementation that is used to turn source item into target items. 180 @since 3.19.4 181 */ 182 protected SourceItemTransformer getTransformer() 183 { 184 return transformer; 185 } 186 234 187 /** 235 188 We finalize this implementation to make sure that the helper 236 189 implementation always get a chance to re-cycle transactions. 237 Subclasses should implement {@link #getValue(RelatedItemHelper, Annotatable )}190 Subclasses should implement {@link #getValue(RelatedItemHelper, Annotatable, SourceItemTransformer)} 238 191 to return the requested value from the item and use the 239 192 DbControl from {@link RelatedItemHelper#dc}. … … 242 195 public final Object getValue(DbControl dc, Annotatable item) 243 196 { 244 if ( helper.lazy) return getProxyTag(item);197 if (isLazy()) return getProxyTag(item); 245 198 item = helper.recycle(dc, item); 246 return getValue(helper, item );199 return getValue(helper, item, null); 247 200 } 248 201 … … 267 220 We finalize this implementation to make sure that the helper 268 221 implementation always get a chance to re-cycle transactions. 269 Subclasses should implement {@link #getValue(RelatedItemHelper, Annotatable)}222 Subclasses should override {@link #getExportValue(RelatedItemHelper, Annotatable, SourceItemTransformer)} 270 223 to return the requested value from the item and use the 271 224 DbControl from {@link RelatedItemHelper#dc}. … … 275 228 { 276 229 item = helper.recycle(dc, item); 277 return getValue(helper, item); 278 } 279 280 281 /** 282 Alternate implementation for loading data. 283 */ 284 protected abstract Object getValue(RelatedItemHelper helper, Annotatable item); 230 return getExportValue(helper, item, null); 231 } 232 233 234 /** 235 Alternate implementation for loading data. The item is normally an item of the 236 expected source type, but if a preTransform is present, the item may be of 237 different type and must be processed with the {@link SourceItemTransformer#transform(net.sf.basedb.util.listable.TransformContext, Set)} 238 method first. In most cases, implementation should simply call {@link #getRelatedItems(Annotatable, SourceItemTransformer)} 239 to load the final target items. 240 */ 241 protected abstract Object getValue(RelatedItemHelper helper, Annotatable item, SourceItemTransformer preTransform); 242 243 /** 244 Alternate implementation for loading data. The default implementation 245 simply call the {@link #getValue(RelatedItemHelper, Annotatable, SourceItemTransformer)} method. 246 @since 3.19.4 247 */ 248 protected Object getExportValue(RelatedItemHelper helper, Annotatable item, SourceItemTransformer preTransform) 249 { 250 return getValue(helper, item, preTransform); 251 } 285 252 286 253 /** … … 288 255 */ 289 256 @SuppressWarnings({ "unchecked", "rawtypes" }) 290 protected Set<Annotatable> getRelatedItems(Annotatable item) 291 { 292 Set<Integer> relatedIds = transformer.transform(helper.transformContext, Collections.singleton(item.getId())); 257 protected Set<Annotatable> getRelatedItems(Annotatable item, SourceItemTransformer preTransform) 258 { 259 Set<Integer> sourceIds = Collections.singleton(item.getId()); 260 if (preTransform != null) 261 { 262 sourceIds = preTransform.transform(helper.transformContext, sourceIds); 263 } 264 Set<Integer> relatedIds = transformer.transform(helper.transformContext, sourceIds); 293 265 294 266 Set<Annotatable> relatedItems = new TreeSet<Annotatable>(new NameableComparator(false)); 295 267 for (Integer id : relatedIds) 296 268 { 297 Annotatable a = targetType.getById(helper.dc, id);269 Annotatable a = spec.targetType.getById(helper.dc, id); 298 270 relatedItems.add(a); 299 271 } … … 301 273 } 302 274 275 /** 276 Represents a specification for loading a related parent or child item. 277 The general format is: /DIRECTION/ITEMTYPE/subtype-id/property 278 279 where 280 * DIRECTION: PARENT or CHILD 281 * ITEMTYPE: Item.name() value for the related item 282 * subtype-id: Id of an {@link ItemSubtype} or a {@link RawDataType} 283 * property: a specification of what value to retrieve from the related item. 284 It can have different formats: 285 - #annotationtype-id: Id of an annotation type 286 - !extension-id#action-id: Id of an extension and {@link ListColumnAction} to use for loading the value 287 Otherwise it is interpreted as a property that is defined on the targeted instance. 288 289 @since 3.19.4 290 */ 291 public static class Specification 292 { 293 294 public static Specification parse(Item sourceType, String expression) 295 { 296 Specification spec = new Specification(); 297 spec.sourceType = sourceType; 298 spec.expression = expression; 299 spec.directionRaw = "PARENT"; 300 spec.direction = SourceItemTransform.CHILD_TO_PARENT; 301 302 String[] tmp = expression.split("/", 5); 303 int baseIndex = 1; 304 if (tmp.length == 5) 305 { 306 baseIndex = 2; 307 spec.directionRaw = tmp[1]; 308 if ("CHILD".equals(spec.directionRaw)) spec.direction = SourceItemTransform.PARENT_TO_CHILD; 309 } 310 spec.targetType = Item.valueOf(tmp[baseIndex]); 311 spec.subtype = tmp[baseIndex+1]; 312 spec.property = tmp[baseIndex+2]; 313 314 if (spec.property.startsWith("/")) 315 { 316 spec.multiHop = true; 317 } 318 else if (spec.property.startsWith("#")) 319 { 320 spec.annotationId = Values.getInt(spec.property.substring(1)); 321 } 322 else if (spec.property.startsWith("!")) 323 { 324 // The normal property format is: !extension-id#action-id 325 // but it may be a filter specification: !x.action-id 326 // or (but it should never happen): !action-id 327 if (spec.property.startsWith("!x.")) 328 { 329 spec.actionId = spec.property.substring(3); 330 } 331 else 332 { 333 int splitAt = spec.property.indexOf('#'); 334 if (splitAt > 0) 335 { 336 spec.extensionId = Values.getStringOrNull(spec.property.substring(1, splitAt)); 337 spec.actionId = spec.property.substring(splitAt+1); 338 } 339 else 340 { 341 spec.actionId = spec.property.substring(1); 342 } 343 } 344 } 345 346 return spec; 347 } 348 349 String expression; 350 String directionRaw; 351 SourceItemTransform direction; 352 Item sourceType; 353 Item targetType; 354 String subtype; 355 String subtypeName; 356 String property; 357 int annotationId; 358 String extensionId; 359 String actionId; 360 boolean multiHop; 361 362 /** 363 Get the full expression that defines the targeted item and property. 364 */ 365 public String getExpression() 366 { 367 return expression; 368 } 369 370 /** 371 Get the raw value of the DIRECTION. 372 */ 373 public String getDirectionRaw() 374 { 375 return directionRaw; 376 } 377 378 /** 379 Get the parsed value of the DIRECTION. 380 */ 381 public SourceItemTransform getDirection() 382 { 383 return direction; 384 } 385 386 /** 387 Get the source item type. This is not part of the expression, but 388 is taken from the current list. 389 */ 390 public Item getSourceType() 391 { 392 return sourceType; 393 } 394 395 /** 396 Get the target ITEMTYPE from the expression. 397 */ 398 public Item getTargetType() 399 { 400 return targetType; 401 } 402 403 /** 404 Get the target subtype from the expression. 405 */ 406 public String getSubtype() 407 { 408 return subtype; 409 } 410 411 /** 412 Get the name of the targeted subtype. This may not always 413 be available. 414 */ 415 public String getSubtypeName() 416 { 417 return subtypeName; 418 } 419 420 /** 421 Get the property from the expression. 422 */ 423 public String getProperty() 424 { 425 return property; 426 } 427 428 /** 429 Get the annotation id from the expression. Only available if the 430 property represents an annotation (it starts with '#'). 431 */ 432 public int getAnnotationId() 433 { 434 return annotationId; 435 } 436 437 /** 438 Get the extension id from the expression. Only available if the 439 property represents an extension (it starts with '!'). 440 */ 441 public String getExtensionId() 442 { 443 return extensionId; 444 } 445 446 /** 447 Get the action id from the expression. Only available if the 448 property represents an extension (it starts with '!'). 449 */ 450 public String getActionId() 451 { 452 return actionId; 453 } 454 455 /** 456 If the property starts with a '/' it is a multi-hop specification. 457 A multi-hop specification must be from child->parent->child and must 458 not have more than two hops. 459 */ 460 public boolean isMultiHop() 461 { 462 return multiHop; 463 } 464 465 /** 466 Generates a title using target item type and subtype as prefix. 467 */ 468 public String generateTitle(String title) 469 { 470 String result = "<span class=\""; 471 result += direction == SourceItemTransform.CHILD_TO_PARENT ? "parentitem" : "childitem"; 472 if (multiHop) result += " multihop"; 473 result += "\">"; 474 result += StringUtil.coalesce(subtypeName, targetType.name()); 475 result += "</span>"; 476 result += withPrefix(".", title); 477 return result; 478 } 479 480 /** 481 Generates a tooltip using target item type and subtype as prefix. 482 */ 483 public String generateTooltip(String tooltip) 484 { 485 return (direction == SourceItemTransform.CHILD_TO_PARENT ? "Parent › " : "Child › ") + 486 StringUtil.coalesce(subtypeName, targetType.name()) + withPrefix(" › ", tooltip); 487 } 488 489 /** 490 Return prefix+string if string is not null or the empty string otherwise. 491 */ 492 private String withPrefix(String prefix, String s) 493 { 494 return s == null ? "" : prefix+s; 495 } 496 497 public ItemSubtype getItemSubtype(DbControl dc) 498 { 499 ItemSubtype st = ItemSubtype.getById(dc, Values.getInt(subtype)); 500 subtypeName = st.getName(); 501 return st; 502 } 503 504 public RawDataType getRawDataType() 505 { 506 RawDataType rdt = RawDataTypes.getSafeRawDataType(subtype); 507 subtypeName = rdt.getName(); 508 return rdt; 509 } 510 511 public Restriction createTargetTypeRestriction(DbControl dc) 512 { 513 Restriction r = null; 514 if (targetType == Item.RAWBIOASSAY) 515 { 516 RawDataType rdt = getRawDataType(); 517 r = Restrictions.eq(Hql.property("rawDataType"), Expressions.string(rdt.getId())); 518 } 519 else 520 { 521 ItemSubtype subtype = getItemSubtype(dc); 522 r = Restrictions.eq(Hql.property("itemSubtype"), Hql.entity(subtype)); 523 } 524 return r; 525 } 526 527 } 528 303 529 } -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/RelatedItemColumnActionFactory.java
r7874 r8083 5 5 6 6 import net.sf.basedb.clients.web.extensions.AbstractJspActionFactory; 7 import net.sf.basedb.clients.web.extensions.ExtensionsControl; 7 8 import net.sf.basedb.clients.web.extensions.JspContext; 9 import net.sf.basedb.clients.web.extensions.list.RelatedItemColumn.Specification; 8 10 import net.sf.basedb.core.Annotatable; 9 11 import net.sf.basedb.core.DbControl; … … 12 14 import net.sf.basedb.core.Metadata.PropertyPath; 13 15 import net.sf.basedb.core.SessionControl; 16 import net.sf.basedb.core.SyncFilter.SourceItemTransform; 14 17 import net.sf.basedb.core.plugin.GuiContext; 15 18 import net.sf.basedb.util.Values; 19 import net.sf.basedb.util.extensions.ExtensionsFilter; 20 import net.sf.basedb.util.extensions.ExtensionsInvoker; 16 21 import net.sf.basedb.util.extensions.InvokationContext; 22 import net.sf.basedb.util.extensions.SingleExtensionFilter; 17 23 18 24 /** … … 49 55 String subContext = Values.getString(guiContext.getSubContext(), ""); 50 56 51 ItemContext cc = sc.getCurrentContext(item, subContext, null); 52 if (cc == null) return false; 53 54 String allColumns = cc.getSetting("columns"); 57 String allColumns = null; 58 SourceItemTransform requiredDirection = null; 59 if (RelatedItemExtensionColumn.SUBCONTEXT.equals(subContext)) 60 { 61 // The current item should be a Specification instance that may specify 62 // the column. Otherwise we load the columns from the regular list context 63 // which makes it possible to select linked item columns from a parent/child 64 // that are currently available 65 Specification spec = jspContext.getCurrentItem(); 66 if (spec != null) 67 { 68 allColumns = spec.actionId; 69 if (spec.direction == SourceItemTransform.CHILD_TO_PARENT) 70 { 71 // We may only select CHILD columns from a PARENT 72 requiredDirection = SourceItemTransform.PARENT_TO_CHILD; 73 subContext = ""; 74 } 75 } 76 } 77 if (allColumns == null) 78 { 79 ItemContext cc = sc.getCurrentContext(item, subContext, null); 80 if (cc == null) return false; 81 allColumns = cc.getSetting("columns"); 82 } 55 83 if (allColumns == null || !allColumns.contains("/")) return false; 56 84 57 jspContext.addScript(jspContext.getRoot() + "/include/scripts/lazy-items.js"); 85 boolean lazy = !Boolean.FALSE.equals(jspContext.getAttribute("lazy-loading")); 86 if (lazy) 87 { 88 jspContext.addScript(jspContext.getRoot() + "/include/scripts/lazy-items.js"); 89 } 90 91 for (String col : allColumns.split(",")) 92 { 93 if (col.startsWith("/") && col.contains("!")) 94 { 95 Specification spec = Specification.parse(item, col); 96 if (requiredDirection != null && requiredDirection != spec.direction) continue; 97 if (spec.property.startsWith("/")) 98 { 99 // We support multi-hop in max two steps 100 spec = Specification.parse(spec.targetType, spec.property); 101 } 102 103 // Create a child context for invoking the specified extensions as if we are on the relevant list page 104 GuiContext childGuiContext = GuiContext.list(spec.targetType, RelatedItemExtensionColumn.SUBCONTEXT); 105 JspContext childJspContext = ExtensionsControl.createContext(jspContext.getDbControl(), jspContext.getPageContext(), childGuiContext, spec); 106 childJspContext.linkAttributes(jspContext); 107 ExtensionsFilter filter = spec.extensionId == null ? null : new SingleExtensionFilter(spec.extensionId); 108 ExtensionsInvoker<ListColumnAction<Annotatable, ?>> childInvoker = ListColumnUtil.useExtensions(childJspContext, filter); 109 110 if (childInvoker.getNumExtensions() > 0) 111 { 112 // Copy scripts and stylesheets to the main JSP context 113 jspContext.addScripts(childJspContext.getScripts()); 114 jspContext.addStylesheets(childJspContext.getStylesheets()); 115 116 // Save the invoker in the main JSP context so that we can use the same 117 // instance in getActions()->RelatedItemColumn.create() 118 jspContext.setAttribute("RelatedItemInvoker."+spec.expression, childInvoker); 119 } 120 } 121 } 122 58 123 return super.prepareContext(context); 59 124 } … … 69 134 Item item = guiContext.getItem(); 70 135 String subContext = Values.getString(guiContext.getSubContext(), ""); 71 136 137 String allColumns = null; 138 SourceItemTransform requiredDirection = null; 139 if (RelatedItemExtensionColumn.SUBCONTEXT.equals(subContext)) 140 { 141 // The current item should be a Specification instance that may specify 142 // the column. Otherwise we load the columns from the regular list context 143 Specification spec = jspContext.getCurrentItem(); 144 if (spec != null) 145 { 146 allColumns = spec.actionId; 147 if (spec.direction == SourceItemTransform.CHILD_TO_PARENT) 148 { 149 // We may only select CHILD columns from a PARENT 150 requiredDirection = SourceItemTransform.PARENT_TO_CHILD; 151 subContext = ""; 152 } 153 } 154 } 72 155 ItemContext cc = sc.getCurrentContext(item, subContext, null); 73 if (cc == null) return null; 74 75 String allColumns = cc.getSetting("columns"); 156 if (allColumns == null) 157 { 158 if (cc == null) return null; 159 allColumns = cc.getSetting("columns"); 160 } 76 161 if (allColumns == null || !allColumns.contains("/")) return null; 77 78 162 79 163 boolean lazy = !Boolean.FALSE.equals(jspContext.getAttribute("lazy-loading")); 80 164 DbControl dc = jspContext.getDbControl(); 81 RelatedItemHelper helper = new RelatedItemHelper(sc, lazy);165 RelatedItemHelper helper = new RelatedItemHelper(sc, jspContext, lazy); 82 166 List<ListColumnAction<Annotatable, Object>> actions = new ArrayList<>(); 83 167 for (String col : allColumns.split(",")) … … 85 169 if (col.startsWith("/")) 86 170 { 87 ListColumnAction<Annotatable, Object> action = RelatedItemColumn.create(dc, actions.size(), item, col, helper); 171 Specification spec = Specification.parse(item, col); 172 if (requiredDirection != null && requiredDirection != spec.direction) continue; 173 ListColumnAction<Annotatable, Object> action = RelatedItemColumn.create(dc, actions.size(), spec, helper); 88 174 if (action != null) actions.add(action); 89 175 } … … 91 177 92 178 if (actions.size() == 0) return null; 93 179 // If we are lazy-loading child/parent items we need to store context attributes 180 // so that the lazy-loading feature can pick them up again 181 if (lazy) jspContext.storeAttributes(cc, "lazy-related-items-attributes"); 94 182 return actions.toArray(new ListColumnAction[actions.size()]); 95 183 } -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/RelatedItemHelper.java
r7962 r8083 4 4 import java.util.Set; 5 5 6 import net.sf.basedb.clients.web.extensions.JspContext; 6 7 import net.sf.basedb.core.Annotatable; 7 8 import net.sf.basedb.core.DbControl; … … 24 25 25 26 final SessionControl sc; 27 final JspContext jspContext; 26 28 final boolean lazy; 27 29 // Use the snapshot manager for efficient loading of annotations … … 32 34 TransformContext transformContext; 33 35 34 RelatedItemHelper(SessionControl sc, boolean lazy)36 RelatedItemHelper(SessionControl sc, JspContext jspContext, boolean lazy) 35 37 { 36 38 this.sc = sc; 39 this.jspContext = jspContext; 37 40 this.lazy = lazy; 38 41 this.manager = new SnapshotManager(); -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/RelatedItemListColumn.java
r7893 r8083 7 7 8 8 import net.sf.basedb.clients.web.Base; 9 import net.sf.basedb.clients.web.formatter.LinkedItemFormatter; 9 10 import net.sf.basedb.core.Annotatable; 10 11 import net.sf.basedb.core.DbControl; 11 import net.sf.basedb.core.Item;12 12 import net.sf.basedb.core.ItemContext; 13 13 import net.sf.basedb.core.ItemList; 14 14 import net.sf.basedb.core.ItemQuery; 15 import net.sf.basedb.core.SyncFilter.SourceItemTransform;16 15 import net.sf.basedb.core.Type; 17 16 import net.sf.basedb.core.query.Expressions; 18 17 import net.sf.basedb.core.query.Hql; 19 import net.sf.basedb.core.query.Restriction;20 18 import net.sf.basedb.core.query.Restrictions; 21 19 import net.sf.basedb.util.NameableComparator; 22 20 import net.sf.basedb.util.filter.Filter; 21 import net.sf.basedb.util.formatter.CollectionFormatter; 22 import net.sf.basedb.util.formatter.NameableFormatter; 23 import net.sf.basedb.util.listable.SourceItemTransformer; 23 24 24 25 /** … … 35 36 private final ItemQuery<ItemList> listQuery; 36 37 37 RelatedItemListColumn(DbControl dc, int index, String id, Item sourceType, Item targetType, SourceItemTransform direction, Restriction targetRestriction, Filter<? super ItemList> itemListFilter, RelatedItemHelper helper) 38 @SuppressWarnings({ "unchecked", "rawtypes" }) 39 RelatedItemListColumn(DbControl dc, int index, Specification spec, Filter<? super ItemList> itemListFilter, RelatedItemHelper helper) 38 40 { 39 super(index, id, sourceType, targetType, direction, targetRestriction, helper); 40 ItemContext cc = dc.getSessionControl().getCurrentContext(sourceType); 41 setEnumeration(Base.getItemListsEnum(dc, targetType, cc.getInclude(), itemListFilter)); 41 super(dc, index, spec, helper); 42 ItemContext cc = dc.getSessionControl().getCurrentContext(spec.sourceType); 43 setEnumeration(Base.getItemListsEnum(dc, spec.targetType, cc.getInclude(), itemListFilter)); 44 setTitle(spec.generateTitle("Item list")); 45 setTooltip(spec.generateTooltip("Item list")); 46 setValueType(Type.INT); 47 setExportFormatter(new NameableFormatter()); 48 if (!helper.lazy) setFormatter(new CollectionFormatter(new LinkedItemFormatter())); 42 49 43 50 listQuery = ItemList.getQuery(); 44 51 listQuery.setIncludes(cc.getInclude()); 45 52 listQuery.join(Hql.innerJoin("members", "m")); 46 listQuery.restrict(Restrictions.eq(Hql.property("memberType"), Expressions.integer( targetType.getValue())));53 listQuery.restrict(Restrictions.eq(Hql.property("memberType"), Expressions.integer(spec.targetType.getValue()))); 47 54 listQuery.restrict(Restrictions.in(Hql.alias("m"), Expressions.parameter("itemId", Type.INT))); 48 55 } 49 56 50 57 @Override 51 public Object getValue(RelatedItemHelper helper, Annotatable item )58 public Object getValue(RelatedItemHelper helper, Annotatable item, SourceItemTransformer preTransform) 52 59 { 53 Set<Annotatable> items = getRelatedItems(item );60 Set<Annotatable> items = getRelatedItems(item, preTransform); 54 61 if (items.size() == 0) return null; 55 62 -
trunk/src/clients/web/net/sf/basedb/clients/web/extensions/list/RelatedItemPropertyColumn.java
r7893 r8083 2 2 3 3 import java.util.ArrayList; 4 import java.util.HashMap; 4 5 import java.util.List; 6 import java.util.Map; 5 7 import java.util.Set; 6 8 9 import net.sf.basedb.clients.web.Base; 10 import net.sf.basedb.clients.web.formatter.FormatterFactory; 11 import net.sf.basedb.clients.web.formatter.LinkedItemFormatter; 7 12 import net.sf.basedb.core.Annotatable; 13 import net.sf.basedb.core.DbControl; 8 14 import net.sf.basedb.core.Item; 15 import net.sf.basedb.core.ItemQuery; 16 import net.sf.basedb.core.ItemSubtype; 9 17 import net.sf.basedb.core.Metadata; 18 import net.sf.basedb.core.Type; 10 19 import net.sf.basedb.core.Metadata.PropertyPath; 11 import net.sf.basedb.core.SyncFilter.SourceItemTransform; 12 import net.sf.basedb.core.query.Restriction; 20 import net.sf.basedb.util.Enumeration; 21 import net.sf.basedb.util.formatter.CollectionFormatter; 22 import net.sf.basedb.util.formatter.NameableFormatter; 23 import net.sf.basedb.util.formatter.ToStringFormatter; 24 import net.sf.basedb.util.listable.SourceItemTransformer; 13 25 14 26 /** … … 23 35 { 24 36 37 38 private static Map<String, String> pathTitles = new HashMap<>(); 39 static 40 { 41 // See also add_relateditem_columns.js 42 pathTitles.put("externalId", "External ID"); 43 pathTitles.put("bioWell.bioPlate", "Bioplate"); 44 pathTitles.put("entryDate", "Registered"); 45 pathTitles.put("remainingQuantity", "Remaining quantity"); 46 pathTitles.put("originalQuantity", "Original quantity"); 47 pathTitles.put("creationEvent.entryDate", "Registration date"); 48 pathTitles.put("creationEvent.eventDate", "Creation date"); 49 pathTitles.put("creationEvent.protocol", "Protocol"); 50 pathTitles.put("creationEvent.hardware", "Hardware"); 51 pathTitles.put("protocol", "Protocol"); 52 pathTitles.put("hardware", "Hardware"); 53 pathTitles.put("software", "Software"); 54 pathTitles.put("tag", "Tag"); 55 pathTitles.put("job.itemSubtype", "Job type"); 56 pathTitles.put("$itemLists", "Item lists"); 57 } 58 59 /** 60 Convert some paths to a more user-friendly title instead. 61 */ 62 private static String pathToTitle(String path) 63 { 64 String title = pathTitles.get(path); 65 return title == null ? path.substring(0, 1).toUpperCase() + path.substring(1) : title; 66 } 67 25 68 private final PropertyPath<? super Annotatable, ?> property; 26 69 27 RelatedItemPropertyColumn(int index, String id, Item sourceType, Item targetType, SourceItemTransform direction, Restriction targetRestriction, RelatedItemHelper helper, PropertyPath<? super Annotatable, ?> property) 70 @SuppressWarnings({ "rawtypes", "unchecked" }) 71 RelatedItemPropertyColumn(DbControl dc, int index, Specification spec, RelatedItemHelper helper, PropertyPath<? super Annotatable, ?> property) 28 72 { 29 super( index, id, sourceType, targetType, direction, targetRestriction, helper);73 super(dc, index, spec, helper); 30 74 this.property = property; 75 if (property == null) 76 { 77 setTitle(spec.generateTitle(null)); 78 setTooltip(spec.generateTooltip(null)); 79 } 80 else 81 { 82 setTitle(spec.generateTitle(pathToTitle(spec.property))); 83 setTooltip(spec.generateTooltip(spec.property)); 84 } 85 // If the property ends with an item... 86 if (property == null || property.getHibernateType().isEntityType()) 87 { 88 setExportFormatter(new NameableFormatter()); 89 if (!helper.lazy) setFormatter(new CollectionFormatter(new LinkedItemFormatter())); 90 // Specical case for Job subtypes which we set as an enumeration of existing subtypes 91 if ("job.itemSubtype".equals(spec.property)) 92 { 93 ItemQuery<ItemSubtype> jobSubtypesQuery = Base.getSubtypesQuery(Item.JOB); 94 setEnumeration(Enumeration.fromItems(jobSubtypesQuery.list(dc), null)); 95 setValueType(Type.INT); 96 } 97 else 98 { 99 // Filtering is made on the "name" 100 if (getFilterProperty()==null) 101 { 102 // Add '.name' to make filtering work 103 String nameProperty = getProperty() + ".name"; 104 if (nameProperty.contains("/..")) nameProperty = nameProperty.replace("/..", "/"); 105 setFilterProperty(nameProperty); 106 } 107 setValueType(Type.STRING); 108 } 109 } 110 else 111 { 112 Type colType = Type.fromHibernateType(property.getHibernateType()); 113 if (colType != null) 114 { 115 setValueType(colType); 116 setExportFormatter(FormatterFactory.getTypeFormatter(dc.getSessionControl(), colType)); 117 } 118 else 119 { 120 // Last fallback -- if we get here the Type.fromHibernateType() should be update with special cases 121 setValueType(Type.STRING); 122 setExportFormatter(new ToStringFormatter<>()); 123 } 124 if (!helper.lazy) setFormatter(new CollectionFormatter(getExportFormatter())); 125 } 126 31 127 } 32 128 33 129 @Override 34 public Object getValue(RelatedItemHelper helper, Annotatable item )130 public Object getValue(RelatedItemHelper helper, Annotatable item, SourceItemTransformer preTransform) 35 131 { 36 Set<Annotatable> items = getRelatedItems(item );132 Set<Annotatable> items = getRelatedItems(item, preTransform); 37 133 if (items.size() == 0) return null; 38 134 -
trunk/src/clients/web/net/sf/basedb/clients/web/taglib/table/ColumnDef.java
r7943 r8083 618 618 JSONObject json = new JSONObject(); 619 619 json.put("id", getId()); 620 json.put("title", getTitle()); 620 json.put("title", HTML.stripMarkup(getTitle())); 621 json.put("titleHTML", getTitle()); 621 622 json.put("subtitle", getSubtitle()); 622 623 json.put("property", getProperty()); … … 696 697 { 697 698 context = table.getSc().getCurrentContext(table.getItem(), table.getSubcontext()); 698 // Check if hidden columns have a filter699 if ( !isVisible &&context != null)699 String filterProp = getFilterproperty(); 700 if (context != null) 700 701 { 701 if (isPermission) 702 // If the filter property is not the same as the ID, we should keep 703 // a pointer between them so we can match in Base.getAndSetCurrentContext() 704 // when testing if a filter is present on a hidden column 705 if (filterProp != null && !filterProp.equals(getId())) 702 706 { 703 hasFilter = context.getItemPermission() != null;707 context.setObject("id2filter."+getId(), filterProp); 704 708 } 705 else if (getFilterproperty() != null) 709 710 // Check if hidden columns have a filter 711 if (!isVisible) 706 712 { 707 hasFilter = context.hasPropertyFilter(getFilterproperty()); 713 if (isPermission) 714 { 715 hasFilter = context.getItemPermission() != null; 716 } 717 else if (filterProp != null) 718 { 719 hasFilter = context.hasPropertyFilter(filterProp); 720 } 708 721 } 709 722 } … … 731 744 if (!isSortable) 732 745 { 733 String theTooltip = getTooltip() != null ? getTooltip() : getTitle();746 String theTooltip = getTooltip() != null ? getTooltip() : HTML.stripMarkup(getTitle()); 734 747 sb.append("<span title=\"").append(theTooltip).append("\">"); 735 748 sb.append(getTitle()); … … 747 760 748 761 String theTooltip = getTooltip() != null ? 749 getTooltip() : "Sort by "+ getTitle() + "; hold CTRL, ALT or SHIFT to sort by multiple columns";762 getTooltip() : "Sort by "+HTML.stripMarkup(getTitle()) + "; hold CTRL, ALT or SHIFT to sort by multiple columns"; 750 763 sb.append("<span class=\"link table-sort\""); 751 764 sb.append(" id=\"").append(idPrefix).append(".sort\""); … … 922 935 if (isFilterable && hasFilter) 923 936 { 924 table.addFilteredColumn(get Id(), isVisible);937 table.addFilteredColumn(getFilterproperty(), isVisible); 925 938 } 926 939 } -
trunk/src/clients/web/net/sf/basedb/clients/web/taglib/table/PresetSelector.java
r7407 r8083 40 40 import net.sf.basedb.clients.web.util.HTML; 41 41 import net.sf.basedb.util.Values; 42 43 import java.util.List; 42 import net.sf.basedb.util.formatter.Formatter; 43 44 import java.util.Set; 45 44 46 import javax.servlet.jsp.JspException; 45 47 import javax.servlet.jsp.JspTagException; … … 305 307 { 306 308 sb.append(""); 307 List<String> hiddenFilteredColumns = table.getHiddenFilteredColumns();309 Set<String> hiddenFilteredColumns = table.getHiddenFilteredColumns(); 308 310 int numHidden = hiddenFilteredColumns == null ? 0 : hiddenFilteredColumns.size(); 309 311 if (numHidden > 0) 310 312 { 311 String columns = Values.getString(hiddenFilteredColumns, ",", true); 313 String columns = Values.getString(hiddenFilteredColumns, ",", true, 314 new Formatter<>() 315 { 316 @Override 317 public String format(String value) 318 { 319 if (value.startsWith("/")) 320 { 321 if (value.endsWith("/name") || value.endsWith(".name")) 322 { 323 value = value.replace("/name", "/."); 324 } 325 else if (value.endsWith(".name")) 326 { 327 value = value.replace(".name", ""); 328 } 329 else if (value.contains("/!x.")) 330 { 331 // This is an extension filter convert to /!# 332 value = value.replace("/!x.", "/!#"); 333 } 334 } 335 else if (value.startsWith("!x.")) 336 { 337 value = value.substring(3); 338 } 339 return value; 340 } 341 342 @Override 343 public String parseString(String value) 344 { 345 return null; 346 } 347 }); 312 348 sb.append("<span class=\"link\" id=\"").append(table.getId()).append(".showcolumns\""); 313 349 sb.append(" data-hidden-columns=\"").append(columns).append("\""); -
trunk/src/clients/web/net/sf/basedb/clients/web/taglib/table/Table.java
r7982 r8083 39 39 import java.util.LinkedHashSet; 40 40 import java.util.List; 41 import java.util.LinkedList;42 41 import java.util.Arrays; 42 import java.util.Collection; 43 43 import java.util.Set; 44 44 … … 326 326 private Map<String, String> columnSubtitle; 327 327 private transient Map<String, Formatter<?>> columnFormatter; 328 private List<String> hiddenFilteredColumns;328 private Set<String> hiddenFilteredColumns; 329 329 330 330 @SuppressWarnings("rawtypes") … … 568 568 { 569 569 numFilteredColumns++; 570 if ( !visible) hiddenFilteredColumns.add(columnId);571 } 572 List<String> getHiddenFilteredColumns()570 if (visible) hiddenFilteredColumns.remove(columnId); 571 } 572 Set<String> getHiddenFilteredColumns() 573 573 { 574 574 return hiddenFilteredColumns; … … 720 720 columnFilter = new HashMap<String, String>(); 721 721 columnFormatter = new HashMap<String, Formatter<?>>(); 722 hiddenFilteredColumns = new Linked List<String>();722 hiddenFilteredColumns = new LinkedHashSet<String>(); 723 723 jsonColumnDefs = new JSONArray(); 724 724 if ((columns == null) || columns.equals("all")) … … 751 751 { 752 752 addHidden("exclude", (String)exclude); 753 } 754 755 // Until we know more, assume that all filtered columns are hidden... 756 Collection<net.sf.basedb.core.PropertyFilter> filters = cc.getPropertyFilters(); 757 if (filters != null) 758 { 759 for (net.sf.basedb.core.PropertyFilter pf : filters) 760 { 761 hiddenFilteredColumns.add(pf.getProperty()); 762 } 753 763 } 754 764 } -
trunk/src/core/common-queries.xml
r7997 r8083 2752 2752 <query id="DBLOG_GET_ANNOTATIONTYPE_INFO" type="HQL"> 2753 2753 <sql> 2754 SELECT at.name, at.valueType, at.disableLogOfValues 2754 SELECT at.name, at.valueType, at.disableLogOfValues, at.projectAnnotations 2755 2755 FROM AnnotationTypeData at 2756 2756 WHERE at.id = :annotationTypeId 2757 2757 </sql> 2758 2758 <description> 2759 An HQL query that loads the 'name', 'valueType' and 'disableLogOfValues' flag2760 for an annotation type given the ID.2759 An HQL query that loads the 'name', 'valueType', 'disableLogOfValues' and 2760 the 'projectAnnotations' flag for an annotation type given the ID. 2761 2761 </description> 2762 2762 </query> -
trunk/src/core/net/sf/basedb/core/Annotation.java
r7643 r8083 147 147 Annotation a = dc.loadItem(Annotation.class, id); 148 148 if (a == null) throw new ItemNotFoundException("Annotation[id="+id+"]"); 149 return a; 150 } 151 152 /** 153 Get an <code>Annotation</code> item when you know the id 154 and ignore the rules for project-specific annotations when 155 modifying the values. 156 @see #setValuesIfDifferent(List, Unit) 157 @since 3.19.4 158 */ 159 public static Annotation getById(DbControl dc, int id, boolean ignoreActiveProject) 160 throws ItemNotFoundException, PermissionDeniedException, BaseException 161 { 162 Annotation a = getById(dc, id); 163 a.ignoreActiveProject = ignoreActiveProject; 149 164 return a; 150 165 } -
trunk/src/core/net/sf/basedb/core/AnnotationBatcher.java
r7522 r8083 1068 1068 1069 1069 /** 1070 Get the id of the project this annotation belong to. 1071 Only relevant if the annotation type is using project-specific 1072 annotations. 1073 @since 3.19.4 1074 */ 1075 public int getProjectId() 1076 { 1077 return projectId; 1078 } 1079 1080 /** 1070 1081 Get the ID of the unit this annotation is using. 1071 1082 */ -
trunk/src/core/net/sf/basedb/core/ItemContext.java
r7982 r8083 172 172 private Set<Integer> selected; 173 173 private boolean noPaging = false; 174 private int rowsPerPage ;174 private int rowsPerPage = 30; 175 175 private int page; 176 176 private int numFilterRows = 1; 177 177 private String sortProperty = null; 178 178 private String message; 179 private Throwable exception; 179 180 private SortDirection sortDirection = SortDirection.ASC; 180 181 private Set<Include> include; … … 255 256 this.noPaging = true; 256 257 copyFromSyncFilter(syncFilter); 258 } 259 260 /** 261 Initialize the context with the special purpose of configuring 262 a query with the given filters. It should not be used for 263 representing context in a GUI. 264 @since 3.19.4 265 */ 266 ItemContext(Item itemType, PropertyFilter... filters) 267 { 268 this.itemType = itemType; 269 this.subContext = null; 270 this.name = null; 271 this.noPaging = true; 272 for (PropertyFilter filter : filters) 273 { 274 setPropertyFilter(filter); 275 } 257 276 } 258 277 … … 623 642 { 624 643 this.message = message; 644 } 645 646 /** 647 Get the exception that is the cause for the error in 648 {@link #configureQuery(DbControl, EntityQuery, boolean)}. 649 @since 3.19.14 650 */ 651 public Throwable getException() 652 { 653 return exception; 654 } 655 656 /** 657 Set the exception that is the cause for the error in 658 {@link #configureQuery(DbControl, EntityQuery, boolean)}. 659 @since 3.19.14 660 */ 661 public void setException(Throwable t) 662 { 663 this.exception = t; 625 664 } 626 665 … … 1752 1791 1753 1792 // Get default restriction 1754 if (r == null) r = filter.getRestriction(dc, query );1793 if (r == null) r = filter.getRestriction(dc, query, context); 1755 1794 } 1756 1795 catch (Throwable ex) … … 1759 1798 Application.getLogger().warn(msg, ex); 1760 1799 setMessage(msg + ": " + ex.getMessage()); 1800 setException(ex); 1761 1801 } 1762 1802 -
trunk/src/core/net/sf/basedb/core/PropertyFilter.java
r8013 r8083 50 50 import net.sf.basedb.core.query.Hql; 51 51 import net.sf.basedb.core.query.IdListRestriction; 52 import net.sf.basedb.util.extensions.ClientContext; 52 53 import net.sf.basedb.util.listable.ListableUtil; 53 54 import net.sf.basedb.util.listable.SourceItemTransformerFactory; … … 560 561 */ 561 562 public Restriction getRestriction(DbControl dc, EntityQuery query) 563 { 564 return getRestriction(dc, query, null); 565 } 566 567 /** 568 Create a restriction from the filter. There are a few special cases: 569 <p> 570 If the property starts with a $ it is interpreted as a <code>alias.property</code> 571 value, ignoring the root alias of the query. The alias is removed from the 572 property before proceeding. 573 <p> 574 If the property starts with a # or ## it is a filter on annotations. The rest of 575 the property is the ID of the annotation type, and we use a 576 {@link AnnotationSimpleRestriction} object for the restriction. If there 577 is only one # in the prefix only direct annotations are searched, if there 578 are two ##, inherited annotations are also searched. 579 <p> 580 If the property starts with a £, the rest of the property is the path that 581 joins the {@link ReporterData#getReporterListScores()} collection. The filter 582 value is the ID of the reporter list. 583 <p> 584 If the property starts with a @ the property is a collection of values and 585 we always use an IN restriction, ignoring the specified operator. 586 <p> 587 If the property starts with & it is a collection of entities. The property 588 must end with a property name from the entity in the collection within paranthesis. 589 Eg. &experiments(name). The filter is applied as a subquery. 590 <p> 591 If the property starts with ! it is another special filter: 592 <ul> 593 <li>Since BASE 2.15: !sharedTo.name, which is used to filter 594 shareable items by the name of users/groups/projects it has been shared to. 595 The 'name' part can be replaced with any other property that is common for 596 users, groups and projects (eg. description or id). 597 <li>Since BASE 3.18: !x., which is intended to be handled by extensions. It 598 is ignored by the BASE core code. The rest of the property should be a unique 599 identifier for the extension that is handling the filter. 600 </ul> 601 <p> 602 The property may also start with $@. 603 @return The restriction 604 @since 3.19.4 605 */ 606 public Restriction getRestriction(DbControl dc, EntityQuery query, ClientContext context) 562 607 { 563 608 … … 812 857 813 858 PropertyFilter pp = new PropertyFilter(linkProperty, operator, getValue(), getValueType()); 814 subquery.restrict(pp.getRestriction(dc, subquery ));859 subquery.restrict(pp.getRestriction(dc, subquery, context)); 815 860 restriction = AnyToAnyRestriction.exists(null, linkName, linkType, subquery); 816 861 } … … 851 896 } 852 897 PropertyFilter pp = new PropertyFilter(targetProperty, operator, getValue(), getValueType()); 853 subquery.restrict(pp.getRestriction(dc, subquery)); 898 ClientContext subContext = new ClientContext(dc); 899 if (context != null) subContext.linkAttributes(context); 900 ItemContext targetItemContext = new ItemContext(targetType, pp); 901 targetItemContext.configureQuery(dc, subquery, subContext, true); 902 if (context != null && targetItemContext.getException() != null) 903 { 904 // Copy error messages 905 ItemContext cc = context.getCurrentItem(); 906 cc.setMessage(targetItemContext.getMessage()); 907 cc.setException(targetItemContext.getException()); 908 } 854 909 855 910 // We then transform the list if ID:s to a list of child item ID:s using ItemList functionality … … 970 1025 PropertyFilter pp = new PropertyFilter("$" + thisAlias + "." + collectionFilter, 971 1026 actualOperator, actualValue, getValueType()); 972 subquery.restrict(pp.getRestriction(dc, query ));1027 subquery.restrict(pp.getRestriction(dc, query, context)); 973 1028 974 1029 // Create the main restriction … … 1075 1130 PropertyFilter pp = new PropertyFilter("$" + thisAlias + "." + collectionFilter, 1076 1131 actualOperator, actualValue, getValueType()); 1077 subquery.restrict(pp.getRestriction(dc, query ));1132 subquery.restrict(pp.getRestriction(dc, query, context)); 1078 1133 1079 1134 // Create the main restriction … … 1123 1178 { 1124 1179 users = new PropertyFilter("$usr." + filterProperty, 1125 filterOp, filterValue, valueType).getRestriction(dc, query );1180 filterOp, filterValue, valueType).getRestriction(dc, query, context); 1126 1181 groups = new PropertyFilter("$grp." + filterProperty, 1127 filterOp, filterValue, valueType).getRestriction(dc, query );1182 filterOp, filterValue, valueType).getRestriction(dc, query, context); 1128 1183 projects = new PropertyFilter("$prj." + filterProperty, 1129 filterOp, filterValue, valueType).getRestriction(dc, query );1184 filterOp, filterValue, valueType).getRestriction(dc, query, context); 1130 1185 } 1131 1186 restriction = Hql.sharedTo(sharedTo, users, groups, projects); -
trunk/src/core/net/sf/basedb/core/SyncFilter.java
r7883 r8083 28 28 import java.util.Collections; 29 29 import java.util.Comparator; 30 import java.util.HashSet; 30 31 import java.util.List; 31 32 import java.util.Map; 33 import java.util.Set; 32 34 import java.util.SortedSet; 33 35 import java.util.TreeSet; … … 305 307 } 306 308 context.copyFromSyncFilter(getData()); 309 310 // Make sure that all filtered columns are visible 311 String columns = StringUtil.coalesce(context.getSetting("columns"), ""); 312 Set<String> visible = new HashSet<>(Arrays.asList(columns.split(","))); 313 for (String filterProperty : getData().getPropertyFilters().keySet()) 314 { 315 if (visible.contains(filterProperty)) continue; 316 317 // Try some other variants when the filter is on a related-item column 318 if (filterProperty.startsWith("/")) 319 { 320 if (filterProperty.endsWith("/name")) 321 { 322 // replace "name" with "." to use the entity instead 323 filterProperty = filterProperty.replace("/name", "/."); 324 } 325 else if (filterProperty.endsWith(".name")) 326 { 327 // replace ".name" with "" to use the entity instead 328 filterProperty = filterProperty.replace(".name", ""); 329 } 330 else if (filterProperty.contains("/!x.")) 331 { 332 // This is an extension filter convert to /!# 333 filterProperty = filterProperty.replace("/!x.", "/!#"); 334 } 335 } 336 else if (filterProperty.startsWith("!x.")) 337 { 338 filterProperty = filterProperty.substring(3); 339 } 340 if (!visible.contains(filterProperty)) 341 { 342 columns+=","+filterProperty; 343 } 344 } 345 context.setSetting("columns", columns); 307 346 } 308 347 -
trunk/src/core/net/sf/basedb/core/log/TransactionDetails.java
r7950 r8083 23 23 24 24 import java.util.Date; 25 import java.util.HashMap; 26 import java.util.Map; 25 27 26 28 import org.hibernate.query.Query; … … 52 54 private String pluginName; 53 55 private String jobName; 56 private Map<Integer, String> projectNames; 54 57 55 58 /** … … 179 182 180 183 /** 184 Get the name of the project with the given id. 185 Special cases: 0 = default 186 */ 187 public String getProjectName(int projectId) 188 { 189 String name = null; 190 if (projectId > 0) 191 { 192 if (projectNames == null) 193 { 194 projectNames = new HashMap<>(); 195 } 196 else 197 { 198 name = projectNames.get(projectId); 199 } 200 if (name == null) 201 { 202 Query<String> query = logControl.createHqlQuery("select prj.name from ProjectData prj where prj.id = " + projectId, String.class); 203 name = query.uniqueResult(); 204 projectNames.put(projectId, name); 205 } 206 } 207 else 208 { 209 name = "default"; 210 } 211 return name; 212 } 213 214 /** 181 215 Utility method for loading the name of the currently executing plugin. 182 216 The first call to this method will lookup the name in the database -
trunk/src/core/net/sf/basedb/core/log/db/AnnotationLogger.java
r7381 r8083 83 83 Integer parentId = null; 84 84 Integer parentType = null; 85 Integer projectId = null; 85 86 Annotation.Source source = null; 86 87 List<?> newValues = null; … … 105 106 newValues = info.getNewValues(); 106 107 } 108 if (at.getProjectAnnotations()) projectId = info.getProjectId(); 107 109 } 108 110 else if (entity instanceof AnnotationData) … … 120 122 valueType = Type.fromValue((Integer)tmp[1]); 121 123 if ((Boolean)tmp[2]) logOldPropertyValues = false; 124 if ((Boolean)tmp[3]) projectId = annotation.getProjectId(); 122 125 } 123 126 else … … 125 128 annotationType = at.getName(); 126 129 valueType = Type.fromValue(at.getValueType()); 130 if (at.getProjectAnnotations()) projectId = annotation.getProjectId(); 127 131 if (at.getDisableLogOfValues()) logOldPropertyValues = false; 128 132 } … … 190 194 change.setItemType(parentType); 191 195 196 String projectInfo = ""; 197 if (projectId != null) 198 { 199 String projectName = logControl.getTransactionDetails().getProjectName(projectId); 200 if (projectName != null) projectInfo = " ("+projectName+")"; 201 } 192 202 if (source == Annotation.Source.CLONED) 193 203 { 194 change.setChangeInfo("ClonedAnnotation["+annotationType+ "]");204 change.setChangeInfo("ClonedAnnotation["+annotationType+projectInfo+"]"); 195 205 } 196 206 else if (source == Annotation.Source.INHERITED) 197 207 { 198 change.setChangeInfo("InheritedAnnotation["+annotationType+ "]");208 change.setChangeInfo("InheritedAnnotation["+annotationType+projectInfo+"]"); 199 209 } 200 210 else 201 211 { 202 change.setChangeInfo("Annotation["+annotationType+ "]");212 change.setChangeInfo("Annotation["+annotationType+projectInfo+"]"); 203 213 } 204 214 if (logOldPropertyValues) -
trunk/src/core/net/sf/basedb/core/query/AnyToAnyRestriction.java
r7813 r8083 7 7 import net.sf.basedb.core.Item; 8 8 import net.sf.basedb.core.Type; 9 import net.sf.basedb.core.query.Restriction;10 9 import net.sf.basedb.util.EqualsHelper; 11 10 -
trunk/src/core/net/sf/basedb/core/query/ReporterListExpression.java
r7813 r8083 29 29 import net.sf.basedb.core.RealTable; 30 30 import net.sf.basedb.core.ReporterList; 31 import net.sf.basedb.core.query.Expression;32 31 import net.sf.basedb.util.EqualsHelper; 33 32 -
trunk/src/core/net/sf/basedb/core/snapshot/AnnotationSnapshot.java
r7703 r8083 35 35 import net.sf.basedb.core.DbControl; 36 36 import net.sf.basedb.core.Item; 37 import net.sf.basedb.core.ItemNotFoundException; 37 38 import net.sf.basedb.core.Permission; 39 import net.sf.basedb.core.PermissionDeniedException; 40 import net.sf.basedb.core.Project; 38 41 import net.sf.basedb.core.Type; 39 42 import net.sf.basedb.core.Unit; 40 43 import net.sf.basedb.core.data.AnnotationData; 44 import net.sf.basedb.util.EqualsHelper; 41 45 import net.sf.basedb.util.filter.Filter; 42 46 import net.sf.basedb.util.units.UnitConverter; … … 70 74 { 71 75 return new AnnotationTypeComparator(dc); 76 } 77 78 /** 79 Create a comparator for sorting annotation snapshots by the name 80 of the project. Default values can be sorted first or last. 81 @since 3.19.4 82 */ 83 public static Comparator<AnnotationSnapshot> sortByProject(DbControl dc, boolean defaultFirst) 84 { 85 return new ProjectComparator(dc, defaultFirst); 72 86 } 73 87 … … 308 322 309 323 /** 324 Get the project that a project-specific annotation belongs to. Returns 325 null if the annotation is a default value or if the project no longer 326 exists. 327 @throws PermissionDeniedException If the logged in user doesn't have access to the project 328 @since 3.19.4 329 */ 330 public Project getProject(DbControl dc) 331 { 332 try 333 { 334 return projectId == 0 ? null : Project.getById(dc, projectId); 335 } 336 catch (ItemNotFoundException ex) 337 {} // The project may have been deleted 338 return null; 339 } 340 341 /** 342 Get the name of project if the annotation is a project-specific value. 343 */ 344 String getProjectName(DbControl dc) 345 { 346 try 347 { 348 return projectId == 0 ? null : Project.getById(dc, projectId).getName(); 349 } 350 catch (RuntimeException ex) 351 {} // The project may have been deleted or the logged in user may not have permission 352 return null; 353 } 354 355 /** 310 356 Get the id of the default annotation that this project-specific 311 357 annotation is overriding. … … 587 633 } 588 634 } 635 636 /** 637 Comparator implementation for sorting snapshots 638 by name of project. If the project names are equal the snapshots are 639 sorted by annotation type. 640 */ 641 static class ProjectComparator 642 implements Comparator<AnnotationSnapshot> 643 { 644 private final DbControl dc; 645 private final boolean defaultFirst; 646 private final AnnotationTypeComparator atComp; 647 648 ProjectComparator(DbControl dc, boolean defaultFirst) 649 { 650 this.dc = dc; 651 this.defaultFirst = defaultFirst; 652 this.atComp = new AnnotationTypeComparator(dc); 653 } 654 655 @Override 656 public int compare(AnnotationSnapshot o1, AnnotationSnapshot o2) 657 { 658 String n1 = o1.getProjectName(dc); 659 String n2 = o2.getProjectName(dc); 660 if (EqualsHelper.equals(n1, n2)) 661 { 662 return atComp.compare(o1, o2); 663 } 664 else if (n1 == null) 665 { 666 return defaultFirst ? -1 : 1; 667 } 668 else if (n2 == null) 669 { 670 return defaultFirst ? 1 : -1; 671 } 672 return n1.compareTo(n2); 673 } 674 } 675 589 676 } -
trunk/src/core/net/sf/basedb/core/snapshot/SnapshotManager.java
r7767 r8083 129 129 130 130 private Integer projectId; 131 private boolean allProjects; 131 132 132 133 /** … … 140 141 141 142 Calling this method with a null parameter IS NOT the same 142 as calling {@link #setNoProject()} .143 as calling {@link #setNoProject()} or {@link #setAllProjects()}. 143 144 144 145 @param project The project or null to only search for default … … 151 152 { 152 153 this.projectId = project == null ? 0 : project.getId(); 154 this.allProjects = false; 153 155 } 154 156 … … 161 163 { 162 164 this.projectId = null; 163 } 165 this.allProjects = false; 166 } 167 168 /** 169 Search for project-specific annotations for all projects including the default 170 values. 171 172 @since 3.19.4 173 */ 174 public void setAllProjects() 175 { 176 this.projectId = null; 177 this.allProjects = true; 178 } 164 179 165 180 /** … … 243 258 { 244 259 int shotProjectId = shot.getProjectId(); 245 // Ignore all annotations not belonging to the specified project 246 if (projectId != null && shotProjectId != projectId) continue; 247 // Ignore project-specfic annotations not belonging to the current project 248 if (shotProjectId != 0 && shotProjectId != activeProjectId) continue; 249 250 // Ignore default annotations that already have a project-specific value 251 // NOTE! Depends on correct sort order in snapshots 252 if (overridden.contains(shot.getThisAnnotationId())) continue; 260 if (!allProjects) 261 { 262 // Ignore all annotations not belonging to the specified project 263 if (projectId != null && shotProjectId != projectId) continue; 264 // Ignore project-specfic annotations not belonging to the current project 265 if (shotProjectId != 0 && shotProjectId != activeProjectId) continue; 266 267 // Ignore default annotations that already have a project-specific value 268 // NOTE! Depends on correct sort order in snapshots 269 if (overridden.contains(shot.getThisAnnotationId())) continue; 270 } 253 271 254 272 if (shot.getSource() == Annotation.Source.PRIMARY) … … 262 280 result.add(shot); 263 281 // This is a project-specific annotation overriding a default value 264 if ( shotProjectId != 0) overridden.add(shot.getOverrideId());282 if (!allProjects && shotProjectId != 0) overridden.add(shot.getOverrideId()); 265 283 } 266 284 } -
trunk/src/core/net/sf/basedb/util/extensions/ClientContext.java
r8045 r8083 26 26 27 27 import net.sf.basedb.core.DbControl; 28 import net.sf.basedb.core.ItemContext; 28 29 import net.sf.basedb.core.SessionControl; 29 30 … … 232 233 } 233 234 235 /** 236 Link this context to the other context with respect to attributes. 237 Existing attributes in this context will be copied to the other context. 238 After this method call they will share the same internal storage so 239 that updates to one context is reflected in the other. 240 @since 3.19.4 241 */ 242 public void linkAttributes(ClientContext other) 243 { 244 if (other.attributes == null) other.attributes = new HashMap<>(); 245 if (attributes != null) other.attributes.putAll(attributes); 246 attributes = other.attributes; 247 } 248 249 /** 250 Store all attributes that has been set on this context to 251 the given ItemContext instance. Since the ItemContext is 252 persistant for session it is possible to load the same 253 attributes into another context later with {@link 254 #loadAttributes(ItemContext, String)} 255 @since 3.19.4 256 */ 257 public void storeAttributes(ItemContext cc, String key) 258 { 259 if (attributes != null && attributes.size() > 0) 260 { 261 cc.setObject(key, attributes); 262 } 263 } 264 265 /** 266 Load attributes to this context from the given ItemContext 267 instance. 268 @since 3.19.4 269 */ 270 public void loadAttributes(ItemContext cc, String key) 271 { 272 Map<String, Object> tmp = cc.getObject(key); 273 if (tmp != null && tmp.size() > 0) 274 { 275 if (attributes != null) 276 { 277 attributes.putAll(tmp); 278 } 279 else 280 { 281 attributes = tmp; 282 } 283 } 284 } 285 234 286 } -
trunk/src/core/net/sf/basedb/util/extensions/ExtensionsInvoker.java
r7275 r8083 58 58 { 59 59 60 private final ClientContext clientContext; 60 61 private final Collection<ExtensionContext<A>> contexts; 61 62 private boolean clearErrors; … … 64 65 Creates a new invoker object. 65 66 */ 66 ExtensionsInvoker(Collection<ExtensionContext<A>> contexts) 67 { 67 ExtensionsInvoker(ClientContext clientContext, Collection<ExtensionContext<A>> contexts) 68 { 69 this.clientContext = clientContext; 68 70 this.contexts = contexts; 71 } 72 73 /** 74 Get the client context that is used by this invoker. 75 @since 3.19.4 76 */ 77 public ClientContext getClientContext() 78 { 79 return clientContext; 69 80 } 70 81 -
trunk/src/core/net/sf/basedb/util/extensions/Registry.java
r8045 r8083 715 715 // Sort the extensions 716 716 filter.sort(contexts); 717 return new ExtensionsInvoker<A>(c ontexts);717 return new ExtensionsInvoker<A>(clientContext, contexts); 718 718 } 719 719 -
trunk/src/core/net/sf/basedb/util/formatter/PropertyFilterFormatter.java
r7889 r8083 87 87 StringBuilder sb = new StringBuilder(); 88 88 89 if (property.startsWith("/")) 89 int numHops = 0; 90 while (property.startsWith("/") && numHops < 2) 90 91 { 91 92 // Parent or child item filter 93 numHops++; 92 94 String[] tmp = property.split("/", 5); 93 95 int baseIndex = 0; … … 122 124 if (!property.startsWith("#")) sb.append("."); 123 125 } 124 elseif (property.startsWith("|"))126 if (property.startsWith("|")) 125 127 { 126 128 // Linked item filter … … 262 264 else 263 265 { 264 if (property.startsWith("&") || property.startsWith("@") || property.startsWith("!"))266 if (property.startsWith("&") || property.startsWith("@")) 265 267 { 266 268 property = property.substring(1); 269 } 270 else if (property.startsWith("!x.")) 271 { 272 property = property.substring(3); 267 273 } 268 274 sb.append(name(property)).append(" ").append(operator(operator.toString())).append(" "); -
trunk/src/core/net/sf/basedb/util/jep/LeftFunction.java
r6875 r8083 26 26 import org.nfunk.jep.ParseException; 27 27 28 import net.sf.basedb.util.jep.JepFunction;29 28 30 29 /** -
trunk/src/test/TestFile.java
r7982 r8083 79 79 int baseTracServer = TestFileServer.test_create("base.thep.lu.se", "BASE trac site", 80 80 "base", "base", "data/base.thep.lu.se.crt", null, null); 81 int base2DemoServer = TestFileServer.test_create("base.onk.lu.se", "BASE demo site", null, null,82 "data/base.onk.lu.se.crt", null, null);83 81 int extId1 = test_create("https://base.thep.lu.se/robots.txt", false, false); 84 int extId2 = test_create("https://base.onk.lu.se/onk/images/baselogo.png", false, false);85 82 int extId3 = test_create("https://base.thep.lu.se/login", false, false); 86 83 test_load(id1); … … 96 93 test_download(id1, "data/test.upload.txt"); 97 94 test_download(extId1, "data/test.download.robots.txt"); 98 test_download(extId2, "data/test.download.baselogo.png");99 95 test_download(extId3, null); 100 96 … … 106 102 test_delete(id2); 107 103 test_delete(extId1); 108 test_delete(extId2);109 104 test_delete(extId3); 110 105 TestFileServer.test_delete(baseTracServer); 111 TestFileServer.test_delete(base2DemoServer);112 106 113 107 write("++Testing file "+(ok ? "OK" : "Failed")+"\n"); -
trunk/src/test/TestFileServer.java
r7982 r8083 57 57 // Standard tests: create, load, list 58 58 int id = test_create("base.thep.lu.se", "BASE trac site", "base", "base", "data/base.thep.lu.se.crt", null, null); 59 int id2 = test_create("base.onk.lu.se", "BASE demo site", null, null, "data/base.onk.lu.se.crt", null, null);60 59 test_load(id); 61 60 test_list(-1); … … 64 63 // Standard test: Delete 65 64 test_delete(id); 66 test_delete(id2);67 65 write("++Testing file servers "+(ok ? "OK" : "Failed")+"\n"); 68 66 return ok; -
trunk/src/test/TestItemImporter.java
r7166 r8083 89 89 int childScanId = TestItemSubtype.test_create(Item.DERIVEDBIOASSAY, "ChildScan", SystemItems.getId(DerivedBioAssay.SCAN)); 90 90 int allSamplesSubtypeId = TestItemSubtype.test_create(Item.SAMPLE, "All samples"); 91 int base2DemoServer = TestFileServer.test_create("base.onk.lu.se", "BASE demo site", null, null,92 "data/base.onk.lu.se.crt", null, null);93 91 94 92 TestProject.test_defaultItem(projectId, Item.PROTOCOL, defaultSamplingProtocolId); … … 105 103 ok = ok & TestFile.test_list("/file-import", 1); // 'reporters.txt' 106 104 ok = ok & TestFile.test_list("/file-import/raw", 2); // 'file1.txt' and 'file2.txt' 107 ok = ok & TestFile.test_list("/file-import/ext", 2); // 'robots.txt' and 'baselogo.png'105 ok = ok & TestFile.test_list("/file-import/ext", 1); // 'robots.txt' 108 106 109 107 // Import annotation types … … 305 303 TestItemSubtype.test_delete(childScanId); 306 304 TestItemSubtype.test_delete(allSamplesSubtypeId); 307 TestFileServer.test_delete(base2DemoServer);308 305 TestProject.test_delete(projectId); 309 306 -
trunk/src/test/data/test.batchimport.files.txt
r6576 r8083 3 3 raw/file1.txt Raw data Raw data #1 4 4 raw/file2.txt Raw data Raw data #2 5 /file-import/ext/robots.txt http://base.thep.lu.se/robots.txt text/plain Robots file for BASE 6 /file-import/ext/baselogo.png https://base.onk.lu.se/onk/images/baselogo.png base.onk.lu.se 5 /file-import/ext/robots.txt https://base.thep.lu.se/robots.txt text/plain Robots file for BASE -
trunk/www/admin/hardware/list_hardware.jsp
r8026 r8083 514 514 list="<%=loader.getValues()%>" 515 515 suffix="<%=loader.getUnitSymbol()%>" 516 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"516 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 517 517 /><% 518 518 } -
trunk/www/admin/protocols/list_protocol.jsp
r8026 r8083 530 530 list="<%=loader.getValues()%>" 531 531 suffix="<%=loader.getUnitSymbol()%>" 532 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"532 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 533 533 /><% 534 534 } -
trunk/www/admin/software/list_software.jsp
r8026 r8083 514 514 list="<%=loader.getValues()%>" 515 515 suffix="<%=loader.getUnitSymbol()%>" 516 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"516 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 517 517 /><% 518 518 } -
trunk/www/biomaterials/bioplates/list_bioplates.jsp
r7982 r8083 668 668 list="<%=loader.getValues()%>" 669 669 suffix="<%=loader.getUnitSymbol()%>" 670 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"670 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 671 671 /><% 672 672 } -
trunk/www/biomaterials/bioplates/wells/list_biowells.jsp
r7982 r8083 650 650 list="<%=loader.getValues()%>" 651 651 suffix="<%=loader.getUnitSymbol()%>" 652 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"652 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 653 653 /><% 654 654 } -
trunk/www/biomaterials/biosources/list_biosources.jsp
r7982 r8083 581 581 list="<%=loader.getValues()%>" 582 582 suffix="<%=loader.getUnitSymbol()%>" 583 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"583 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 584 584 /><% 585 585 } -
trunk/www/biomaterials/extracts/list_extracts.jsp
r7982 r8083 1025 1025 list="<%=loader.getValues()%>" 1026 1026 suffix="<%=loader.getUnitSymbol()%>" 1027 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"1027 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 1028 1028 /><% 1029 1029 } … … 1046 1046 list="<%=loader.getValues()%>" 1047 1047 suffix="<%=loader.getUnitSymbol()%>" 1048 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"1048 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 1049 1049 /><% 1050 1050 } -
trunk/www/biomaterials/kits/list_kits.jsp
r7982 r8083 515 515 list="<%=loader.getValues()%>" 516 516 suffix="<%=loader.getUnitSymbol()%>" 517 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"517 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 518 518 /><% 519 519 } -
trunk/www/biomaterials/samples/list_samples.jsp
r7982 r8083 908 908 list="<%=loader.getValues()%>" 909 909 suffix="<%=loader.getUnitSymbol()%>" 910 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"910 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 911 911 /><% 912 912 } -
trunk/www/biomaterials/tags/list_tags.jsp
r7982 r8083 507 507 list="<%=loader.getValues()%>" 508 508 suffix="<%=loader.getUnitSymbol()%>" 509 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"509 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 510 510 /><% 511 511 } -
trunk/www/common/annotations/annotate.js
r7926 r8083 321 321 var msg = ''; 322 322 var defaultValues = ''; 323 var projectId = Data.int('page-data', 'project-id'); 324 var activeProjectId = Data.int('page-data', 'active-project-id'); 323 325 if (ann.projectId != 0) 324 326 { … … 337 339 else 338 340 { 339 msg = 'This is the default value for a project annotation.<br>Changing it will create a project-specific value.'; 341 msg = 'This is the default value for a project annotation.'; 342 if (projectId < 0 && activeProjectId > 0) msg += '<br>Changing it will create a project-specific value.'; 340 343 } 341 344 Doc.element('project-annotations-message').innerHTML = msg; -
trunk/www/common/annotations/annotate.jsp
r7954 r8083 35 35 import="net.sf.basedb.core.Nameable" 36 36 import="net.sf.basedb.core.Protocol" 37 import="net.sf.basedb.core.Project" 37 38 import="net.sf.basedb.core.Subtypable" 38 39 import="net.sf.basedb.core.ItemSubtype" … … 85 86 final Item itemType = Item.valueOf(request.getParameter("item_type")); 86 87 final int itemId = Values.getInt(request.getParameter("item_id")); 88 final int projectId = Values.getInt(request.getParameter("project_id"), -1); 87 89 final int protocolId = Values.getInt(request.getParameter("protocol_id"), -1); 88 90 final int subtypeId = Values.getInt(request.getParameter("subtype_id"), -1); … … 139 141 } 140 142 143 // Load a project if it has been specified 144 Project project = null; 145 boolean readProject = true; 146 try 147 { 148 if (projectId > 0) project = Project.getById(dc, projectId); 149 } 150 catch (PermissionDeniedException ex) 151 { 152 readProject = false; 153 } 154 141 155 // Load the current subtype 142 156 // NOTE! User may have selected a different subtype in the form than what is … … 196 210 final ItemQuery<AnnotationType> parameterQuery = Base.getProtocolParametersQuery(protocol); 197 211 212 if (projectId >= 0) 213 { 214 title += " (" + (project != null ? "for project: "+HTML.encodeTags(project.getName()) : "default values") + ")"; 215 annotationTypeQuery.restrict(Restrictions.eq(Hql.property("projectAnnotations"), Expressions.bool(true))); 216 if (parameterQuery != null) 217 { 218 parameterQuery.restrict(Restrictions.eq(Hql.property("projectAnnotations"), Expressions.bool(true))); 219 } 220 } 221 198 222 // Holds all categories that we have found 199 223 final Set<AnnotationTypeCategory> allCategories = … … 230 254 // Load the existing annotations 231 255 as = item.getAnnotationSet(); 232 ItemQuery<Annotation> aQuery = as.getAnnotations(null);256 ItemQuery<Annotation> aQuery = projectId < 0 ? as.getAnnotations(null) : as.getProjectAnnotations(null, project); 233 257 aQuery.order(Orders.asc(Hql.property("annotationType.name"))); 234 258 List<Annotation> annotations = aQuery.list(dc); … … 543 567 data-annotation-type-id="<%=annotationTypeId%>" 544 568 data-annotation-id="<%=annotationId %>" 569 data-project-id="<%=projectId%>" 570 data-active-project-id="<%=sc.getActiveProjectId()%>" 545 571 data-date-format="<%=htmlDateFormat%>" 546 572 data-datetime-format="<%=htmlDateTimeFormat%>" … … 846 872 <input type="hidden" name="item_type" value="<%=itemType.name()%>"> 847 873 <input type="hidden" name="item_id" value="<%=itemId%>"> 874 <% 875 if (projectId >= 0) 876 { 877 %> 878 <input type="hidden" name="project_id" value="<%=projectId%>"> 879 <% 880 } 881 %> 848 882 </form> 849 883 <% -
trunk/www/common/annotations/list.js
r7604 r8083 82 82 { 83 83 var target = event.currentTarget; 84 var annotationId = Data.int(target, 'annotation');85 84 var url = App.getRoot() + 'common/annotations/annotate.jsp?ID='+App.getSessionId(); 86 url += '&annotation_id='+Data.int(target, 'annotation'); ;85 url += '&annotation_id='+Data.int(target, 'annotation'); 87 86 url += '&item_type='+Data.get(target, 'item-type'); 88 87 url += '&item_id='+Data.get(target, 'item-id'); 89 88 url += '&annotationtype_id='+Data.get(target, 'annotation-type'); 89 var projectId = Data.get(target, 'project'); 90 if (projectId) url += '&project_id='+projectId; 90 91 url += '&standalone=1'; 91 92 Dialogs.openPopup(url, 'EditAnnotation', 750, 500); -
trunk/www/common/annotations/list_annotations.jsp
r7954 r8083 30 30 import="net.sf.basedb.core.AnnotationType" 31 31 import="net.sf.basedb.core.Protocol" 32 import="net.sf.basedb.core.Project" 32 33 import="net.sf.basedb.core.AnnotationSet" 33 34 import="net.sf.basedb.core.Annotation" … … 53 54 import="net.sf.basedb.core.snapshot.AnnotationSnapshot" 54 55 import="net.sf.basedb.core.snapshot.SnapshotManager" 56 import="net.sf.basedb.core.snapshot.ProjectFilter" 57 import="net.sf.basedb.core.snapshot.AnnotationTypeFilter" 55 58 import="net.sf.basedb.clients.web.Base" 56 59 import="net.sf.basedb.clients.web.util.HTML" … … 61 64 import="net.sf.basedb.clients.web.formatter.FormatterFactory" 62 65 import="net.sf.basedb.util.Values" 66 import="net.sf.basedb.util.filter.Filter" 67 import="net.sf.basedb.util.filter.AllOfFilter" 68 import="java.util.Arrays" 63 69 import="java.util.Collections" 64 70 import="java.util.ArrayList" … … 85 91 Set<AnnotationType> protocolParameters = null; 86 92 Map<AnnotationType, AnnotationSnapshot> existing = null; 93 94 boolean hasProjectSpecificAnnotationType = false; 95 AnnotationTypeFilter annotationTypeFilter = null; 96 Filter<AnnotationSnapshot> projectFilter = null; 87 97 88 98 try … … 124 134 { 125 135 existing.put(a.getAnnotationType(dc), a); 136 hasProjectSpecificAnnotationType |= at.getProjectAnnotations(); 126 137 } 127 138 else … … 132 143 Collections.sort(inheritedAnnotations, AnnotationSnapshot.sortByAnnotationType(dc)); 133 144 annotationTypes.addAll(existing.keySet()); 145 } 146 147 if (hasProjectSpecificAnnotationType) 148 { 149 manager.setAllProjects(); 150 annotationTypeFilter = new AnnotationTypeFilter(); 151 projectFilter = new AllOfFilter<>(Arrays.asList(annotationTypeFilter, new ProjectFilter(dc))); 134 152 } 135 153 … … 151 169 annotationTypes.removeAll(protocolParameters); 152 170 } 171 153 172 // Load the current subtype 154 173 // NOTE! User may have selected a different subtype in the form than what is … … 187 206 } 188 207 Formatter<Date> dateTimeFormatter = FormatterFactory.getDateTimeFormatter(sc); 208 String projectValuesTitle = "<div class=\"project-values\"><div>Project values</div><div></div><div></div><div class=\"last-modified\">Last modified</div></div>"; 189 209 %> 190 210 <base:page type="iframe" id="list-annotations"> … … 207 227 { 208 228 float: right; 229 } 230 231 .project-values 232 { 233 display: grid; 234 grid-template-columns: 1fr 1fr 18px 12em; 235 } 236 237 .project-values div 238 { 239 padding: 1px 2px; 240 border-bottom-width: 1px; 241 border-bottom-style: dotted; 242 } 243 244 /* Remove borders on last row */ 245 .project-values div:nth-last-child(-n+4) 246 { 247 border-bottom-width: 0; 248 } 249 250 .project-values .default-value 251 { 252 font-style: italic; 253 } 254 255 .project-values .current-value::before 256 { 257 font-weight: bold; 258 content: '›'; 259 margin-right: 2px; 260 } 261 262 .project-values .other-value::before 263 { 264 font-weight: bold; 265 content: '›'; 266 color: transparent; 267 margin-right: 2px; 268 } 269 270 /* Span accross first 2 columns */ 271 .project-values .no-default 272 { 273 font-style: italic; 274 grid-column-start: 1; 275 grid-column-end: 3; 276 } 277 278 .last-modified 279 { 280 white-space: nowrap; 281 border-left-width: 1px; 282 border-left-style: dotted; 209 283 } 210 284 </style> … … 249 323 <tbl:columndef 250 324 id="values" 251 title=" Values"325 title="<%=hasProjectSpecificAnnotationType ? "Current values" : "Values" %>" 252 326 /> 253 327 <tbl:columndef 254 id="note" 255 title="Note" 328 id="projectValues" 329 title="<%=projectValuesTitle%>" 330 tooltip="Project values" 331 show="<%=hasProjectSpecificAnnotationType ? "always" : "never" %>" 332 style="padding: 0;" 256 333 /> 257 334 <tbl:columndef … … 259 336 title="Last modified" 260 337 formatter="<%=dateTimeFormatter%>" 338 show="<%=hasProjectSpecificAnnotationType ? "never" : "always" %>" 261 339 /> 262 340 <tbl:columndef … … 293 371 AnnotationSnapshot a = existing != null ? existing.get(at) : null; 294 372 List<?> values = null; 295 List<?> defaultValues = null;296 373 Formatter<Object> formatter = null; 297 374 boolean projectSpecific = false; … … 303 380 values = a.getActualValues(ann.getUnitConverter(null), ann.getValueType()); 304 381 formatter = FormatterFactory.getAnnotationFormatter(sc, ann, null); 305 306 382 projectSpecific = a.getProjectId() != 0; 307 if (ann.isOverridingDefault())308 {309 Annotation defAnn = ann.getDefaultAnnotation();310 defaultValues = defAnn.getValues();311 }312 383 } 313 384 if (values != null || !at.isRemoved()) … … 337 408 visible="<%=at.isRemoved()%>" 338 409 /><%=Base.getLinkedName(ID, at, false, true)%></tbl:cell> 339 <tbl:cell column="values" clazz="<%=projectSpecific && defaultValues != null ? "cell ps-annotation" : "cell" %>">410 <tbl:cell column="values" clazz="<%=projectSpecific ? "cell ps-annotation" : "cell" %>" disableOverflowCheck="true"> 340 411 <%=values == null || values.size() == 0 ? "<i>- no values -</i>" : Values.getString(values, ", ", true, formatter)%> 341 <base:icon image=" edit.png" subclass="edit-annotation auto-init link"412 <base:icon image="<%=a==null?"edit_create.png":"edit.png" %>" subclass="edit-annotation auto-init link" 342 413 data-auto-init="edit-annotation" 343 414 data-annotation="<%=a!=null ? a.getThisAnnotationId() : 0 %>" … … 345 416 data-item-type="<%=itemType.name()%>" 346 417 data-item-id="<%=itemId %>" 347 tooltip=" Modify the values of this annotation"418 tooltip="<%=a==null?"Create this annotation":"Modify the values of this annotation"%>" 348 419 visible="<%=annotatePermission %>" 349 420 /> 350 421 </tbl:cell> 351 <tbl:cell column="note"> 422 <tbl:cell column="projectValues" disableOverflowCheck="true" style="padding:0;"> 423 <div class="project-values"> 352 424 <% 353 if (projectSpecific) 425 if (at.getProjectAnnotations()) 426 { 427 annotationTypeFilter.setAnnotationType(at); 428 List<AnnotationSnapshot> projectValues = manager.findAnnotations(dc, snapshot, projectFilter, false); 429 projectValues.sort(AnnotationSnapshot.sortByProject(dc, true)); 430 int numValues = projectValues.size(); 431 if (numValues == 0 || projectValues.get(0).getProjectId() != 0) 432 { 433 %> 434 <div class="no-default"><%=numValues > 0 ? "No default value":""%></div> 435 <div><base:icon image="edit_create.png" subclass="edit-annotation auto-init link" 436 data-auto-init="edit-annotation" 437 data-annotation="0" 438 data-annotation-type="<%=at.getId()%>" 439 data-project="0" 440 data-item-type="<%=itemType.name()%>" 441 data-item-id="<%=itemId %>" 442 tooltip="Create a default value" 443 visible="<%=annotatePermission %>" 444 /></div> 445 <div class="last-modified"></div> 446 <% 447 } 448 if (numValues > 0) 449 { 450 for (AnnotationSnapshot pv : projectValues) 451 { 452 boolean isDefault = pv.getProjectId() == 0; 453 boolean isCurrent = pv == a; 454 Annotation ann = pv.getThisAnnotation(dc); 455 values = pv.getActualValues(ann.getUnitConverter(null), ann.getValueType()); 456 formatter = FormatterFactory.getAnnotationFormatter(sc, ann, null); 457 if (numValues > 1) 458 { 459 %> 460 <div class="<%=isCurrent?"current-value":"other-value"%><%=isDefault ? " default-value":""%>"><%=isDefault ? "Default" : pv.getProject(dc).getName() %></div> 461 <div><%=Values.getString(values, ", ", true, formatter) %></div> 462 <% 463 } 464 else 465 { 466 %> 467 <div></div><div></div> 468 <% 469 } 470 %> 471 <div><base:icon image="edit.png" subclass="edit-annotation auto-init link" 472 data-auto-init="edit-annotation" 473 data-annotation="<%=pv.getThisAnnotationId()%>" 474 data-annotation-type="<%=at.getId()%>" 475 data-project="<%=pv.getProjectId()%>" 476 data-item-type="<%=itemType.name()%>" 477 data-item-id="<%=itemId %>" 478 tooltip="<%=isDefault ? "Modify the default value" : "Modify the value in this project"%>" 479 visible="<%=annotatePermission %>" 480 /></div> 481 <div class="last-modified"><%=dateTimeFormatter.format(pv.getThisLastUpdate())%></div> 482 <% 483 } 484 } 485 } 486 else if (a != null) 354 487 { 355 488 %> 356 <%=defaultValues == null || defaultValues.size() == 0 ? "No default value" : "Default value: " + Values.getString(defaultValues, ", ", true, formatter)%> 489 <div></div><div></div><div></div> 490 <div class="last-modified"><%=dateTimeFormatter.format(a.getThisLastUpdate())%></div> 357 491 <% 358 492 } 359 493 %> 494 </div> 360 495 </tbl:cell> 361 496 <tbl:cell column="lastModified" value="<%=a == null ? null : a.getThisLastUpdate() %>" /> … … 411 546 <tbl:columndef 412 547 id="values" 413 title=" Values"548 title="<%=hasProjectSpecificAnnotationType ? "Current values" : "Values" %>" 414 549 /> 415 550 <tbl:columndef 416 id="note" 417 title="Note" 551 id="projectValues" 552 title="<%=projectValuesTitle%>" 553 tooltip="Project values" 554 show="<%=hasProjectSpecificAnnotationType ? "always" : "never" %>" 555 style="padding: 0;" 418 556 /> 419 557 <tbl:columndef … … 421 559 title="Last modified" 422 560 formatter="<%=dateTimeFormatter%>" 561 show="<%=hasProjectSpecificAnnotationType ? "never" : "always" %>" 423 562 /> 424 563 <tbl:columndef … … 439 578 boolean annotatePermission = writePermission & at.hasPermission(Permission.USE); 440 579 AnnotationSnapshot a = existing != null ? existing.get(at) : null; 580 List<?> values = null; 441 581 Formatter<Object> formatter = null; 442 List<?> values = null;443 List<?> defaultValues = null;444 582 boolean projectSpecific = false; 445 583 if (a != null) … … 450 588 values = a.getActualValues(ann.getUnitConverter(null), ann.getValueType()); 451 589 formatter = FormatterFactory.getAnnotationFormatter(sc, ann, null); 452 453 590 projectSpecific = a.getProjectId() != 0; 454 if (ann.isOverridingDefault())455 {456 Annotation defAnn = ann.getDefaultAnnotation();457 defaultValues = defAnn.getValues();458 }459 591 } 460 592 if (values != null || !at.isRemoved()) … … 467 599 visible="<%=at.isRemoved()%>" 468 600 /><%=Base.getLinkedName(ID, at, false, true)%></tbl:cell> 469 <tbl:cell column="values" clazz="<%=projectSpecific && defaultValues != null ? "cell ps-annotation" : "cell" %>">601 <tbl:cell column="values" clazz="<%=projectSpecific ? "cell ps-annotation" : "cell" %>" disableOverflowCheck="true"> 470 602 <%=values == null || values.size() == 0 ? "<i>- no values -</i>" : Values.getString(values, ", ", true, formatter)%> 471 <base:icon image=" edit.png" subclass="edit-annotation auto-init link"603 <base:icon image="<%=a==null?"edit_create.png":"edit.png" %>" subclass="edit-annotation auto-init link" 472 604 data-auto-init="edit-annotation" 473 605 data-annotation="<%=a!=null ? a.getThisAnnotationId() : 0 %>" … … 475 607 data-item-type="<%=itemType.name()%>" 476 608 data-item-id="<%=itemId %>" 477 tooltip=" Modify the values of this protocol parameter"609 tooltip="<%=a==null?"Create this protocol parameter":"Modify the values of this protocol parameter"%>" 478 610 visible="<%=annotatePermission %>" 479 611 /> 480 612 </tbl:cell> 481 <tbl:cell column="note"> 613 <tbl:cell column="projectValues" disableOverflowCheck="true" style="padding:0;"> 614 <div class="project-values"> 482 615 <% 483 if (projectSpecific) 616 if (at.getProjectAnnotations()) 617 { 618 annotationTypeFilter.setAnnotationType(at); 619 List<AnnotationSnapshot> projectValues = manager.findAnnotations(dc, snapshot, projectFilter, false); 620 projectValues.sort(AnnotationSnapshot.sortByProject(dc, true)); 621 int numValues = projectValues.size(); 622 if (numValues == 0 || projectValues.get(0).getProjectId() != 0) 623 { 624 %> 625 <div class="no-default"><%=numValues > 0 ? "No default value":""%></div> 626 <div><base:icon image="edit_create.png" subclass="edit-annotation auto-init link" 627 data-auto-init="edit-annotation" 628 data-annotation="0" 629 data-annotation-type="<%=at.getId()%>" 630 data-project="0" 631 data-item-type="<%=itemType.name()%>" 632 data-item-id="<%=itemId %>" 633 tooltip="Create a default value" 634 visible="<%=annotatePermission %>" 635 /></div> 636 <div class="last-modified"></div> 637 <% 638 } 639 if (numValues > 0) 640 { 641 for (AnnotationSnapshot pv : projectValues) 642 { 643 boolean isDefault = pv.getProjectId() == 0; 644 boolean isCurrent = pv == a; 645 Annotation ann = pv.getThisAnnotation(dc); 646 values = pv.getActualValues(ann.getUnitConverter(null), ann.getValueType()); 647 formatter = FormatterFactory.getAnnotationFormatter(sc, ann, null); 648 if (numValues > 1) 649 { 650 %> 651 <div class="<%=isCurrent?"current-value":"other-value"%><%=isDefault ? " default-value":""%>"><%=isDefault ? "Default" : pv.getProject(dc).getName() %></div> 652 <div><%=Values.getString(values, ", ", true, formatter) %></div> 653 <% 654 } 655 else 656 { 657 %> 658 <div></div><div></div> 659 <% 660 } 661 %> 662 <div><base:icon image="edit.png" subclass="edit-annotation auto-init link" 663 data-auto-init="edit-annotation" 664 data-annotation="<%=pv.getThisAnnotationId()%>" 665 data-annotation-type="<%=at.getId()%>" 666 data-project="<%=pv.getProjectId()%>" 667 data-item-type="<%=itemType.name()%>" 668 data-item-id="<%=itemId %>" 669 tooltip="Modify the values of this protocol parameter" 670 visible="<%=annotatePermission %>" 671 /></div> 672 <div class="last-modified"><%=dateTimeFormatter.format(pv.getThisLastUpdate())%></div> 673 <% 674 } 675 } 676 } 677 else if (a != null) 484 678 { 485 679 %> 486 <%=defaultValues == null || defaultValues.size() == 0 ? "No default value" : "Default value: " + Values.getString(defaultValues, ", ", true, formatter)%> 680 <div></div><div></div><div></div> 681 <div class="last-modified"><%=dateTimeFormatter.format(a.getThisLastUpdate())%></div> 487 682 <% 488 683 } 489 684 %> 685 </div> 490 686 </tbl:cell> 491 687 <tbl:cell column="lastModified" value="<%=a == null ? null : a.getThisLastUpdate() %>" /> … … 625 821 <tbl:row> 626 822 <tbl:cell column="annotation"><%=Base.getLinkedName(ID, at, at == null, true)%></tbl:cell> 627 <tbl:cell column="values" clazz="<%=projectSpecific && defaultValues != null ? "cell ps-annotation" : "cell" %>">823 <tbl:cell column="values" clazz="<%=projectSpecific ? "cell ps-annotation" : "cell" %>" disableOverflowCheck="true"> 628 824 <%=values == null || values.size() == 0 ? 629 825 "<i>- no values -</i>" : Values.getString(values, ", ", true, formatter)%> -
trunk/www/common/columns/add_relateditem_column.js
r7885 r8083 28 28 var configure = {}; 29 29 30 var currentSubtypes; 31 var currentAnnotationTypesAll; 32 var currentAnnotationTypesCategory; 30 var currentTargetSubtypes; 31 var currentTargetAnnotationTypesAll; 32 var currentTargetAnnotationTypesCategory; 33 var currentTargetExtensionColumns; 34 35 var currentChildItemTypes; 36 var currentChildSubtypes; 37 var currentChildAnnotationTypesAll; 38 var currentChildAnnotationTypesCategory; 39 var currentChildExtensionColumns; 33 40 34 41 // See also net.sf.basedb.clients.web.extensions.list.RelatedItemColumn.pathTitles 35 36 42 var nameCol = {'id': '.', 'text': 'Name'}; 37 43 var externalIdCol = {'id': 'externalId', 'text': 'External ID'}; … … 68 74 { 69 75 Events.addEventHandler('targetItemType', 'change', configure.targetItemTypeOnChange); 70 Events.addEventHandler('subtype', 'change', configure.subtypeOnChange); 76 Events.addEventHandler('targetSubtype', 'change', configure.targetSubtypeOnChange); 77 Events.addEventHandler('childItemType', 'change', configure.childItemTypeOnChange); 78 Events.addEventHandler('childSubtype', 'change', configure.childSubtypeOnChange); 71 79 Events.addEventHandler('showAllAnnotationTypes', 'change', configure.updateColumnsList); 72 Events.addEventHandler('column', 'dblclick', configure.addColumn);73 80 Events.addEventHandler(document.body, 'click', configure.hideMessage); 74 81 … … 78 85 } 79 86 80 81 configure.subtypeOnChange = function(event) 82 { 83 var frm = document.forms['relatedItems']; 87 // Clear existing columns 88 configure.clearColumns = function() 89 { 90 Doc.element('columns').innerHTML = ''; 91 Doc.element('annotations').innerHTML = ''; 92 Doc.element('extensions').innerHTML = ''; 93 Doc.hide('targetPath'); 94 Doc.hide('childPath'); 95 } 96 // Clear the list and disable it 97 configure.clearList = function(listOrId) 98 { 99 var list = Doc.element(listOrId); 100 list.disabled = true; 101 list.length = 1; 102 } 103 104 configure.targetItemTypeOnChange = function(event) 105 { 106 // Reset everything that depends on the target item type 107 configure.clearColumns(); 108 configure.clearList('targetSubtype'); 109 configure.clearList('childItemType'); 110 configure.clearList('childSubtype'); 111 112 var frm = document.forms['relatedItems']; 113 var sourceItemType = frm.item_type.value; 84 114 var targetItemType = frm.targetItemType.value; 85 var category = frm.subtype[frm.subtype.selectedIndex].text; 115 var direction = Data.get(frm.targetItemType[frm.targetItemType.selectedIndex], 'direction'); 116 117 if (targetItemType) 118 { 119 var url = 'ajax.jsp?ID='+App.getSessionId(); 120 url += '&cmd=GetSubtypesAndAnnotationTypes&itemType='+encodeURIComponent(targetItemType); 121 url += '&sourceItemType='+encodeURIComponent(sourceItemType); 122 url += '&direction='+encodeURIComponent(direction); 123 124 var request = Ajax.getXmlHttpRequest(); 125 request.open("GET", url, true); 126 request.send(null); 127 Ajax.setReadyStateHandler(request, configure.targetTypeInfoLoaded, configure.targetTypeInfoLoaded); 128 } 129 } 130 131 configure.targetSubtypeOnChange = function(event) 132 { 133 // Reset everything that depends on the target subtype 134 configure.clearColumns(); 135 136 var frm = document.forms['relatedItems']; 137 var targetItemType = frm.targetItemType.value; 138 var sourceItemType = frm.item_type.value; 139 var category = frm.targetSubtype.selectedIndex > 0 ? frm.targetSubtype[frm.targetSubtype.selectedIndex].text : null; 86 140 if (targetItemType && category) 87 141 { 88 142 var url = 'ajax.jsp?ID='+App.getSessionId(); 89 143 url += '&cmd=GetAnnotationTypesForCategory&itemType='+encodeURIComponent(targetItemType); 144 url += '&sourceItemType='+encodeURIComponent(sourceItemType); 90 145 url += '&category='+encodeURIComponent(category); 91 146 … … 93 148 request.open("GET", url, true); 94 149 request.send(null); 95 Ajax.setReadyStateHandler(request, configure.typeInfoLoaded, configure.typeInfoLoaded); 96 } 97 } 98 99 configure.targetItemTypeOnChange = function(event) 100 { 101 var targetItemType = event.currentTarget.value; 102 if (targetItemType) 150 Ajax.setReadyStateHandler(request, configure.targetTypeInfoLoaded, configure.targetTypeInfoLoaded); 151 } 152 } 153 154 configure.targetTypeInfoLoaded = function(request) 155 { 156 var response = JSON.parse(request.responseText); 157 if (response.status != 'ok') 158 { 159 App.debug(request.responseText); 160 return; 161 } 162 var frm = document.forms['relatedItems']; 163 164 if (response.childTypes) 165 { 166 frm.childItemType.length = 1; 167 currentChildItemTypes = response.childTypes; 168 for (var i = 0; i < currentChildItemTypes.length; i++) 169 { 170 var st = currentChildItemTypes[i]; 171 frm.childItemType[frm.childItemType.length] = new Option('Child: '+st.name, st.id); 172 } 173 frm.childItemType.selectedIndex = 0; 174 frm.childItemType.disabled = currentChildItemTypes.length == 0; 175 } 176 177 if (response.subtypes) 178 { 179 currentTargetSubtypes = response.subtypes; 180 frm.targetSubtype.length = 1; 181 for (var i = 0; i < currentTargetSubtypes.length; i++) 182 { 183 var st = currentTargetSubtypes[i]; 184 frm.targetSubtype[frm.targetSubtype.length] = new Option(st.name, st.id); 185 } 186 frm.targetSubtype.selectedIndex = 0; 187 frm.targetSubtype.disabled = currentTargetSubtypes.length == 0; 188 } 189 190 if (response.annotationTypes) currentTargetAnnotationTypesAll = response.annotationTypes; 191 if (response.annotationTypesCategory) currentTargetAnnotationTypesCategory = response.annotationTypesCategory; 192 if (response.extensionColumns) currentTargetExtensionColumns = response.extensionColumns; 193 194 configure.updateColumnsList(); 195 } 196 197 configure.childItemTypeOnChange = function(event) 198 { 199 // Reset everything that depends on the child item type 200 configure.clearColumns(); 201 configure.clearList('childSubtype'); 202 203 var frm = document.forms['relatedItems']; 204 var targetItemType = frm.targetItemType.value; 205 var childItemType = frm.childItemType.value; 206 207 if (targetItemType && childItemType) 103 208 { 104 209 var url = 'ajax.jsp?ID='+App.getSessionId(); 105 url += '&cmd=GetSubtypesAndAnnotationTypes&itemType='+encodeURIComponent(targetItemType); 210 url += '&cmd=GetSubtypesAndAnnotationTypes&itemType='+encodeURIComponent(childItemType); 211 url += '&sourceItemType='+encodeURIComponent(targetItemType); 212 url += '&direction=CHILD'; 106 213 107 214 var request = Ajax.getXmlHttpRequest(); 108 215 request.open("GET", url, true); 109 216 request.send(null); 110 Ajax.setReadyStateHandler(request, configure.typeInfoLoaded, configure.typeInfoLoaded); 111 } 112 } 113 114 configure.typeInfoLoaded = function(request) 217 Ajax.setReadyStateHandler(request, configure.childTypeInfoLoaded, configure.childTypeInfoLoaded); 218 } 219 else 220 { 221 configure.updateColumnsList(); 222 } 223 } 224 225 configure.childSubtypeOnChange = function(event) 226 { 227 // Reset everything that depends on the child subtype 228 configure.clearColumns(); 229 230 var frm = document.forms['relatedItems']; 231 var targetItemType = frm.targetItemType.value; 232 var childItemType = frm.childItemType.value; 233 var category = frm.childSubtype.selectedIndex > 0 ? frm.childSubtype[frm.childSubtype.selectedIndex].text : null; 234 if (targetItemType && childItemType && category) 235 { 236 var url = 'ajax.jsp?ID='+App.getSessionId(); 237 url += '&cmd=GetAnnotationTypesForCategory&itemType='+encodeURIComponent(childItemType); 238 url += '&sourceItemType='+encodeURIComponent(targetItemType); 239 url += '&category='+encodeURIComponent(category); 240 241 var request = Ajax.getXmlHttpRequest(); 242 request.open("GET", url, true); 243 request.send(null); 244 Ajax.setReadyStateHandler(request, configure.childTypeInfoLoaded, configure.childTypeInfoLoaded); 245 } 246 } 247 248 configure.childTypeInfoLoaded = function(request) 115 249 { 116 250 var response = JSON.parse(request.responseText); … … 120 254 return; 121 255 } 122 123 var frm = document.forms['relatedItems']; 256 var frm = document.forms['relatedItems']; 257 124 258 if (response.subtypes) 125 259 { 126 current Subtypes = response.subtypes;127 frm. subtype.length = 1;128 for (var i = 0; i < current Subtypes.length; i++)260 currentChildSubtypes = response.subtypes; 261 frm.childSubtype.length = 1; 262 for (var i = 0; i < currentChildSubtypes.length; i++) 129 263 { 130 var st = current Subtypes[i];131 frm. subtype[frm.subtype.length] = new Option(st.name, st.id);264 var st = currentChildSubtypes[i]; 265 frm.childSubtype[frm.childSubtype.length] = new Option(st.name, st.id); 132 266 } 133 frm.subtype.selectedIndex = 0; 134 } 135 if (response.annotationTypes) currentAnnotationTypesAll = response.annotationTypes; 136 if (response.annotationTypesCategory) currentAnnotationTypesCategory = response.annotationTypesCategory; 267 frm.childSubtype.selectedIndex = 0; 268 frm.childSubtype.disabled = currentChildSubtypes.length == 0; 269 } 270 271 if (response.annotationTypes) currentChildAnnotationTypesAll = response.annotationTypes; 272 if (response.annotationTypesCategory) currentChildAnnotationTypesCategory = response.annotationTypesCategory; 273 if (response.extensionColumns) currentChildExtensionColumns = response.extensionColumns; 274 137 275 configure.updateColumnsList(); 138 276 } 277 139 278 140 279 configure.updateColumnsList = function() … … 143 282 144 283 var targetItemType = frm.targetItemType.value; 145 var subtype = frm.subtype.value; 146 147 if (!targetItemType || !subtype) 148 { 149 frm.column.length = 0; 150 return; 151 } 152 153 var annotationTypes = frm.showAllAnnotationTypes.checked ? currentAnnotationTypesAll : currentAnnotationTypesCategory; 154 frm.column.length = 0; 155 284 var targetSubtype = frm.targetSubtype.value; 285 var childItemType = frm.childItemType.value; 286 var childSubtype = frm.childSubtype.value; 287 288 // At least a target item type and a subtype must be selected 289 if (!targetItemType || !targetSubtype) return; 290 // If a child item type is selected, so must a child subtype 291 if (childItemType && !childSubtype) return; 292 293 var targetDirection = Data.get(frm.targetItemType[frm.targetItemType.selectedIndex], 'direction'); 294 var targetText = frm.targetSubtype[frm.targetSubtype.selectedIndex].text; 295 if (targetDirection == 'PARENT') 296 { 297 Doc.element('targetPath').innerHTML = 'Parent('+targetText+')'; 298 Doc.element('targetPath').className = 'parentitem'; 299 } 300 else 301 { 302 Doc.element('targetPath').innerHTML = 'Child('+targetText+')'; 303 Doc.element('targetPath').className = 'childitem'; 304 } 305 Doc.show('targetPath', 'inline'); 306 307 var staticCols = staticColumns[targetItemType] || []; 308 var annotationCols = frm.showAllAnnotationTypes.checked ? currentTargetAnnotationTypesAll : currentTargetAnnotationTypesCategory; 309 var xtCols = currentTargetExtensionColumns; 310 311 if (childItemType && childSubtype) 312 { 313 staticCols = staticColumns[childItemType] || []; 314 annotationCols = frm.showAllAnnotationTypes.checked ? currentChildAnnotationTypesAll : currentChildAnnotationTypesCategory; 315 xtCols = currentChildExtensionColumns; 316 var childText = frm.childSubtype[frm.childSubtype.selectedIndex].text; 317 Doc.element('childPath').innerHTML = 'Child: '+childText; 318 Doc.show('childPath', 'inline'); 319 } 320 321 var html = ''; 156 322 // Add static property columns depending on itemtype 157 var cols = staticColumns[targetItemType] || []; 158 for (var i = 0; i < cols.length; i++) 159 { 160 var c = cols[i]; 161 frm.column[frm.column.length] = new Option(c.text, c.id); 162 } 163 323 for (var i = 0; i < staticCols.length; i++) 324 { 325 var c = staticCols[i]; 326 var text = Strings.encodeTags(c.text); 327 html += '<div><label><input type="checkbox" data-column-id="'+c.id+'" data-column-title="'+text+'">'; 328 html += text + '</label></div>'; 329 } 330 Doc.element('columns').innerHTML = html; 331 164 332 // Add annotation columns 165 for (var i = 0; i < annotationTypes.length; i++) 166 { 167 var at = annotationTypes[i]; 168 frm.column[frm.column.length] = new Option(at.name+" [A]", "#"+at.id); 169 } 170 frm.column.selectedIndex = -1; 171 } 172 333 html = ''; 334 for (var i = 0; i < annotationCols.length; i++) 335 { 336 var at = annotationCols[i]; 337 var text = Strings.encodeTags(at.name); 338 html += '<div><label><input type="checkbox" data-column-id="#+'+at.id+'" data-column-title="'+text+'">'; 339 html += text + '</label></div>'; 340 } 341 Doc.element('annotations').innerHTML = html; 342 343 // Extension columns 344 html = ''; 345 for (var i = 0; i < xtCols.length; i++) 346 { 347 var xtCol = xtCols[i]; 348 var id = "!"+xtCol.id+"#"+xtCol.columnId; 349 var text = Strings.encodeTags(xtCol.name); 350 if (xtCol.id.indexOf('net.sf.basedb.clients.web.related-item-columns') == 0) 351 { 352 // Special case when the extension is the related-item extionsion 353 // It has built-in support for two hops and doen't have to use the 354 // extension mechanism recursively 355 id = xtCol.columnId; 356 } 357 html += '<div title="'+Strings.encodeTags(xtCol.tooltip)+'">'; 358 html += '<label><input type="checkbox" data-column-id="'+id+'" data-column-title="'+text+'">'; 359 html += (xtCol.nameHTML || text) + '</label></div>'; 360 } 361 Doc.element('extensions').innerHTML = html; 362 } 173 363 174 364 configure.addColumn = function(event) … … 178 368 var data = {}; 179 369 data.targetItemType = frm.targetItemType.value; 180 data.subtype = frm.subtype.value; 370 data.targetSubtype = frm.targetSubtype.value; 371 data.childItemType = frm.childItemType.value; 372 data.childSubtype = frm.childSubtype.value; 181 373 182 374 if (!data.targetItemType) … … 185 377 return; 186 378 } 187 188 if (!data.subtype) 189 { 190 Forms.showNotification('subtype', 'Please select a subtype!'); 191 return; 192 } 193 data.subtypeName = frm.subtype[frm.subtype.selectedIndex].text; 379 if (!data.targetSubtype) 380 { 381 Forms.showNotification('targetSubtype', 'Please select a subtype!'); 382 return; 383 } 384 if (data.childItemType && !data.childSubtype) 385 { 386 Forms.showNotification('childSubtype', 'Please select a subtype!'); 387 return; 388 } 389 390 data.targetSubtypeName = frm.targetSubtype[frm.targetSubtype.selectedIndex].text; 194 391 data.direction = Data.get(frm.targetItemType[frm.targetItemType.selectedIndex], 'direction'); 195 392 393 var propertyPrefix = '/'+data.direction+'/'+data.targetItemType+'/'+data.targetSubtype+'/'; 394 var titlePrefix = data.targetSubtypeName; 395 if (data.childItemType) 396 { 397 data.childSubtypeName = frm.childSubtype[frm.childSubtype.selectedIndex].text; 398 propertyPrefix += '/CHILD/'+data.childItemType+'/'+data.childSubtype+'/'; 399 titlePrefix += '.'+data.childSubtypeName; 400 } 401 196 402 var numSelected = 0; 197 for (var i = 0; i < frm.column.length; i++) 198 { 199 if (frm.column[i].selected) 403 var allColumns = document.querySelectorAll("input[data-column-id]"); 404 for (var i = 0; i < allColumns.length; i++) 405 { 406 if (allColumns[i].checked) 200 407 { 201 var col = frm.column[i];202 data.column = col.value;203 data.property = '/'+data.direction+'/'+data.targetItemType+'/'+data.subtype+'/'+data.column;204 data.title = data.subtypeName + (col.value == '.' ? '' : '.'+col.text);408 var col = allColumns[i]; 409 data.column = Data.get(col, 'column-id'); 410 data.property = propertyPrefix+data.column; 411 data.title = titlePrefix + (data.column == '.' ? '' : '.'+Data.get(col, 'column-title')); 205 412 Events.sendCustomEvent(window.opener.Doc.element('selectRelatedItemColumn'), 'base-selected', data); 206 413 numSelected++; 414 // Remove the column from the list 415 var div = col.parentNode.parentNode; 416 div.parentNode.removeChild(div); 207 417 } 208 418 } … … 210 420 if (numSelected == 0) 211 421 { 212 Forms.showNotification('column', 'Please select a column!'); 213 return; 214 } 215 216 for (var i = frm.column.length-1; i >= 0; i--) 217 { 218 if (frm.column[i].selected) frm.column[i] = null; 422 Forms.showNotification('columnstable', 'Please select at least one column!', null, 'pointer-below'); 423 return; 219 424 } 220 425 Doc.element('added-column-msg').innerHTML = numSelected == 1 ? 221 426 '1 column has been added to the table.' : numSelected + ' columns have been added to the table.'; 222 Doc.show('added-column-msg' );427 Doc.show('added-column-msg', 'table'); 223 428 event.stopPropagation(); // To prevent the hideMessage() method from executing 429 setTimeout(configure.hideMessage, 2000); 224 430 } 225 431 -
trunk/www/common/columns/add_relateditem_column.jsp
r7842 r8083 43 43 %> 44 44 <base:page type="popup" title="Add parent or child item column"> 45 <base:head scripts="~add_relateditem_column.js" /> 45 <base:head scripts="~add_relateditem_column.js"> 46 <style> 47 th 48 { 49 min-width: 10em; 50 } 51 select 52 { 53 width: calc(100% - 1em); 54 min-width: 15em; 55 } 56 57 .columnstable 58 { 59 width: 100%; 60 } 61 62 .columnstable tr 63 { 64 vertical-align: top; 65 } 66 67 .columnstable th 68 { 69 text-align: left; 70 padding: 2px 4px; 71 } 72 73 .columnstable td 74 { 75 text-align: left; 76 padding: 4px 1px; 77 } 78 79 .columnstable td > div 80 { 81 margin-bottom: 4px; 82 white-space: nowrap; 83 } 84 85 #added-column-msg 86 { 87 position: absolute; 88 bottom: 1em; 89 left: 0; 90 right: 0; 91 display: table; 92 margin-left: auto; 93 margin-right: auto; 94 } 95 96 .parentitem::before 97 { 98 content: url('../../images/parent-item.png'); 99 margin: 0 2px; 100 vertical-align: -2px; 101 } 102 .childitem::before 103 { 104 content: url('../../images/child-item.png'); 105 margin: 0 2px; 106 vertical-align: -2px; 107 } 108 109 .columnsFrom 110 { 111 font-weight: bold; 112 } 113 114 .columnsFrom > span 115 {} 116 </style> 117 </base:head> 46 118 <base:body> 47 119 <h1>Add parent or child item column <base:help helpid="columns.configure.add_relateditem_column" /></h1> … … 52 124 53 125 <div class="content"> 54 <table class="fullform input100 smaller bottomborder"> 126 <div class="absolutefull" > 127 <table class="fullform input100 bottomborder"> 55 128 <tr> 56 <th> Item type</th>57 <td> 58 <select name="targetItemType" id="targetItemType" style="min-width: 15em;">129 <th>Parent/child</th> 130 <td> 131 <select name="targetItemType" id="targetItemType" class="required"> 59 132 <option value="">- select - 60 133 <% … … 74 147 </select> 75 148 </td> 149 <td style="white-space: nowrap;">with link to</td> 150 <td> 151 <select name="childItemType" id="childItemType" disabled> 152 <option value="">- optional - 153 </select> 154 </td> 155 <td> 156 </td> 76 157 </tr> 77 158 <tr> 78 159 <th>Subtype</th> 79 160 <td> 80 <select name=" subtype" id="subtype" style="min-width: 15em;">161 <select name="targetSubtype" id="targetSubtype" class="required" disabled> 81 162 <option value="">- select - 82 163 </select> 83 <label><input type="checkbox" id="showAllAnnotationTypes" name="showAllAnnotationTypes">Show annotation types for all subtypes</label> 164 </td> 165 <td>subtype</td> 166 <td> 167 <select name="childSubtype" id="childSubtype" disabled> 168 <option value="">- select - 169 </select> 84 170 </td> 85 171 </tr> 86 172 <tr class="dynamic"> 87 <th>Column</th> 88 <td> 89 <select name="column" id="column" style="min-width: 25em; height: calc(100% - 4em);" multiple></select> 90 <div id="added-column-msg" class="messagecontainer note" style="display:none; margin: 0.5em 0;"></div> 91 </td> 173 <th></th> 174 <td></td> 175 <td></td> 176 <td></td> 92 177 </tr> 93 178 </table> 179 </div> 180 181 <div class="absolutefull" style="top: 4em; left: 10em;"> 182 <div class="columnsFrom" style="height: 1.5em; white-space: nowrap; overflow: hidden;"> 183 <span id="targetPath"></span><span id="childPath" class="childitem" style="display: none;"></span> 184 <label style="font-weight: normal;"><input type="checkbox" id="showAllAnnotationTypes" 185 name="showAllAnnotationTypes">Show annotations for all subtypes</label> 186 </div> 187 <table id="columnstable" class="columnstable" style="width: 100%;"> 188 <thead class="bg-filled-100 topborder bottomborder"> 189 <tr> 190 <th style="width: 33%;">Columns</th> 191 <th style="width: 33%;">Annotations</th> 192 <th style="width: 34%;">Extensions</th> 193 </tr> 194 </thead> 195 <tbody> 196 <tr> 197 <td id="columns"></td> 198 <td id="annotations"></td> 199 <td id="extensions"></td> 200 </tr> 201 </tbody> 202 </table> 203 </div> 204 205 <div id="added-column-msg" class="messagecontainer note" style="display:none;">message</div> 206 94 207 </div> 95 208 </form> -
trunk/www/common/columns/ajax.jsp
r7954 r8083 35 35 import="net.sf.basedb.core.RawDataType" 36 36 import="net.sf.basedb.core.plugin.GuiContext" 37 import="net.sf.basedb.core.SyncFilter.SourceItemTransform" 37 38 import="net.sf.basedb.util.Values" 38 39 import="net.sf.basedb.util.formatter.Formatter" 40 import="net.sf.basedb.util.formatter.ToStringFormatter" 39 41 import="net.sf.basedb.util.error.ThrowableUtil" 42 import="net.sf.basedb.util.extensions.ActionIterator" 40 43 import="net.sf.basedb.util.extensions.ExtensionsInvoker" 44 import="net.sf.basedb.util.listable.ListableUtil" 45 import="net.sf.basedb.util.listable.SourceItemTransformerFactory" 41 46 import="net.sf.basedb.core.query.Restrictions" 42 47 import="net.sf.basedb.core.query.Expressions" … … 45 50 import="net.sf.basedb.clients.web.Base" 46 51 import="net.sf.basedb.clients.web.WebException" 52 import="net.sf.basedb.clients.web.util.HTML" 47 53 import="net.sf.basedb.clients.web.extensions.JspContext" 48 54 import="net.sf.basedb.clients.web.extensions.ExtensionsControl" 49 55 import="net.sf.basedb.clients.web.extensions.list.ListColumnAction" 50 56 import="net.sf.basedb.clients.web.extensions.list.ListColumnUtil" 57 import="net.sf.basedb.clients.web.extensions.list.RelatedItemExtensionColumn" 58 import="net.sf.basedb.clients.web.extensions.list.RelatedItemColumn.Specification" 51 59 import="org.json.simple.JSONObject" 52 60 import="org.json.simple.JSONArray" … … 72 80 { 73 81 dc = sc.newDbControl(":Get subtypes and annotation types"); 74 82 83 Item sourceItemType = Item.valueOf(request.getParameter("sourceItemType")); 84 String direction = Values.getString(request.getParameter("direction"), "PARENT"); 85 86 JSONArray jsonAllowedChildTypes = new JSONArray(); 87 json.put("childTypes", jsonAllowedChildTypes); 88 if ("PARENT".equals(direction)) 89 { 90 SourceItemTransformerFactory factory = ListableUtil.getTransformerFactory(itemType); 91 List<Item> allowedChildItems = factory.getSupportedSourceItems(SourceItemTransform.CHILD_TO_PARENT); 92 for (Item childType : allowedChildItems) 93 { 94 JSONObject jsonCt = new JSONObject(); 95 jsonCt.put("id", childType.name()); 96 jsonCt.put("name", childType.toString()); 97 jsonAllowedChildTypes.add(jsonCt); 98 } 99 } 100 75 101 JSONArray jsonSubtypes = new JSONArray(); 76 102 json.put("subtypes", jsonSubtypes); … … 109 135 json.put("annotationTypes", jsonAnnotationTypes); 110 136 137 Specification spec = Specification.parse(sourceItemType, "/"+direction+"/"+itemType.name()+"/*/*"); 138 GuiContext guiContext = GuiContext.list(itemType, RelatedItemExtensionColumn.SUBCONTEXT); 139 JspContext jspContext = ExtensionsControl.createContext(dc, pageContext, guiContext, spec); 140 ExtensionsInvoker<ListColumnAction<BasicItem,?>> columnsInvoker = ListColumnUtil.useExtensions(jspContext); 141 ActionIterator<ListColumnAction<BasicItem,?>> it = columnsInvoker.iterate(); 142 JSONArray jsonExtensions = new JSONArray(); 143 while (it.hasNext()) 144 { 145 ListColumnAction<BasicItem,?> col = it.next(); 146 JSONObject jsonXt = new JSONObject(); 147 jsonXt.put("id", it.getExtension().getId()); 148 jsonXt.put("columnId", col.getId()); 149 jsonXt.put("name", HTML.stripMarkup(col.getTitle())); 150 jsonXt.put("nameHTML", col.getTitle()); 151 jsonXt.put("tooltip", col.getTooltip()); 152 jsonExtensions.add(jsonXt); 153 } 154 json.put("extensionColumns", jsonExtensions); 111 155 } 112 156 else if ("GetAnnotationTypesForCategory".equals(cmd)) … … 128 172 } 129 173 } 174 130 175 json.put("annotationTypesCategory", jsonAnnotationTypes); 131 176 } 132 177 else if ("GetLazyItemColumns".equals(cmd)) 133 178 { 134 String subContext = Values.getString OrNull(request.getParameter("subcontext"));179 String subContext = Values.getString(request.getParameter("subcontext")); 135 180 GuiContext guiContext = GuiContext.list(itemType, subContext); 181 ItemContext cc = sc.getCurrentContext(itemType, subContext); 136 182 Integer[] items = Values.getInt(request.getParameter("items").split(",")); 183 int remaining = Values.getInt(request.getParameter("remaining"), -1); 137 184 dc = sc.newDbControl(":Get lazy columns"); 138 185 139 186 JspContext jspContext = ExtensionsControl.createContext(dc, pageContext, guiContext, null); 187 jspContext.loadAttributes(cc, "lazy-related-items-attributes"); 188 if (remaining == 0) cc.removeObject("lazy-related-items-attributes"); 189 140 190 jspContext.setAttribute("lazy-loading", false); 141 191 ExtensionsInvoker<ListColumnAction<BasicItem, ?>> columnsInvoker = ListColumnUtil.useExtensions(jspContext); … … 159 209 @SuppressWarnings("unchecked") 160 210 Formatter<Object> f = (Formatter<Object>)col.getFormatter(); 211 if (f == null) f = new ToStringFormatter<>(); 161 212 jsonCols.add(f.format(col.getValue(dc, item))); 162 213 } -
trunk/www/common/columns/configure.js
r7982 r8083 257 257 var url = 'add_relateditem_column.jsp?ID='+App.getSessionId(); 258 258 url += '&item_type='+Data.get('page-data', 'item-type'); 259 Dialogs.openPopup(url, 'SelectRelatedItemColumn', 600, 400);259 Dialogs.openPopup(url, 'SelectRelatedItemColumn', 750, 500); 260 260 } 261 261 -
trunk/www/filemanager/files/list_files.jsp
r7983 r8083 1084 1084 list="<%=loader.getValues()%>" 1085 1085 suffix="<%=loader.getUnitSymbol()%>" 1086 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"1086 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 1087 1087 /><% 1088 1088 } -
trunk/www/include/scripts/lazy-items.js
r7982 r8083 41 41 lazy.initLazyItems = function() 42 42 { 43 lazyElements = document. getElementsByClassName('lazy-item');43 lazyElements = document.querySelectorAll('.lazy-item'); 44 44 if (lazyElements.length > 0) 45 45 { … … 78 78 if (subContext) url += '&subcontext='+encodeURIComponent(subContext); 79 79 url += '&items='+lazyItems.join(','); 80 url += '&remaining='+(lazyElements.length-lazyNo); 80 81 request.open("GET", url, true); 81 82 request.send(null); … … 110 111 for (var col = 0; col < item.data.length; col++) 111 112 { 112 var e = Doc.element('lazy-item-'+col+'-'+itemId) ;113 var e = Doc.element('lazy-item-'+col+'-'+itemId).parentNode; 113 114 if (e) 114 115 { 115 116 e.innerHTML = item.data[col]; 116 Doc.removeClass(e, 'loading');117 Doc.removeClass(e, 'loaded');118 117 Doc.autoInitElements(e); 119 118 } -
trunk/www/include/styles/main.css
r7993 r8083 210 210 211 211 /* Generic button, standalone or in a toolbar, define colors only */ 212 .button, .tab 212 .button, .tab, ::file-selector-button 213 213 { 214 214 background-color: #E8E8E8; … … 223 223 224 224 .interactable:hover, .interactable:focus, .interactable.active, 225 input:hover, textarea:hover, select:hover, input:focus, textarea:focus, select:focus 225 input:hover, textarea:hover, select:hover, input:focus, textarea:focus, select:focus, ::file-selector-button:hover 226 226 { 227 227 border-color: #2288AA; … … 253 253 254 254 /* A single standalone button */ 255 .basicbutton 255 .basicbutton, ::file-selector-button 256 256 { 257 257 display: inline-block; … … 265 265 } 266 266 267 /* We need some additional styling for resetting */ 268 ::file-selector-button 269 { 270 border-style: solid; 271 border-color: inherit; 272 margin-right: 4px; 273 padding-top: 2px; 274 } 275 267 276 .buttongroup .basicbutton 268 277 { … … 277 286 278 287 /* Highlight the button when the mouse is over it */ 279 .basicbutton:hover, .basicbutton:focus 288 .basicbutton:hover, .basicbutton:focus, ::file-selector-button:hover 280 289 { 281 290 /* 1+1=0+2 so that the button is not moving */ … … 284 293 cursor: pointer; 285 294 } 295 296 ::file-selector-button:hover 297 { 298 /* Since we have 4px without hover */ 299 margin-right: 3px; 300 } 301 286 302 287 303 .basicbutton.square … … 727 743 { 728 744 background-color: #F8F8E8 !important; 745 color: inherit !important; 729 746 border-top: 1px solid #2288AA !important; 730 747 border-bottom: 1px solid #2288AA !important; … … 737 754 { 738 755 background-color: #F8F8E8 !important; 756 color: inherit !important; 739 757 border-top: 1px solid #2288AA !important; 740 758 border-bottom: 1px solid #2288AA !important; -
trunk/www/include/styles/table.css
r8026 r8083 260 260 } 261 261 262 .itemlist div.data th.relateditemcol .parentitem::before262 .itemlist div.data th.relateditemcol span.parentitem::before 263 263 { 264 264 content: url('../../images/parent-item.png'); 265 } 266 267 .itemlist div.data th.relateditemcol.childitem::before 265 margin: 0 1px; 266 vertical-align: -1px; 267 } 268 269 .itemlist div.data th.relateditemcol span.childitem::before 268 270 { 269 271 content: url('../../images/child-item.png'); 272 margin: 0 1px; 273 vertical-align: -1px; 270 274 } 271 275 -
trunk/www/lims/arraybatches/list_batches.jsp
r7982 r8083 591 591 list="<%=loader.getValues()%>" 592 592 suffix="<%=loader.getUnitSymbol()%>" 593 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"593 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 594 594 /><% 595 595 } -
trunk/www/lims/arraydesigns/list_designs.jsp
r8026 r8083 759 759 list="<%=loader.getValues()%>" 760 760 suffix="<%=loader.getUnitSymbol()%>" 761 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"761 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 762 762 /><% 763 763 } -
trunk/www/lims/arrayslides/list_slides.jsp
r7982 r8083 576 576 list="<%=loader.getValues()%>" 577 577 suffix="<%=loader.getUnitSymbol()%>" 578 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"578 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 579 579 /><% 580 580 } -
trunk/www/lims/plates/list_plates.jsp
r7982 r8083 651 651 list="<%=loader.getValues()%>" 652 652 suffix="<%=loader.getUnitSymbol()%>" 653 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"653 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 654 654 /><% 655 655 } -
trunk/www/lims/plates/wells/list_wells.jsp
r7982 r8083 652 652 list="<%=loader.getValues()%>" 653 653 suffix="<%=loader.getUnitSymbol()%>" 654 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"654 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 655 655 /><% 656 656 } -
trunk/www/my_base/projects/list_projects.jsp
r7982 r8083 472 472 list="<%=loader.getValues()%>" 473 473 suffix="<%=loader.getUnitSymbol()%>" 474 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"474 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 475 475 /><% 476 476 } -
trunk/www/views/derivedbioassays/list_bioassays.jsp
r7982 r8083 818 818 list="<%=loader.getValues()%>" 819 819 suffix="<%=loader.getUnitSymbol()%>" 820 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"820 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 821 821 /><% 822 822 } -
trunk/www/views/experiments/bioassays/list_bioassays.jsp
r7982 r8083 565 565 list="<%=loader.getValues()%>" 566 566 suffix="<%=loader.getUnitSymbol()%>" 567 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"567 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 568 568 /><% 569 569 } -
trunk/www/views/experiments/bioassaysets/analysis_tree.jsp
r7982 r8083 846 846 list="<%=loader.getValues()%>" 847 847 suffix="<%=loader.getUnitSymbol()%>" 848 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"848 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 849 849 /><% 850 850 } -
trunk/www/views/experiments/list_experiments.jsp
r7982 r8083 637 637 list="<%=loader.getValues()%>" 638 638 suffix="<%=loader.getUnitSymbol()%>" 639 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"639 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 640 640 /><% 641 641 } -
trunk/www/views/itemlists/list_lists.jsp
r7982 r8083 617 617 list="<%=loader.getValues()%>" 618 618 suffix="<%=loader.getUnitSymbol()%>" 619 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"619 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 620 620 /><% 621 621 } -
trunk/www/views/itemlists/members/list_members.jsp
r7982 r8083 607 607 list="<%=loader.getValues()%>" 608 608 suffix="<%=loader.getUnitSymbol()%>" 609 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"609 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 610 610 /><% 611 611 } -
trunk/www/views/physicalbioassays/list_bioassays.jsp
r7982 r8083 766 766 list="<%=loader.getValues()%>" 767 767 suffix="<%=loader.getUnitSymbol()%>" 768 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"768 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 769 769 /><% 770 770 } -
trunk/www/views/rawbioassays/list_rawbioassays.jsp
r7982 r8083 875 875 list="<%=loader.getValues()%>" 876 876 suffix="<%=loader.getUnitSymbol()%>" 877 clazz="<%=psInfo. overridesDefaultAnnotation() ? "ps-annotation" : null%>"877 clazz="<%=psInfo.hasProjectSpecificAnnotation() ? "ps-annotation" : null%>" 878 878 /><% 879 879 }
Note: See TracChangeset
for help on using the changeset viewer.