View Javadoc

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