source: trunk/www/common/overview/info.jsp @ 7762

Last change on this file since 7762 was 7762, checked in by Nicklas Nordborg, 3 years ago

References #2199: Implement a "File viewer" extension point

Implemented in the item overview.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Id
File size: 20.6 KB
Line 
1<%-- $Id: info.jsp 7762 2019-11-28 06:40:54Z nicklas $
2  ------------------------------------------------------------------
3  Copyright (C) 2009 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<%@ page pageEncoding="UTF-8" session="false"
23  import="net.sf.basedb.core.SessionControl"
24  import="net.sf.basedb.core.DbControl"
25  import="net.sf.basedb.core.Project"
26  import="net.sf.basedb.core.Item"
27  import="net.sf.basedb.core.BasicItem"
28  import="net.sf.basedb.core.Nameable"
29  import="net.sf.basedb.core.Annotatable"
30  import="net.sf.basedb.core.AnnotationSet"
31  import="net.sf.basedb.core.AnnotationType"
32  import="net.sf.basedb.core.Annotation"
33  import="net.sf.basedb.core.ItemSubtype"
34  import="net.sf.basedb.core.Subtypable"
35  import="net.sf.basedb.core.AnyToAny"
36  import="net.sf.basedb.core.Protocol"
37  import="net.sf.basedb.core.Unit"
38  import="net.sf.basedb.core.FileStoreEnabled"
39  import="net.sf.basedb.core.FileSetMember"
40  import="net.sf.basedb.core.File"
41  import="net.sf.basedb.core.DataFileType"
42  import="net.sf.basedb.core.ItemContext"
43  import="net.sf.basedb.core.Permission"
44  import="net.sf.basedb.core.PermissionDeniedException"
45  import="net.sf.basedb.core.ItemNotFoundException"
46  import="net.sf.basedb.core.snapshot.SnapshotManager"
47  import="net.sf.basedb.core.snapshot.AnnotationSetSnapshot"
48  import="net.sf.basedb.core.snapshot.AnnotationSnapshot"
49  import="net.sf.basedb.core.snapshot.AnnotationTypeFilter"
50  import="net.sf.basedb.core.snapshot.AnnotationFilter"
51  import="net.sf.basedb.clients.web.Base"
52  import="net.sf.basedb.clients.web.util.HTML"
53  import="net.sf.basedb.clients.web.extensions.section.SectionAction"
54  import="net.sf.basedb.clients.web.extensions.JspContext"
55  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
56  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
57  import="net.sf.basedb.util.Values"
58  import="net.sf.basedb.util.NestedIterator"
59  import="net.sf.basedb.util.units.UnitConverter"
60  import="net.sf.basedb.util.formatter.Formatter"
61  import="net.sf.basedb.util.overview.GenericOverview"
62  import="net.sf.basedb.util.overview.OverviewUtil"
63  import="net.sf.basedb.util.overview.Node"
64  import="net.sf.basedb.util.overview.ValidationOptions"
65  import="net.sf.basedb.util.overview.Validator"
66  import="net.sf.basedb.util.overview.Severity"
67  import="net.sf.basedb.util.overview.Failure"
68  import="net.sf.basedb.util.overview.Fix"
69  import="net.sf.basedb.util.overview.filter.NodeTypeFilter"
70  import="net.sf.basedb.util.overview.extensions.ValidationRuleAction"
71  import="net.sf.basedb.util.extensions.ExtensionsInvoker"
72  import="net.sf.basedb.clients.web.extensions.fileviewer.FileViewerContext"
73  import="net.sf.basedb.clients.web.extensions.fileviewer.FileViewerUtil"
74  import="java.util.ArrayList"
75  import="java.util.Date"
76  import="java.util.List"
77  import="java.util.LinkedList"
78  import="java.util.Iterator"
79  import="java.util.Collection"
80  import="java.util.Collections"
81%>
82<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
83<%@ taglib prefix="t" uri="/WEB-INF/tab.tld" %>
84<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
85<%@ taglib prefix="ext" uri="/WEB-INF/extensions.tld" %>
86<%!
87private String getPath(DbControl dc, String ID, Node node)
88{
89  Node parent = node.getParent();
90  if (parent == null) return "";
91  String path = getPath(dc, ID, parent);
92  if (parent.getNodeType() == Node.Type.FOLDER)
93  {
94    //path += "/" + HTML.encodeTags(parent.getTitle());
95  }
96  else
97  {
98    BasicItem item = parent.getItem();
99    if (path.length() > 0)
100    {
101      path += " <b>›</b> ";
102    }
103    path += Base.getLink(ID, parent.getTitle(), item.getType(), item.getId(), item.hasPermission(Permission.WRITE));
104   
105    if (item instanceof Subtypable)
106    {
107      ItemSubtype subtype = ((Subtypable)parent.getItem(dc)).getItemSubtype();
108      if (subtype != null)
109      {
110        path += " <span class=\"itemsubtype\">(" + Base.getLinkedName(ID, subtype, false, true) + ")</span>";
111      }
112    }
113  }
114  return path;
115}
116%>
117<%
118final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
119final String ID = sc.getId();
120final float scale = Base.getScale(sc);
121final DbControl dc = sc.newDbControl();
122try
123{
124  final GenericOverview overview = OverviewUtil.getCurrentOverview(sc);
125  final ItemContext cc = sc.getCurrentContext(overview.getRootItem().getType());
126  final SnapshotManager snapshotManager = new SnapshotManager();
127  final AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter();
128  final AnnotationFilter annotationFilter = new AnnotationFilter();
129  ValidationOptions options = overview.getValidationOptions();
130  String nodeId = request.getParameter("nodeId");
131  Boolean showFailures = Values.getBoolean(request.getParameter("show_failures"), false);
132  Node node = overview.getNode(nodeId);
133  if (node == null) node = overview.getRootNode();
134
135  JspContext jspContext = ExtensionsControl.createContext(dc, pageContext, null, node);
136  ExtensionsInvoker<SectionAction> invoker = ExtensionsControl.useExtensions(
137      jspContext, "net.sf.basedb.clients.web.overview.info-details");
138 
139  BasicItem item = node.getItem(dc);
140  List<Failure> failures = overview.getFailures();
141  if (failures == null) failures = new LinkedList<Failure>();
142 
143  Formatter<Date> dateTimeFormatter = FormatterFactory.getDateTimeFormatter(sc);
144
145  FileViewerContext fileContext = null;
146  FileViewerUtil fileViewer = null;
147  if (item instanceof FileSetMember)
148  {
149    Node parentNode = node.getFirstParent(new NodeTypeFilter(Node.Type.ITEM));
150    if (parentNode != null)
151    {
152      fileContext = FileViewerContext.dataFile(parentNode.getItem());
153    }
154  }
155  else if (item instanceof AnyToAny)
156  {
157    if (((AnyToAny)item).getToType() == Item.FILE)
158    {
159      Node parentNode = node.getFirstParent(new NodeTypeFilter(Node.Type.ITEM));
160      if (parentNode != null)
161      {
162        fileContext = FileViewerContext.anyToAny(parentNode.getItem());
163      }
164    }
165  }
166  if (fileContext != null)
167  {
168    fileViewer = new FileViewerUtil(jspContext, fileContext);
169  }
170  %>
171  <base:page title="" type="iframe">
172  <base:head styles="table.css" scripts="dragdrop.js,~info.js">
173    <ext:scripts context="<%=jspContext%>" />
174    <ext:stylesheets context="<%=jspContext%>" />
175  <style>
176  .itemsubtype .link
177  {
178    color: #777777;
179  }
180  .edit-annotation
181  {
182    float: right;
183  }
184  </style>
185  </base:head>
186
187    <base:body>
188      <div class="absolutefull auto-init" data-auto-init="drag-support">
189      <div>
190      <table class="fullform bottomborder outlined">
191      <tr>
192        <th>Path</th>
193        <td><%=getPath(dc, ID, node)%></td>
194      </tr>
195      <%
196      if (item instanceof FileSetMember)
197      {
198        FileSetMember member = null;
199        boolean notFound = false;
200        try
201        { 
202          member = FileSetMember.getById(dc, item.getId());
203        }
204        catch (ItemNotFoundException ex)
205        {
206          notFound = true;
207        }
208        File file = null;
209        DataFileType dft = null;
210        boolean deniedFile = false;
211        boolean deniedFileType = false;
212        if (member != null)
213        {
214          try
215          {
216            file = member.getFile();
217          }
218          catch (PermissionDeniedException ex)
219          {
220            deniedFile = true;
221          }
222          try
223          {
224            dft = member.getDataFileType();
225          }
226          catch (PermissionDeniedException ex)
227          {
228            deniedFileType = true;
229          }
230        }
231        if (dft != null || file != null)
232        {
233          if (file != null) fileContext.setCurrentDataFile(file, dft, member);
234          %>
235          <tr>
236            <th><%=dft == null ? "Data file" : HTML.encodeTags(dft.getName())%></th>
237            <td>
238              <%=Base.getLinkedName(ID, file, deniedFile, true)%>
239              <%if (file != null) fileViewer.render(); %>
240            </td>
241          </tr>
242          <%
243        }
244        else
245        {
246          %>
247          <tr>
248          <th><%=HTML.encodeTags(node.getTitle())%></th>
249          <td>
250            <%=notFound ? "<i>- not found -</i>" : "<i>- denied -</i>"%>
251          </td>
252          </tr>
253          <%
254        }
255      }
256      else if (item instanceof AnyToAny)
257      {
258        AnyToAny any = AnyToAny.getById(dc, item.getId());
259        String linkTo = null;
260        try
261        {
262          BasicItem to = any.getTo();
263          String name = to instanceof Nameable ? ((Nameable)to).getName() : to.toString();
264          linkTo = Base.getLink(ID, HTML.encodeTags(name), 
265              to.getType(), to.getId(), to.hasPermission(Permission.WRITE));
266          if (fileViewer != null)
267          {
268            fileContext.setCurrentAnyToAny((File)to, any);
269            linkTo += " " + fileViewer.renderAsString();
270          }
271        }
272        catch (PermissionDeniedException ex)
273        {
274          linkTo = any.getToType() + "[id=" + any.getToId() + "] (<i>denied</i>)";
275        }
276        catch (ItemNotFoundException ex)
277        {
278          linkTo = any.getToType() + "[id=" + any.getToId() + "] (<i>missing</i>)";
279        }
280        %>
281        <tr>
282          <th>Any-to-any link</th>
283          <td>
284            <table>
285            <tr>
286              <td><%=Base.getLinkedName(ID, any, false, true) %></td>
287              <td><base:icon image="bullet.png" /></td>
288              <td><%=linkTo%></td>
289            </tr>
290            </table>
291          </td>
292        </tr>
293        <%
294      }
295      else
296      {
297        %>
298        <tr>
299          <th><%=node.getNodeType() == Node.Type.FOLDER ? 
300              "Folder" : item != null ? item.getType().toString() : "Item"%></th>
301          <td>
302            <%
303            if (item != null)
304            {
305              %>
306              <%=Base.getLink(ID, node.getTitle(), item.getType(),item.getId(), item.hasPermission(Permission.WRITE))%>
307              <%
308              if (item instanceof Subtypable)
309              {
310                ItemSubtype subtype = ((Subtypable)item).getItemSubtype();
311                if (subtype != null)
312                {
313                  %>
314                  <span class="itemsubtype">(<%=Base.getLinkedName(ID, subtype, false, true)%>)</span>
315                  <%
316                }
317              }
318            }
319            else
320            {
321              %>
322              <%=HTML.encodeTags(node.getTitle())%>
323              <%
324            }
325            %>
326          </td>
327        </tr>
328        <%
329      }
330      %>
331      <tr>
332        <th>Errors (children)</th>
333        <%
334        if (showFailures)
335        {
336        %>
337          <td><%=node.getNumErrors()+node.getChildErrors()%> (<%=node.getChildErrors()%>)</td>
338        <%
339        }
340        else
341        {       
342        %>
343          <td><i>- n/a -</i></td>
344        <%
345        }
346        %>
347      </tr>
348      <tr>
349        <th>Warnings (children)</th>
350        <%
351        if (showFailures)
352        {
353        %>
354          <td><%=node.getNumWarnings()+node.getChildWarnings()%> (<%=node.getChildWarnings()%>)</td>
355        <%
356        }
357        else
358        {       
359        %>
360          <td><i>- n/a -</i></td>
361        <%
362        }
363        %>
364      </tr>
365      <%
366      if (item instanceof Nameable)
367      {
368        Nameable nameable = (Nameable)item;
369        %>
370        <tr>
371          <th>Description</th>
372          <td><%=HTML.niceFormat(nameable.getDescription())%></td>
373        </tr>
374        <%
375      }
376      %>
377      </table>
378      </div>
379      <%
380      List<AnnotationSnapshot> annotations = new LinkedList<AnnotationSnapshot>();
381      String annotationsTitle = "";
382      Annotatable annotatable = null;
383      if (item instanceof Annotation)
384      {
385        Annotation a = Annotation.getById(dc, item.getId());
386        AnnotationSetSnapshot snapshot = snapshotManager.getSnapshot(dc, a.getAnnotationSet().getId());
387        annotationFilter.setAnnotation(a);
388        annotations.addAll(snapshotManager.findAnnotations(dc, snapshot, annotationFilter, true));
389       
390        // Find the annotatable parent node
391        Node parentNode = node;
392        while (parentNode != null)
393        {
394          BasicItem parentItem = parentNode.getItem();
395          if (parentItem instanceof Annotatable)
396          {
397            annotatable = (Annotatable)parentItem;
398            break;
399          }
400          parentNode = parentNode.getParent();
401        }
402        annotationsTitle = "Annotation values";
403      }
404      else if (item instanceof AnnotationType)
405      {
406        // Find the annotatable parent
407        Node parentNode = node;
408        while (parentNode != null)
409        {
410          BasicItem parentItem = parentNode.getItem();
411          if (parentItem instanceof Annotatable && !(parentItem instanceof Protocol))
412          {
413            annotatable = (Annotatable)parentItem.getType().getById(dc, parentItem.getId());
414            if (annotatable.isAnnotated())
415            {
416              annotationTypeFilter.setAnnotationType((AnnotationType)item);
417              annotations.addAll(snapshotManager.findAnnotations(dc, annotatable, annotationTypeFilter, true));
418            }
419            annotationsTitle = "Annotation values";
420            break;
421          }
422          parentNode = parentNode.getParent();
423        }
424      }
425      else if (item instanceof Annotatable)
426      {
427        annotationsTitle = "Annotations &amp; protocol parameters";
428        annotatable = (Annotatable)item.getType().getById(dc, item.getId());
429        if (annotatable.isAnnotated())
430        {
431          annotations.addAll(snapshotManager.findAnnotations(dc, annotatable, null, true));
432        }
433       
434      }
435      if (annotations != null && annotations.size() > 0)
436      {
437        Collections.sort(annotations, AnnotationSnapshot.sortByAnnotationType(dc));
438        %>
439        <base:section
440          id="allAnnotations"
441          title="<%="Annotations &amp; parameters (" +  annotations.size() + ")"%>"
442          context="<%=cc %>">
443       
444        <tbl:table id="annotations">
445          <tbl:columndef id="annotation" title="Annotation/parameter" />
446          <tbl:columndef id="values" title="Values" />
447          <tbl:columndef 
448            id="item"
449            title="Parent item"
450          />
451          <tbl:columndef 
452            id="note"
453            title="Note"
454          />
455          <tbl:columndef
456            id="lastModified"
457            title="Last modified"
458            formatter="<%=dateTimeFormatter %>"
459          />
460          <tbl:data>
461            <tbl:headers>
462              <tbl:headerrow>
463                <tbl:columnheaders />
464              </tbl:headerrow>
465            </tbl:headers>
466            <tbl:rows>
467              <%
468              for (AnnotationSnapshot snapshot : annotations)
469              {
470                AnnotationType at = snapshot.getAnnotationType(dc);
471               
472                // Unit and formatter for the values
473                Formatter<Object> formatter = FormatterFactory.getTypeFormatter(sc, at.getValueType());
474                Unit unit = snapshot.getActualUnit(dc);
475                UnitConverter converter = null;
476                if (unit != null)
477                {
478                  converter = unit.getUnitConverter(at.getDefaultUnit());
479                  formatter = unit.getFormatter(formatter);
480                }
481                // Get values
482                List<?> values = snapshot.getActualValues(converter, at.getValueType());
483               
484                // Project-specific values
485                List<?> defaultValues = null;
486                boolean projectSpecific = snapshot.getProjectId() != 0;;
487                boolean overrideDefault = snapshot.getOverrideId() != 0;
488                if (overrideDefault)
489                {
490                  Annotation defAnn = Annotation.getById(dc, snapshot.getOverrideId());
491                  if (defAnn.getSource() == Annotation.Source.INHERITED)
492                  {
493                    defAnn = defAnn.getInheritedFrom();
494                  }
495                  defaultValues = defAnn.getValues();
496                }
497
498                // Inheritance information
499                Date lastModified = snapshot.getThisLastUpdate();
500                boolean isUpToDate = snapshot.isUpToDate();
501                AnnotationSnapshot inherited = snapshot.getInheritedFrom();
502                Annotatable inheritedFrom = null;
503                if (inherited != null)
504                {
505                  if (lastModified == null)
506                  {
507                    lastModified = inherited.getThisLastUpdate();
508                  }
509                  try
510                  {
511                    inheritedFrom = inherited.getThisItem(dc);
512                  }
513                  catch (Throwable t)
514                  {}
515                }
516               
517                String icon = at != null && at.isProtocolParameter() ? 
518                    "parameter.png" : "annotation.png";
519                boolean editPermission = snapshot.hasPermission(dc, Permission.WRITE);
520                %>
521                <tbl:row>
522                  <tbl:cell column="annotation"><base:icon image="<%=icon%>" />
523                    <%=Base.getLinkedName(ID, at, false, true)%>
524                  </tbl:cell>
525                  <tbl:cell column="values" clazz="<%=projectSpecific && defaultValues != null ? "cell ps-annotation" : "cell" %>">
526                    <%=Values.getString(values, ", ", true, formatter)%>
527                    <base:icon id="<%=Integer.toString(snapshot.getThisAnnotationId())%>" 
528                      image="<%=isUpToDate ? "edit.png" : "edit-outofsync.png"%>" 
529                      subclass="edit-annotation auto-init" 
530                      data-auto-init="edit-annotation"
531                      data-item-type="<%=annotatable.getType().name()%>" 
532                      data-item-id="<%=annotatable.getId()%>" 
533                      data-annotation-type-id="<%=at.getId()%>"
534                      data-annotation-id="<%=snapshot.getThisAnnotationId()%>"
535                      tooltip="Edit this annotation" visible="<%=editPermission %>"/>
536                  </tbl:cell>
537                  <tbl:cell column="item">
538                    <%
539                    if (inheritedFrom != null)
540                    {
541                      String ownerName = inheritedFrom instanceof Nameable ?
542                          ((Nameable)inheritedFrom).getName() : inheritedFrom.toString();
543                      %>
544                      <%=Base.getLink(ID, ownerName, inheritedFrom.getType(), inheritedFrom.getId(), inheritedFrom.hasPermission(Permission.WRITE)) %>
545                      <%
546                    }
547                    else if (snapshot.getSource() == Annotation.Source.CLONED)
548                    {
549                      %>
550                      <i>- missing -</i>
551                      <%
552                    }
553                    %>
554                  </tbl:cell>
555                  <tbl:cell column="note">
556                    <%=snapshot.getSource() == Annotation.Source.CLONED ? "Cloned" + (isUpToDate ? "; up-to-date" : "; modified") : "" %>
557                    <%
558                    if (projectSpecific)
559                    {
560                      %>
561                      <%=defaultValues == null || defaultValues.size() == 0 ? "No default value" : "Default value: " + Values.getString(defaultValues, ", ", true, formatter)%>
562                      <%
563                    }
564                    %>
565                  </tbl:cell>
566                  <tbl:cell column="lastModified" value="<%=lastModified %>" />
567                </tbl:row>
568                <%
569              }
570              %>
571            </tbl:rows>
572          </tbl:data>       
573        </tbl:table>
574        </base:section>
575        <%
576      }     
577      if (showFailures)
578      {
579        %>
580        <base:section
581          id="failures"
582          title="<%="Failure details (" + (node.getNumErrors() + node.getNumWarnings()) + ")"%>"
583          context="<%=cc %>"
584          >
585        <table>
586        <%
587        int numFailures = 0;
588        for (Failure failure : failures)
589        {
590          ValidationRuleAction validator = failure.getValidator();
591          Severity severity = options.getSeverity(validator);
592          if (failure.getNode() == node && severity != Severity.IGNORE)
593          {
594            numFailures++;
595            List<Fix> fixes = failure.getFixes();
596            %>
597            <tr>
598              <td><base:icon image="<%=severity == Severity.ERROR ? "error.png" : "warning.png"%>" style="padding-right: 4px;"/></td>
599              <td><b><%=HTML.encodeTags(failure.getMessage())%></b></td>
600            </tr>
601            <tr>
602              <td></td>
603              <td style="padding-bottom: 0.3em;">
604                <%=HTML.encodeTags(validator.getDescription())%><br>
605                <%
606                if (fixes != null && fixes.size() > 0)
607                {
608                  for (Fix fix : fixes)
609                  {
610                    if (fix == null) continue;
611                    BasicItem fixItem = fix.getItem();
612                    AnnotationType at = fix.getAnnotationType();
613                    Annotation a = fix.getAnnotation();
614                    boolean enabled = a != null ? 
615                      a.hasPermission(Permission.WRITE) : fixItem.hasPermission(Permission.WRITE);
616                    String autoInit = "";
617                    if (enabled)
618                    {
619                      if (fix.isAnnotationFix())
620                      {
621                        autoInit = "edit-annotation";
622                      }
623                      else if (fix.isDataFileFix())
624                      {
625                        autoInit = "edit-data-file";
626                      }
627                      else
628                      {
629                        autoInit = "edit-item";
630                      }
631                    }
632                    %>
633                    <base:icon image="fixit.png" 
634                      style="padding-bottom: 4px;" 
635                      id="<%=Integer.toString(fix.hashCode())%>"
636                      enabled="<%=enabled %>"
637                      subclass="auto-init"
638                      data-auto-init="<%=autoInit%>"
639                      data-item-type="<%=fixItem.getType().name()%>"
640                      data-item-id="<%=fixItem.getId()%>"
641                      data-annotation-type-id="<%=at != null ? at.getId() : ""%>"
642                      data-annotation-id="<%=a != null ? a.getId() : "" %>"
643                      tooltip="<%=enabled ? "Edit this item to fix the problem" : "You do not have permission to modify this item" %>"
644                    ><%=HTML.encodeTags(fix.getMessage())%></base:icon><br>
645                    <%
646                  }
647                }
648                %>
649              </td>
650            </tr>
651            <%
652          }
653        }
654        %>
655        </table>
656        <%
657        if (numFailures == 0)
658        {
659          %>
660          <div class="messagecontainer note">
661          No validation failures on this item
662          </div>
663          <%
664        }
665        %>
666        </base:section>
667        <%
668      }
669     
670      for (SectionAction section : invoker)
671      {
672        %>
673        <base:section 
674          id="<%=section.getId() %>" 
675          showclazz="<%=section.getShowClazz() %>"
676          hideclazz="<%=section.getHideClazz() %>"
677          style="<%=section.getStyle() %>"
678          contentstyle="<%=section.getContentStyle() %>"
679          title="<%=section.getTitle() %>"
680          context="<%=cc %>"
681          ><%=section.getContent() %></base:section>
682        <%
683      }
684      %>
685      </div>
686    </base:body>
687  </base:page>
688  <%
689}
690finally
691{
692  if (dc != null) dc.close();
693}
694%>
Note: See TracBrowser for help on using the repository browser.