source: branches/3.16-stable/www/views/itemlists/index.jsp @ 7800

Last change on this file since 7800 was 7800, checked in by Nicklas Nordborg, 2 years ago

Fixes #2208: Add option to create a private item list

File size: 21.6 KB
Line 
1<%-- $Id$
2  ------------------------------------------------------------------
3  Copyright (C) 2015 Nicklas Nordborg
4
5  This file is part of BASE - BioArray Software Environment.
6  Available at http://base.thep.lu.se/
7
8  BASE is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License
10  as published by the Free Software Foundation; either version 3
11  of the License, or (at your option) any later version.
12
13  BASE is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with BASE. If not, see <http://www.gnu.org/licenses/>.
20  ------------------------------------------------------------------
21
22  @author Nicklas
23--%>
24<%@ page pageEncoding="UTF-8" session="false"
25  import="net.sf.basedb.core.SessionControl"
26  import="net.sf.basedb.core.DbControl"
27  import="net.sf.basedb.core.Item"
28  import="net.sf.basedb.core.Type"
29  import="net.sf.basedb.core.Include"
30  import="net.sf.basedb.core.ItemList"
31  import="net.sf.basedb.core.ItemList.SynchronizeOption"
32  import="net.sf.basedb.core.RawDataTypes"
33  import="net.sf.basedb.core.ItemSubtype"
34  import="net.sf.basedb.core.ItemQuery"
35  import="net.sf.basedb.core.Permission"
36  import="net.sf.basedb.core.ItemContext"
37  import="net.sf.basedb.core.SyncFilter"
38  import="net.sf.basedb.core.SyncFilter.SourceItemTransform"
39  import="net.sf.basedb.core.ItemResultIterator"
40  import="net.sf.basedb.core.ItemQuery"
41  import="net.sf.basedb.core.MultiPermissions"
42  import="net.sf.basedb.core.OwnedItem"
43  import="net.sf.basedb.core.Listable"
44  import="net.sf.basedb.core.SimpleProgressReporter"
45  import="net.sf.basedb.core.PermissionDeniedException"
46  import="net.sf.basedb.core.ItemAlreadyExistsException"
47  import="net.sf.basedb.core.query.Restrictions"
48  import="net.sf.basedb.core.query.Hql"
49  import="net.sf.basedb.core.query.Expressions"
50  import="net.sf.basedb.util.ChainedProgressReporter"
51  import="net.sf.basedb.util.RemovableUtil"
52  import="net.sf.basedb.util.ShareableUtil"
53  import="net.sf.basedb.util.OwnableUtil"
54  import="net.sf.basedb.clients.web.Base"
55  import="net.sf.basedb.clients.web.WebException"
56  import="net.sf.basedb.util.Values"
57  import="net.sf.basedb.clients.web.util.HTML"
58  import="net.sf.basedb.core.plugin.GuiContext"
59  import="net.sf.basedb.util.extensions.ExtensionsInvoker"
60  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
61  import="net.sf.basedb.clients.web.extensions.JspContext"
62  import="net.sf.basedb.clients.web.extensions.edit.EditUtil"
63  import="net.sf.basedb.clients.web.extensions.edit.OnSaveAction"
64  import="net.sf.basedb.clients.web.extensions.edit.OnSaveRenderer"
65  import="net.sf.basedb.clients.web.extensions.list.ListColumnExportRenderer"
66  import="net.sf.basedb.clients.web.extensions.list.ListColumnAction"
67  import="net.sf.basedb.clients.web.extensions.list.ListColumnUtil"
68  import="java.util.Enumeration"
69  import="java.util.Set"
70  import="java.util.HashSet"
71  import="java.util.List"
72  import="java.util.ArrayList"
73  import="java.util.Collections"
74  import="java.util.Arrays"
75%>
76<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
77<%!
78  private static final ItemContext defaultContext = Base.createDefaultContext("name", "name,memberType,size,description");
79  private static final Item itemType = Item.ITEMLIST;
80 
81  private static class ReSyncList
82    implements Runnable
83  {
84   
85    private final SessionControl sc;
86    private final int listId;
87    private final Integer[] depending;
88    private final SynchronizeOption syncOption;
89    private final boolean applySubtypeFilter;
90   
91    ReSyncList(ItemList list, Integer[] depending, SynchronizeOption syncOption, boolean applySubtypeFilter)
92    {
93      this.sc = list.getSessionControl();
94      this.listId = list.getId();
95      this.depending = depending;
96      this.syncOption = syncOption;
97      this.applySubtypeFilter = applySubtypeFilter;
98    }
99   
100    public void run()
101    {
102      SimpleProgressReporter progress = new SimpleProgressReporter(null);
103      ChainedProgressReporter master = null;
104      sc.setSessionSetting("progress.resync-itemlist-"+listId, progress);
105      DbControl dc = null;
106      try
107      {
108       
109        if (depending != null && depending.length > 0)
110        {
111          master = new ChainedProgressReporter(progress);
112          float step = 100f / (1+depending.length); // Divide 100% into equal parts for each list
113          int index = 0;
114       
115          for (int i = depending.length - 1; i >= 0; i--)
116          {
117            // Work backwards since the last ones are the "deepest" children
118            dc = sc.newDbControl();
119            ItemList list = ItemList.getById(dc, depending[i]);
120
121            master.setRange((int)(index*step), (int)((index+1)*step));
122            index++;
123            master.setMessagePrefix(list.getName() + ": ");
124           
125            list.resyncFilter(dc, syncOption, applySubtypeFilter, master);
126            dc.commit();
127          }
128         
129          master.setRange((int)(100-step), 100);
130          master.setMessagePrefix(null);
131        }
132       
133        dc = sc.newDbControl();
134        ItemList list = ItemList.getById(dc, listId);
135        if (master != null) master.setMessagePrefix(list.getName() + ": ");
136        int membersBefore = list.getSize();
137        int membersAfter = list.resyncFilter(dc, syncOption, applySubtypeFilter, master != null ? master : progress);
138        progress.display(99, "Committing to database...");
139        dc.commit();
140        progress.display(100, "List synchronized successfully; " + membersAfter + " members ("+membersBefore+")");
141      }
142      catch (Throwable t)
143      {
144        progress.display(100, t.getMessage());
145        progress.setError(t);
146        t.printStackTrace(System.out);
147      }
148      finally
149      {
150        if (dc != null) dc.close();
151      }
152    }
153  }
154%>
155<%
156final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
157final String ID = sc.getId();
158final String cmd = request.getParameter("cmd");
159final String root = request.getContextPath()+"/";
160final String mode = request.getParameter("mode");
161final String callback = request.getParameter("callback");
162final String itemId = request.getParameter("item_id");
163final String listPage = "list_lists.jsp?ID="+ID
164  +(mode == null ? "" : "&mode="+mode)
165  +(callback == null ? "" : "&callback="+callback)
166  +(itemId == null ? "" : "&item_id="+itemId);
167final String viewPage = "view_list.jsp?ID="+ID;
168final String editPage = "edit_list.jsp?ID="+ID;
169
170String forward = null;
171String redirect = null;
172String message = null;
173DbControl dc = null;
174
175try
176{
177  if (cmd == null || "List".equals(cmd))
178  {
179    // Display the list page without updatinging the current context
180    Base.getAndSetCurrentContext(sc, itemType, null, defaultContext, true);
181    redirect = listPage;
182  }
183  else if ("UpdateContext".equals(cmd))
184  {
185    // Display the list page after updating the current context from the request parameters
186    Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
187    redirect = listPage;
188  }
189  else if ("LoadContext".equals(cmd))
190  {
191    // Display the list page after loading a saved context
192    int contextId = Values.getInt(request.getParameter("context"));
193    Base.loadContext(sc, contextId, defaultContext);
194    redirect = listPage;
195  }
196
197  else if ("ViewItem".equals(cmd))
198  {
199    // Display the view page for a single item
200    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
201    forward = viewPage;
202  }
203  else if ("EditItem".equals(cmd))
204  {
205    // Display the edit page for a single item (should be opened in a popup)
206    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
207    redirect = editPage;
208  }
209  else if ("NewItem".equals(cmd))
210  {
211    // Display the edit page for a new item (should be opened in a popup)
212    if (!sc.hasPermission(Permission.CREATE, itemType))
213    {
214      throw new PermissionDeniedException(Permission.CREATE, itemType.toString());
215    }
216    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
217    cc.setId(0);
218    forward = editPage;
219  }
220  else if ("UpdateItem".equals(cmd))
221  {
222    // Update the properties on an item (will close the popup)
223    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, null, defaultContext);
224    dc = sc.newDbControl();
225    ItemList list = cc.getObject("item");
226   
227    if (list == null)
228    {
229      Item memberType = Item.valueOf(request.getParameter("memberType"));
230      list = ItemList.getNew(dc, memberType);
231      if (Values.getBoolean(request.getParameter("private")))
232      {
233        list.setProjectKey(null);
234        list.setItemKey(null);
235      }
236      dc.saveItem(list);
237     
238      int cloneId = Values.getInt(request.getParameter("clone_id"));
239      if (cloneId > 0)
240      {
241        ItemList cloneFrom = ItemList.getById(dc, cloneId);
242        list.copySyncFiltersAndMembers(dc, cloneFrom);
243      }
244      message = "List created";
245    }
246    else
247    {
248      dc.reattachItem(list, false);
249      message = "List updated";
250    }
251    JspContext jspContext = ExtensionsControl.createContext(dc, pageContext, GuiContext.item(Item.ITEMLIST), list);
252    ExtensionsInvoker<OnSaveAction> invoker = EditUtil.useOnSaveExtensions(jspContext);
253    try
254    {
255      list.setName(Values.getStringOrNull(request.getParameter("name")));
256      list.setExternalId(Values.getStringOrNull(request.getParameter("external_id")));
257      list.setDescription(Values.getStringOrNull(request.getParameter("description")));
258      list.setDisableManualMembers(Values.getBoolean(request.getParameter("disableManual"), false));
259      list.setDisableSyncFilters(Values.getBoolean(request.getParameter("disableSyncFilters"), false));
260      if (list.getMemberType() == Item.RAWBIOASSAY)
261      {
262        list.setRawDataType(RawDataTypes.getRawDataType(request.getParameter("rawdatatype")));
263      }
264      else
265      {
266        int subtypeId = Values.getInt(request.getParameter("subtype_id"), -1);
267        ItemSubtype subtype = null;
268        if (subtypeId >= 0) // < 0 = denied or unchanged
269        {
270          if (subtypeId > 0) subtype = ItemSubtype.getById(dc, subtypeId);
271          list.setItemSubtype(subtype);
272        }
273      }
274
275      // Annotations tab
276      Base.updateAnnotations(dc, list, list, request);
277
278      if (!list.isInDatabase())
279      {
280        // Add individual items from selected from a list page
281        String source = request.getParameter("source");
282        if (source != null)
283        {
284          String subContext = Values.getString(request.getParameter("subContext"), "");
285          boolean useSyncFilter = false;
286          ItemContext filterContext = sc.getCurrentContext(list.getMemberType(), subContext);
287          ItemQuery<? extends Listable> query = filterContext.getQuery();
288          if ("all".equals(source))
289          {
290            useSyncFilter = Values.getBoolean(request.getParameter("syncFilter")) && filterContext.getNumPropertyFilters() > 0;
291            query.setFirstResult(0);
292            query.setMaxResults(-1);
293            if (useSyncFilter)
294            {
295              SyncFilter sf = list.newSyncFilter(dc);
296              sf.setName("Origin");
297              sf.setSyncContext(filterContext, SourceItemTransform.NONE);
298              dc.saveItem(sf);
299             
300              list.resyncFilter(dc, SynchronizeOption.ADD_ONLY, false, null);
301            }
302          }
303          else if ("selected".equals(source))
304          {
305            query.setFirstResult(0);
306            query.setMaxResults(-1);
307            Integer[] itemIds = Values.getInt(request.getParameter("items").split(","));
308            query.restrict(
309              Restrictions.in(
310                Hql.property("id"),
311                Expressions.parameter("selectedItems")
312              )
313            );
314            query.setParameter("selectedItems", Arrays.asList(itemIds), Type.INT);     
315          }
316          // else -- no modifications to the query mean that we only get the current page
317         
318          if (!useSyncFilter) list.add(query.iterate(dc));
319        }
320       
321        message = "List created with " + list.getSize() + " item(s)";
322      }
323
324     
325      // OnSave extensions
326      invoker.render(OnSaveRenderer.ON_SAVE);
327      dc.commit();
328      invoker.render(OnSaveRenderer.ON_COMMIT);
329    }
330    catch (Exception ex)
331    {
332      invoker.render(OnSaveRenderer.onRollback(ex));
333      throw ex;
334    }
335    finally
336    {
337      cc.removeObject("item");
338    }
339  }
340  else if ("ReSync".equals(cmd))
341  {
342    dc = sc.newDbControl();
343    ItemList list = ItemList.getById(dc, Values.getInt(itemId));
344   
345    SynchronizeOption syncOption = SynchronizeOption.valueOf(request.getParameter("syncoption"));
346    boolean subtypeFilter = Values.getBoolean(request.getParameter("subtype"));
347    boolean rawDataTypeFilter = Values.getBoolean(request.getParameter("rawdatatype"));
348   
349    Integer[] depending = Values.getInt(request.getParameterValues("depending"));
350    Runnable resync = new ReSyncList(list, depending, syncOption, subtypeFilter || rawDataTypeFilter);
351    dc.commit();
352
353    Thread t = new Thread(resync);
354    t.start();
355    redirect = "../../common/progress_reporter.jsp?ID="+ID+
356      "&progress=resync-itemlist-"+list.getId()+"&notify_opener=btnResync&title=" + HTML.urlEncode("Synchronizing: " + list.getName());
357  }
358  else if ("DeleteItem".equals(cmd))
359  {
360    // Delete a single item and then return to the view page
361    dc = sc.newDbControl();
362    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
363    RemovableUtil.setRemoved(dc, itemType, Collections.singleton(cc.getId()), true);
364    dc.commit();
365    redirect = viewPage;
366  }
367  else if ("DeleteItems".equals(cmd))
368  {
369    // Delete all selected items on the list page
370    dc = sc.newDbControl();
371    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
372    int numTotal = cc.getSelected().size();
373    int numRemoved = RemovableUtil.setRemoved(dc, itemType, cc.getSelected(), true);
374    dc.commit();
375    if (numTotal != numRemoved)
376    {
377      message = (numRemoved == 0 ? "No" : "Only "+numRemoved+" of "+numTotal) + " items could be deleted, because you have no DELETE permission";
378    }
379    redirect = listPage;
380  }
381  else if ("RestoreItem".equals(cmd))
382  {
383    // Restore a single item and then return to the view page
384    dc = sc.newDbControl();
385    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
386    RemovableUtil.setRemoved(dc, itemType, Collections.singleton(cc.getId()), false);
387    dc.commit();
388    redirect = viewPage;
389  }
390  else if ("RestoreItems".equals(cmd))
391  {
392    // Restore all selected items on the list page
393    dc = sc.newDbControl();
394    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
395    int numTotal = cc.getSelected().size();
396    int numRemoved = RemovableUtil.setRemoved(dc, itemType, cc.getSelected(), false);
397    dc.commit();
398    if (numTotal != numRemoved)
399    {
400      message = (numRemoved == 0 ? "No" : "Only "+numRemoved+" of "+numTotal) + " items could be restored, because you have no WRITE permission";
401    }
402    redirect = listPage;
403  }
404  else if ("ShareItem".equals(cmd))
405  {
406    // Display a popup window for sharing a single item
407    dc = sc.newDbControl();
408    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
409    MultiPermissions permissions = ShareableUtil.getMultiPermissions(dc, itemType, Collections.singleton(cc.getId()));
410    dc.close();
411    cc.setObject("MultiPermissions", permissions);
412    redirect = "../../common/share/share.jsp?ID="+ID+"&item_type="+itemType.name();
413  }
414  else if ("ShareItems".equals(cmd))
415  {
416    // Display a popup window for sharing all selected items on the list page
417    dc = sc.newDbControl();
418    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
419    MultiPermissions permissions = ShareableUtil.getMultiPermissions(dc, itemType, cc.getSelected());
420    dc.close();
421    cc.setObject("MultiPermissions", permissions);
422    redirect = "../../common/share/share.jsp?ID="+ID+"&item_type="+itemType.name();
423  }
424  else if ("SetOwnerOfItem".equals(cmd))
425  {
426    // Change owner of items selected on a list page
427    dc = sc.newDbControl();
428    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
429    OwnedItem item = (OwnedItem)itemType.getById(dc, cc.getId());
430    cc.setObject("OwnedItems", Collections.singleton(item));
431    redirect = "../../common/ownership/ownership.jsp?ID="+ID+"&item_type="+itemType.name();
432  }
433  else if ("SetOwnerOfItems".equals(cmd))
434  {
435    // Change owner of items selected on a list page
436    dc = sc.newDbControl();
437    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
438    Set<OwnedItem> items = new HashSet<OwnedItem>();
439    for (Integer id : cc.getSelected())
440    {
441      if (id != null) items.add((OwnedItem)itemType.getById(dc, id));
442    }
443    dc.close();
444    cc.setObject("OwnedItems", items);
445    redirect = "../../common/ownership/ownership.jsp?ID="+ID+"&item_type="+itemType.name();
446  }
447  else if ("ExportItems".equals(cmd))
448  {
449    // Run an export plugin in a list context
450    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
451    final ItemQuery<ItemList> query = ItemList.getQuery();
452    dc = sc.newDbControl();
453    cc.configureQuery(dc, query, true);
454    dc.close();
455    cc.setQuery(query);
456    redirect = "../../common/export/index.jsp?ID="+ID+"&cmd=SelectPlugin&item_type="+itemType.name()+"&context_type=LIST&title=Export+item+lists";
457  }
458  else if ("ExportItem".equals(cmd))
459  {
460    // Run an export plugin in single-item context
461    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
462    redirect = "../../common/export/index.jsp?ID="+ID+"&cmd=SelectPlugin&item_type="+itemType.name()+"&context_type=ITEM&title=Export+item+list";
463  }
464  else if ("ImportItems".equals(cmd))
465  {
466    // Run an import plugin in a list context
467    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
468    final ItemQuery<ItemList> query = ItemList.getQuery();
469    dc = sc.newDbControl();
470    cc.configureQuery(dc, query, true);
471    cc.setQuery(query);
472    JspContext jspContext = ExtensionsControl.createContext(dc, pageContext, GuiContext.list(itemType), null);
473    ExtensionsInvoker<ListColumnAction<ItemList,?>> listInvoker = ListColumnUtil.useExtensions(jspContext);
474    listInvoker.render(new ListColumnExportRenderer<ItemList>(cc));
475    dc.close();
476    redirect = "../../common/import/index.jsp?ID="+ID+"&cmd=SelectPlugin&item_type="+itemType.name()+"&context_type=LIST&title=Import+item+lists";
477  }
478  else if ("ImportItem".equals(cmd))
479  {
480    // Run an import plugin in single-item context
481    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
482    redirect = "../../common/import/index.jsp?ID="+ID+"&cmd=SelectPlugin&item_type="+itemType.name()+"&context_type=ITEM&title=Import+item+list";
483  }
484  else if ("RunListPlugin".equals(cmd))
485  {
486    // Run another plugin in a list context
487    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
488    final ItemQuery<ItemList> query = ItemList.getQuery();
489    dc = sc.newDbControl();
490    cc.configureQuery(dc, query, true);
491    dc.close();
492    cc.setQuery(query);
493    redirect = "../../common/plugin/index.jsp?ID="+ID+"&cmd=SelectPlugin&item_type="+itemType.name()+"&context_type=LIST&main_type=OTHER&title=Run+plugin";
494  }
495  else if ("RunPlugin".equals(cmd))
496  {
497    // Run another plugin in single-item context
498    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
499    redirect = "../../common/plugin/index.jsp?ID="+ID+"&cmd=SelectPlugin&item_type="+itemType.name()+"&context_type=ITEM&main_type=OTHER&title=Run+plugin";
500  }
501  else if ("MergeItem".equals(cmd))
502  {
503    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, null, defaultContext);
504    dc = sc.newDbControl();
505    ItemList itemList = cc.getObject("item");
506    dc.reattachItem(itemList, false);
507   
508    String mergeType = request.getParameter("mergeType");
509    String sourceMerge = request.getParameter("sourceMerge");
510   
511    String[] sourceListIds = Values.getString(request.getParameter("+ITEMLIST")).split(",");
512    List<ItemList> sourceLists = new ArrayList<ItemList>(sourceListIds.length);
513    for (int i = 0; i < sourceListIds.length; ++i)
514    {
515      int listId = Values.getInt(sourceListIds[i], -1);
516      if (listId != -1) sourceLists.add(ItemList.getById(dc, listId));
517    }
518   
519    int numAdded = 0;
520    int numRemoved = 0;
521    if ("union".equals(mergeType))
522    {
523      if ("union".equals(sourceMerge))
524      {
525        numAdded = itemList.addUnion(sourceLists);
526      }
527      else if ("intersection".equals(sourceMerge))
528      {
529        numAdded = itemList.addIntersection(sourceLists);
530      }
531      message = numAdded + " items added to this list";
532    }
533    else if ("intersection".equals(mergeType))
534    {
535      if ("union".equals(sourceMerge))
536      {
537        numRemoved = itemList.retainUnion(sourceLists);
538      }
539      else if ("intersection".equals(sourceMerge))
540      {
541        numRemoved = itemList.retainIntersection(sourceLists);
542      }
543      message = numRemoved + " items removed from this list";
544    }
545    else if ("complement".equals(mergeType))
546    {
547      if ("union".equals(sourceMerge))
548      {
549        numRemoved = itemList.removeUnion(sourceLists);
550      }
551      else if ("intersection".equals(sourceMerge))
552      {
553        numRemoved = itemList.removeIntersection(sourceLists);
554      }
555      message = numRemoved + " items removed from this list";
556    }
557    dc.commit();
558  } 
559  else
560  {
561    throw new WebException("popup", "Invalid command", "The command {1} is not recognised as a valid command.", cmd);
562  }
563}
564finally
565{
566  if (dc != null) dc.close();
567}
568
569if (forward != null)
570{
571  sc.setSessionSetting("alert-message", message);
572  pageContext.forward(forward);
573}
574else if (redirect != null)
575{
576  sc.setSessionSetting("alert-message", message);
577  response.sendRedirect(redirect);
578}
579else if (message == null)
580{
581  response.sendRedirect(root + "common/close_popup.jsp?refresh_opener=1&wait=0");
582}
583else
584{
585  response.sendRedirect(root + "common/close_popup.jsp?refresh_opener=1&message="+HTML.urlEncode(message));
586}
587%>
588
Note: See TracBrowser for help on using the repository browser.