View Javadoc

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