View Javadoc

1   /*
2    * $Source: /usr/cvsroot/melati/melati/src/main/java/org/melati/admin/Admin.java,v $
3    * $Revision: 1.137 $
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.admin;
47  
48  import java.util.Vector;
49  import java.util.Enumeration;
50  
51  import org.melati.Melati;
52  import org.melati.servlet.FormDataAdaptor;
53  import org.melati.servlet.InvalidUsageException;
54  import org.melati.servlet.Form;
55  import org.melati.servlet.TemplateServlet;
56  import org.melati.template.ServletTemplateContext;
57  import org.melati.template.FormParameterException;
58  
59  import org.melati.poem.AccessToken;
60  import org.melati.poem.AccessPoemException;
61  import org.melati.poem.BaseFieldAttributes;
62  import org.melati.poem.Capability;
63  import org.melati.poem.Column;
64  import org.melati.poem.ColumnInfo;
65  import org.melati.poem.ColumnInfoTable;
66  import org.melati.poem.ColumnTypePoemType;
67  import org.melati.poem.Database;
68  import org.melati.poem.DeletionIntegrityPoemException;
69  import org.melati.poem.DisplayLevel;
70  import org.melati.poem.ExecutingSQLPoemException;
71  import org.melati.poem.Field;
72  import org.melati.poem.Initialiser;
73  import org.melati.poem.Persistent;
74  import org.melati.poem.PoemException;
75  import org.melati.poem.PoemThread;
76  import org.melati.poem.PoemType;
77  import org.melati.poem.PoemTypeFactory;
78  import org.melati.poem.ReferencePoemType;
79  import org.melati.poem.Setting;
80  import org.melati.poem.Table;
81  import org.melati.poem.TableInfo;
82  import org.melati.poem.TableInfoTable;
83  import org.melati.poem.ValidationPoemException;
84  
85  import org.melati.util.CountedDumbPagedEnumeration;
86  import org.melati.poem.util.EnumUtils;
87  import org.melati.poem.util.MappedEnumeration;
88  import org.melati.util.MelatiBugMelatiException;
89  import org.melati.util.MelatiRuntimeException;
90  
91  /**
92   * Melati template servlet for database administration.
93   * <p>
94   * This class defines {@link #doTemplateRequest(Melati, ServletTemplateContext)}
95   * and methods it calls to interpret requests, depending on the current table
96   * and object, if any.
97   * <p>
98   * Java methods with names ending "<code>Template</code>" and taking a
99   * {@link ServletTemplateContext} and {@link Melati} as arguments are generally
100  * called by {@link #doTemplateRequest(Melati, ServletTemplateContext)}) to
101  * implement corresponding request methods.
102  * {@link #modifyTemplate(ServletTemplateContext, Melati)} and associated
103  * methods are slight variations.
104  * <p>
105  * {@link #adminTemplate(ServletTemplateContext, String)} is called in all cases
106  * to return the template path. The name of the template is usually the same as
107  * the request method but not if the same template is used for more than one
108  * method or the template served depends on how request processing proceeds.
109  * <p>
110  * These methods are called to modify the context:
111  * <ul>
112  * <li>{@link #popupSelect(ServletTemplateContext, Melati)}</li>
113  * <li>{@link #primarySelect(ServletTemplateContext, Melati)}</li>
114  * <li>{@link #selection(ServletTemplateContext, Melati)}</li>
115  * </ul>
116  * 
117  * @todo Review working of where clause for dates
118  * @todo Move Nav icons into PrimarySelect
119  * @todo Make Top.login JS agnostic
120  * @todo Make Chooser JS agnostic
121  * @todo Make Navigation JS agnostic
122  * @todo Rename Left template to Table
123  * @FIXME primaryDisplayTable should not be static as this messes with DB switching
124  */
125 
126 public class Admin extends TemplateServlet {
127   private static final long serialVersionUID = 1L;
128 
129   private static String screenStylesheetURL = null;
130   private static String primaryDisplayTable = null;
131   private static String homepageURL = null;
132 
133   /**
134    * Creates a row for a table using field data in a template context.
135    */
136   protected static Persistent create(Table table,
137       final ServletTemplateContext context) {
138     Persistent result = table.create(new Initialiser() {
139       public void init(Persistent object) throws AccessPoemException,
140           ValidationPoemException {
141         Form.extractFields(context, object);
142       }
143     });
144     result.postEdit(true);
145     return result;
146   }
147 
148   /**
149    * Return the resource path for an admin template.
150    */
151   protected static final String adminTemplate(String name) {
152     return "org/melati/admin/" + name;
153   }
154 
155   /**
156    * @return a DSD for the database
157    */
158   protected static String dsdTemplate(ServletTemplateContext context) {
159     // Webmacro security prevents access from within template
160 
161     // Note: getPackage() can return null dependant upon
162     // the classloader so we have to chomp the class name
163 
164     String c = PoemThread.database().getClass().getName();
165     int dot = c.lastIndexOf('.');
166     String p = c.substring(0, dot);
167 
168     context.put("package", p);
169     return adminTemplate("DSD");
170   }
171 
172   /**
173    * @return primary select template
174    */
175   protected static String primarySelectTemplate(ServletTemplateContext context,
176       Melati melati) throws PoemException {
177     final Table table = melati.getTable();
178 
179     Field primaryCriterion;
180 
181     Column column = table.primaryCriterionColumn();
182     if (column != null) {
183       String sea = context.getForm("field_" + column.getName());
184       primaryCriterion = new Field(
185           sea == null ? 
186            (
187             melati.getObject() == null ? 
188                 null : column.getRaw(melati.getObject()))
189           : column.getType().rawOfString(sea), 
190           new BaseFieldAttributes(column,column.getType().withNullable(true)));
191     } else
192       primaryCriterion = null;
193 
194     context.put("primaryCriterion", primaryCriterion);
195     return adminTemplate("PrimarySelect");
196   }
197 
198 
199   /**
200    * Return template for a selection of records from a table.
201    */
202   protected static String selectionTemplate(ServletTemplateContext context,
203       Melati melati) {
204     String templateName = context.getForm("template");
205     if (templateName == null) {
206       selection(context, melati, true);
207       return adminTemplate("Selection");
208     } else { 
209       selection(context, melati, false);
210       return adminTemplate(templateName);
211     }
212   }
213 
214   /**
215    * Implements request to display a selection of records from a table in the
216    * right hand pane.
217    * 
218    * @return SelectionRight template.
219    */
220   protected static String selectionRightTemplate(
221       ServletTemplateContext context, Melati melati) {
222     selection(context, melati, true);
223     context.put("inRight", Boolean.TRUE);
224     return adminTemplate("Selection");
225   }
226 
227   /**
228    * Modifies the context in preparation for serving a template to view a
229    * selection of rows.
230    * <p>
231    * Any form fields in the context with names starting "field_" are assumed to
232    * hold values that must be matched in selected rows (if not null).
233    * <p>
234    * An encoding of the resulting whereClause is added to the context. "AND" is
235    * replaced by an &amp; separator.
236    * <p>
237    * A form field with name "start" is assumed to hold the number of the start
238    * row in the result set. The default is zero. The next 20 rows are selected
239    * and added as to the context as "results".
240    * 
241    * @return The modified context.
242    * @see #adminTemplate(ServletTemplateContext, String)
243    */
244   protected static ServletTemplateContext selection(
245       ServletTemplateContext context, Melati melati, boolean paged) {
246     final Table table = melati.getTable();
247 
248     final Database database = table.getDatabase();
249 
250     // sort out search criteria
251 
252     final Persistent criteria = table.newPersistent();
253 
254     Vector whereClause = new Vector();
255 
256     for (Enumeration c = table.columns(); c.hasMoreElements();) {
257       Column column = (Column) c.nextElement();
258       String name = "field_" + column.getName();
259       String fieldValue = Form.getFieldNulled(context, name);
260       if (fieldValue != null) {
261         column
262             .setRaw_unsafe(criteria, column.getType().rawOfString(fieldValue));
263 
264         // FIXME Needs to work for dates
265         whereClause.addElement(name + "=" + melati.urlEncode(fieldValue));
266       }
267     }
268 
269     context.put("whereClause", EnumUtils.concatenated("&", whereClause
270         .elements()));
271 
272     // sort out ordering 
273 
274     PoemType searchColumnsType = getSearchColumnsType(database, table);
275 
276     Vector orderings = new Vector();
277     Vector orderClause = new Vector();
278 
279     
280     for (int o = 1; o <= table.displayColumnsCount(DisplayLevel.summary); ++o) {
281       String name = "field_order-" + o;
282       String orderColumnIDString = Form.getFieldNulled(context, name);
283       Integer orderColumnID = null;
284 
285       if (orderColumnIDString != null) {
286         String toggleName = "field_order-" + o + "-toggle";
287         String orderColumnSortOrderToggle = Form.getFieldNulled(context,
288             toggleName);
289         Boolean toggle = new Boolean(orderColumnSortOrderToggle);
290         orderColumnID = (Integer) searchColumnsType
291             .rawOfString(orderColumnIDString);
292         ColumnInfo info = (ColumnInfo) searchColumnsType
293             .cookedOfRaw(orderColumnID);
294         String desc = Boolean.TRUE.equals(info.getSortdescending()) ? (Boolean.TRUE
295             .equals(toggle) ? "" : " DESC")
296             : (Boolean.TRUE.equals(toggle) ? " DESC" : "");
297         orderings.addElement(database.quotedName(info.getName()) + desc);
298         orderClause.addElement(name + "=" + orderColumnIDString);
299       }
300     }
301 
302     String orderBySQL = null;
303     if (orderings.elements().hasMoreElements())
304       orderBySQL = EnumUtils.concatenated(", ", orderings.elements());
305     context.put("orderClause", EnumUtils.concatenated("&", orderClause
306         .elements()));
307 
308     int start = 0;
309     String startString = Form.getFieldNulled(context, "start");
310     if (startString != null) {
311       try {
312         start = Math.max(0, Integer.parseInt(startString));
313       } catch (NumberFormatException e) {
314         throw new MelatiBugMelatiException("How did you get that in there?",
315             new FormParameterException("start", "Param must be an Integer"));
316       }
317     }
318     if (paged) { 
319       final int resultsPerPage = 20;
320       context.put("results", 
321                   new CountedDumbPagedEnumeration(
322                           table.selection(criteria, orderBySQL, false, false),
323                           start, resultsPerPage,
324                           table.cachedCount(criteria, false, false).count())
325       );
326     } else { 
327       context.put("results", table.selection(criteria, orderBySQL, false, false));
328     }
329     return context;
330   }
331 
332   /**
333    * Implements the field search/selection request method.
334    */
335   protected static String popupSelectTemplate(ServletTemplateContext context,
336       Melati melati) throws PoemException {
337     popupSelect(context, melati);
338     return adminTemplate("PopupSelect");
339   }
340 
341   protected static ServletTemplateContext popupSelect(ServletTemplateContext context,
342       Melati melati) throws PoemException {
343     final Table table = melati.getTable();
344 
345     final Database database = table.getDatabase();
346 
347     // sort out search criteria
348 
349     final Persistent criteria = table.newPersistent();
350 
351     MappedEnumeration criterias = new MappedEnumeration(table
352         .getSearchCriterionColumns()) {
353       public Object mapped(Object c) {
354         return ((Column) c).asField(criteria).withNullable(true);
355       }
356     };
357 
358     context.put("criteria", EnumUtils.vectorOf(criterias));
359     PoemType searchColumnsType = getSearchColumnsType(database, table);
360 
361     Vector orderings = new Vector();
362     // NOTE Order by searchable columns, this could be summary columns
363     Enumeration searchColumns = searchColumnsType.possibleRaws();
364     int o = 0;
365     while (searchColumns.hasMoreElements()) {
366       String name = "order-" + o++;
367       orderings.addElement(new Field(searchColumns.nextElement(), 
368           new BaseFieldAttributes(name, searchColumnsType)));
369     }
370 
371     context.put("orderings", orderings);
372 
373     return context;
374   }
375 
376   /**
377    * @return a type whose whose possible members are the search columns of the table
378    */
379   private static PoemType getSearchColumnsType(final Database database, final Table table) {
380     PoemType searchColumnsType = new ReferencePoemType(database
381         .getColumnInfoTable(), false) {
382       protected Enumeration _possibleRaws() {
383         return new MappedEnumeration(table.getSearchCriterionColumns()) {
384           public Object mapped(Object column) {
385             return ((Column) column).getColumnInfo().getTroid();
386           }
387         };
388       }
389     };
390     return searchColumnsType;
391   }
392 
393   /**
394    * @return primary select template
395    */
396   protected static String selectionWindowPrimarySelectTemplate(
397       ServletTemplateContext context, Melati melati) throws PoemException {
398     context.put("inPopup", Boolean.TRUE);
399     return primarySelectTemplate(context, melati);
400   }
401 
402   /**
403    * @return select template (a selection of records from a table)
404    */
405   protected static String selectionWindowSelectionTemplate(
406       ServletTemplateContext context, Melati melati) {
407     selection(context, melati, true);
408     context.put("inPopup", Boolean.TRUE);
409     return adminTemplate("Selection");
410   }
411 
412   /**
413    * Returns the Add template after placing the table and fields for the new row
414    * in the context using any field values already in the context.
415    * 
416    * If the table is a table meta data table, or a column meta data table then
417    * the appropriate extras are added to the co0ntext.
418    * 
419    * The Form does not normally contain values, but this could be used as a
420    * mechanism for providing defaults.
421    */
422   protected static String addTemplate(final ServletTemplateContext context,
423       Melati melati) throws PoemException {
424 
425     /*
426      * Enumeration fields = new MappedEnumeration(melati.getTable().columns()) {
427      * public Object mapped(Object column) { String stringValue =
428      * context.getForm("field_" + ((Column)column).getName()); Object value =
429      * null; if (stringValue != null) value =
430      * ((Column)column).getType().rawOfString(stringValue); return new
431      * Field(value, (Column)column); } }; context.put("fields", fields);
432      */
433 
434     // getDetailDisplayColumns() == columns() but could exclude some in theory
435     Enumeration columns = melati.getTable().getDetailDisplayColumns();
436     Vector fields = new Vector();
437     while (columns.hasMoreElements()) {
438       Column column = (Column) columns.nextElement();
439       String stringValue = context.getForm("field_" + column.getName());
440       Object value = null;
441       if (stringValue != null)
442         value = column.getType().rawOfString(stringValue);
443       else if (column.getType() instanceof ColumnTypePoemType)
444         value = PoemTypeFactory.STRING.getCode();
445       fields.add(new Field(value, column));
446     }
447     if (melati.getTable() instanceof TableInfoTable) {
448       Database database = melati.getDatabase();
449 
450       // Compose field for naming the TROID column: the display name and
451       // description are redundant, since they not used in the template
452 
453       final int troidHeight = 1;
454       final int troidWidth = 20;
455       Field troidNameField = new Field("id", new BaseFieldAttributes(
456           "troidName", "Troid column", "Name of TROID column", database
457               .getColumnInfoTable().getNameColumn().getType(), troidWidth,
458           troidHeight, null, false, true, true));
459 
460       fields.add(troidNameField);
461     }
462     context.put("fields", fields.elements());
463     return adminTemplate("Add");
464   }
465 
466   /**
467    * Returns the Updated template after creating a new row using field data in
468    * the context.
469    * <p>
470    * If successful the template will say so while reloading according to the
471    * returnTarget and returnURL values from the Form in context.
472    */
473   protected static String addUpdateTemplate(ServletTemplateContext context,
474       Melati melati) throws PoemException {
475 
476     Persistent newPersistent = create(melati.getTable(), context);
477 
478     if (melati.getTable() instanceof TableInfoTable)
479       melati.getDatabase().addTableAndCommit((TableInfo) newPersistent,
480           context.getForm("field_troidName"));
481     if (melati.getTable() instanceof ColumnInfoTable)
482       ((ColumnInfo) newPersistent).getTableinfo().actualTable()
483           .addColumnAndCommit((ColumnInfo) newPersistent);
484 
485     context.put("object", newPersistent);
486     return adminTemplate("Updated");
487   }
488 
489   /**
490    * Returns the Updated template after modifying the current row according to
491    * field values in the context.
492    * <p>
493    * If successful the template will say so while reloading according to the
494    * returnTarget and returnURL values from the Form in context.
495    */
496   protected static String updateTemplate(ServletTemplateContext context,
497       Melati melati) throws PoemException {
498     Persistent object = melati.getObject();
499     object.preEdit();
500     Form.extractFields(context, object);
501     object.postEdit(false);
502     return adminTemplate("Updated");
503   }
504 
505   protected static String deleteTemplate(ServletTemplateContext context,
506       Melati melati) throws PoemException {
507     try {
508       if (melati.getTable().getName().equals("tableinfo")) {
509         TableInfo tableInfo = (TableInfo) melati.getObject();
510         melati.getDatabase().deleteTableAndCommit(tableInfo);
511       } else if (melati.getTable().getName().equals("columninfo")) {
512         ColumnInfo columnInfo = (ColumnInfo) melati.getObject();
513         columnInfo.getTableinfo().actualTable().deleteColumnAndCommit(
514             columnInfo);
515       } else
516         melati.getObject().delete();
517 
518       return adminTemplate("Updated");
519     } catch (DeletionIntegrityPoemException e) {
520       context.put("object", e.object);
521       context.put("references", e.references);
522       context.put("returnURL", melati.getSameURL() + "?action=Delete");
523       return adminTemplate("DeleteFailure");
524     }
525   }
526 
527   protected static String duplicateTemplate(ServletTemplateContext context,
528       Melati melati) throws PoemException {
529     Persistent dup = melati.getObject().duplicated();
530     Form.extractFields(context, dup);
531     try {
532       dup.getTable().create(dup);
533     } catch (ExecutingSQLPoemException e) {
534       throw new NonUniqueKeyValueAnticipatedException(e);
535     }
536     context.put("object", dup);
537     return adminTemplate("Updated");
538   }
539 
540   /**
541    * Implements request method "Update".
542    * <p>
543    * Calls another method depending on the requested action.
544    * 
545    * @see #updateTemplate(ServletTemplateContext, Melati)
546    * @see #deleteTemplate(ServletTemplateContext, Melati)
547    * @see #duplicateTemplate(ServletTemplateContext, Melati)
548    */
549   protected static String modifyTemplate(ServletTemplateContext context,
550       Melati melati) throws FormParameterException {
551     String action = melati.getRequest().getParameter("action");
552     if ("Update".equals(action))
553       return updateTemplate(context, melati);
554     if ("Delete".equals(action))
555       return deleteTemplate(context, melati);
556     if ("Duplicate".equals(action))
557       return duplicateTemplate(context, melati);
558     else
559       throw new MelatiBugMelatiException("How did you get that in there?",
560           new FormParameterException(
561             "action", "Bad action from Edit: " + action));
562   }
563 
564   protected static String uploadTemplate(ServletTemplateContext context)
565       throws PoemException {
566     context.put("field", context.getForm("field"));
567     return adminTemplate("Upload");
568   }
569 
570   /**
571    * Finished uploading.
572    * 
573    * If you want the system to display the file you need to set your melati-wide
574    * FormDataAdaptorFactory, in org.melati.MelatiConfig.properties, to something
575    * that returns a valid URL, for instance, PoemFileDataAdaptorFactory;
576    * (remember to set your UploadDir and UploadURL in the Setting table).
577    * 
578    * @param context
579    *          the {@link ServletTemplateContext} in use
580    * @return a template name
581    */
582 
583   protected static String uploadDoneTemplate(ServletTemplateContext context)
584       throws PoemException {
585     String field = context.getForm("field");
586     context.put("field", field);
587     String url = "";
588     url = context.getMultipartForm("file").getDataURL();
589     if (url == null)
590       throw new NullUrlDataAdaptorException(context.getMultipartForm("file").getFormDataAdaptor());
591     context.put("url", url);
592     return adminTemplate("UploadDone");
593   }
594 
595   static class NullUrlDataAdaptorException extends MelatiRuntimeException {
596     private static final long serialVersionUID = 1L;
597     private FormDataAdaptor fda;
598     NullUrlDataAdaptorException(FormDataAdaptor fda) { 
599       this.fda = fda;
600     }
601 
602     /** @return the message */
603     public String getMessage() {
604       return "The configured FormDataAdaptor (" + fda.getClass().getName() + ") returns a null URL.";
605     }
606   }
607 
608   protected static String setupTemplate(ServletTemplateContext context,
609       Melati melati) {
610     screenStylesheetURL = melati.getDatabase().getSettingTable().ensure(
611         Admin.class.getName() + ".ScreenStylesheetURL", "/blue.css",
612         "ScreenStylesheetURL",
613         "path to stylesheet, relative to melati-static, starting with a slash")
614         .getValue();
615     primaryDisplayTable = melati.getDatabase().getSettingTable().ensure(
616         Admin.class.getName() + ".PrimaryDisplayTable", "setting",
617         "PrimaryDisplayTable", "The default table to display").getValue();
618     Setting homepageURLSetting = melati.getDatabase().getSettingTable().ensure(
619         Admin.class.getName() + ".HomepageURL", "http://www.melati.org/",
620         "HomepageURL", "The home page for this database");
621     homepageURL = homepageURLSetting.getValue();
622     // HACK Not very satisfactory, but only to get tests working elsewhere
623     context.put("object", homepageURLSetting);
624     return adminTemplate("Updated");
625   }
626 
627   protected String doTemplateRequest(Melati melati,
628       ServletTemplateContext context) throws Exception {
629 
630     if (Form.getField(context, "goto", null) != null)
631       melati.getResponse().sendRedirect(Form.getField(context, "goto", null));
632 
633     melati.setPassbackExceptionHandling();
634     melati.setResponseContentType("text/html");
635 
636     Capability admin = PoemThread.database().getCanAdminister();
637     AccessToken token = PoemThread.accessToken();
638     if (!token.givesCapability(admin))
639       throw new AccessPoemException(token, admin);
640 
641     context.put("admin", new AdminUtils(melati));
642 
643     if (melati.getMethod() == null)
644       return adminTemplate("Main");
645     if (melati.getMethod().equals("blank"))
646       return adminTemplate("blank");
647     if (melati.getMethod().equals("setup"))
648       return setupTemplate(context, melati);
649     if (melati.getMethod().equals("Main"))
650       return adminTemplate("Main");
651     if (melati.getMethod().equals("Top"))
652       return adminTemplate("Top");
653     if (melati.getMethod().equals("UploadDone"))
654       return uploadDoneTemplate(context);
655     if (melati.getMethod().equals("Record"))
656       return adminTemplate("Record");
657     if (melati.getMethod().equals("Selection"))
658       return selectionTemplate(context, melati);
659 
660     if (melati.getObject() != null) {
661       if (melati.getMethod().equals("Update"))
662         return modifyTemplate(context, melati);
663       if (melati.getObject() instanceof AdminSpecialised) {
664         String templateName = ((AdminSpecialised) melati.getObject())
665             .adminHandle(melati, melati.getMarkupLanguage());
666         if (templateName != null)
667           return templateName;
668       }
669     }
670 
671     if (melati.getTable() != null) {
672       if (melati.getMethod().equals("Tree"))
673         return adminTemplate("Tree");
674       if (melati.getMethod().equals("Bottom"))
675         return adminTemplate("Bottom");
676       if (melati.getMethod().equals("Table"))
677         return adminTemplate("Table");
678       if (melati.getMethod().equals("PrimarySelect"))
679         return primarySelectTemplate(context, melati);
680       if (melati.getMethod().equals("EditHeader"))
681         return adminTemplate("EditHeader");
682       if (melati.getMethod().equals("Edit"))
683         return adminTemplate("Edit");
684       if (melati.getMethod().equals("Upload"))
685         return uploadTemplate(context);
686 
687       if (melati.getMethod().equals("SelectionRight"))
688         return selectionRightTemplate(context, melati);
689       if (melati.getMethod().equals("Navigation"))
690         return adminTemplate("Navigation");
691       if (melati.getMethod().equals("PopUp"))
692         return popupSelectTemplate(context, melati);
693       if (melati.getMethod().equals("SelectionWindow"))
694         return adminTemplate("SelectionWindow");
695       if (melati.getMethod().equals("SelectionWindowPrimarySelect"))
696         return selectionWindowPrimarySelectTemplate(context, melati);
697       if (melati.getMethod().equals("SelectionWindowSelection"))
698         return selectionWindowSelectionTemplate(context, melati);
699       if (melati.getMethod().equals("Add"))
700         return addTemplate(context, melati);
701       if (melati.getMethod().equals("Created"))
702         return addUpdateTemplate(context, melati);
703     }
704     if (melati.getMethod().equals("DSD"))
705       return dsdTemplate(context);
706 
707     throw new InvalidUsageException(this, melati.getPoemContext());
708   }
709 
710   /**
711    * @return the screenStylesheetURL
712    */
713   static String getScreenStylesheetURL() {
714     return screenStylesheetURL;
715   }
716 
717   /**
718    * @param screenStylesheetURL the screenStylesheetURL to set
719    */
720   static void setScreenStylesheetURL(String screenStylesheetURL) {
721     Admin.screenStylesheetURL = screenStylesheetURL;
722   }
723 
724   /**
725    * @return the primaryDisplayTable
726    */
727   static String getPrimaryDisplayTable() {
728     return primaryDisplayTable;
729   }
730 
731   /**
732    * @param primaryDisplayTable the primaryDisplayTable to set
733    */
734   static void setPrimaryDisplayTable(String primaryDisplayTable) {
735     Admin.primaryDisplayTable = primaryDisplayTable;
736   }
737 
738   /**
739    * @return the homepageURL
740    */
741   static String getHomepageURL() {
742     return homepageURL;
743   }
744 
745   /**
746    * @param homepageURL the homepageURL to set
747    */
748   static void setHomepageURL(String homepageURL) {
749     Admin.homepageURL = homepageURL;
750   }
751 }