Coverage Report - org.melati.admin.Admin
 
Classes in this File Line Coverage Branch Coverage Complexity
Admin
96%
246/256
93%
113/122
3.735
Admin$1
100%
3/3
N/A
3.735
Admin$2
100%
2/2
N/A
3.735
Admin$3
100%
2/2
N/A
3.735
Admin$3$1
100%
2/2
N/A
3.735
 
 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  1
 public class Admin extends TemplateServlet {
 127  
   private static final long serialVersionUID = 1L;
 128  
 
 129  1
   private static String screenStylesheetURL = null;
 130  1
   private static String primaryDisplayTable = null;
 131  1
   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  6
     Persistent result = table.create(new Initialiser() {
 139  6
       public void init(Persistent object) throws AccessPoemException,
 140  
           ValidationPoemException {
 141  6
         Form.extractFields(context, object);
 142  6
       }
 143  
     });
 144  6
     result.postEdit(true);
 145  6
     return result;
 146  
   }
 147  
 
 148  
   /**
 149  
    * Return the resource path for an admin template.
 150  
    */
 151  
   protected static final String adminTemplate(String name) {
 152  369
     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  1
     String c = PoemThread.database().getClass().getName();
 165  1
     int dot = c.lastIndexOf('.');
 166  1
     String p = c.substring(0, dot);
 167  
 
 168  1
     context.put("package", p);
 169  1
     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  36
     final Table table = melati.getTable();
 178  
 
 179  
     Field primaryCriterion;
 180  
 
 181  36
     Column column = table.primaryCriterionColumn();
 182  36
     if (column != null) {
 183  29
       String sea = context.getForm("field_" + column.getName());
 184  29
       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  29
     } else
 192  7
       primaryCriterion = null;
 193  
 
 194  36
     context.put("primaryCriterion", primaryCriterion);
 195  36
     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  49
     String templateName = context.getForm("template");
 205  49
     if (templateName == null) {
 206  49
       selection(context, melati, true);
 207  49
       return adminTemplate("Selection");
 208  
     } else { 
 209  0
       selection(context, melati, false);
 210  0
       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  1
     selection(context, melati, true);
 223  1
     context.put("inRight", Boolean.TRUE);
 224  1
     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  52
     final Table table = melati.getTable();
 247  
 
 248  52
     final Database database = table.getDatabase();
 249  
 
 250  
     // sort out search criteria
 251  
 
 252  52
     final Persistent criteria = table.newPersistent();
 253  
 
 254  52
     Vector whereClause = new Vector();
 255  
 
 256  52
     for (Enumeration c = table.columns(); c.hasMoreElements();) {
 257  589
       Column column = (Column) c.nextElement();
 258  589
       String name = "field_" + column.getName();
 259  589
       String fieldValue = Form.getFieldNulled(context, name);
 260  589
       if (fieldValue != null) {
 261  8
         column
 262  
             .setRaw_unsafe(criteria, column.getType().rawOfString(fieldValue));
 263  
 
 264  
         // FIXME Needs to work for dates
 265  8
         whereClause.addElement(name + "=" + melati.urlEncode(fieldValue));
 266  
       }
 267  589
     }
 268  
 
 269  52
     context.put("whereClause", EnumUtils.concatenated("&", whereClause
 270  
         .elements()));
 271  
 
 272  
     // sort out ordering 
 273  
 
 274  52
     PoemType searchColumnsType = getSearchColumnsType(database, table);
 275  
 
 276  52
     Vector orderings = new Vector();
 277  52
     Vector orderClause = new Vector();
 278  
 
 279  
     
 280  204
     for (int o = 1; o <= table.displayColumnsCount(DisplayLevel.summary); ++o) {
 281  152
       String name = "field_order-" + o;
 282  152
       String orderColumnIDString = Form.getFieldNulled(context, name);
 283  152
       Integer orderColumnID = null;
 284  
 
 285  152
       if (orderColumnIDString != null) {
 286  10
         String toggleName = "field_order-" + o + "-toggle";
 287  10
         String orderColumnSortOrderToggle = Form.getFieldNulled(context,
 288  
             toggleName);
 289  10
         Boolean toggle = new Boolean(orderColumnSortOrderToggle);
 290  10
         orderColumnID = (Integer) searchColumnsType
 291  
             .rawOfString(orderColumnIDString);
 292  10
         ColumnInfo info = (ColumnInfo) searchColumnsType
 293  
             .cookedOfRaw(orderColumnID);
 294  10
         String desc = Boolean.TRUE.equals(info.getSortdescending()) ? (Boolean.TRUE
 295  
             .equals(toggle) ? "" : " DESC")
 296  
             : (Boolean.TRUE.equals(toggle) ? " DESC" : "");
 297  10
         orderings.addElement(database.quotedName(info.getName()) + desc);
 298  10
         orderClause.addElement(name + "=" + orderColumnIDString);
 299  
       }
 300  
     }
 301  
 
 302  52
     String orderBySQL = null;
 303  52
     if (orderings.elements().hasMoreElements())
 304  9
       orderBySQL = EnumUtils.concatenated(", ", orderings.elements());
 305  52
     context.put("orderClause", EnumUtils.concatenated("&", orderClause
 306  
         .elements()));
 307  
 
 308  52
     int start = 0;
 309  52
     String startString = Form.getFieldNulled(context, "start");
 310  52
     if (startString != null) {
 311  
       try {
 312  6
         start = Math.max(0, Integer.parseInt(startString));
 313  0
       } catch (NumberFormatException e) {
 314  0
         throw new MelatiBugMelatiException("How did you get that in there?",
 315  
             new FormParameterException("start", "Param must be an Integer"));
 316  6
       }
 317  
     }
 318  52
     if (paged) { 
 319  52
       final int resultsPerPage = 20;
 320  52
       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  52
     } else { 
 327  0
       context.put("results", table.selection(criteria, orderBySQL, false, false));
 328  
     }
 329  52
     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  2
     popupSelect(context, melati);
 338  2
     return adminTemplate("PopupSelect");
 339  
   }
 340  
 
 341  
   protected static ServletTemplateContext popupSelect(ServletTemplateContext context,
 342  
       Melati melati) throws PoemException {
 343  2
     final Table table = melati.getTable();
 344  
 
 345  2
     final Database database = table.getDatabase();
 346  
 
 347  
     // sort out search criteria
 348  
 
 349  2
     final Persistent criteria = table.newPersistent();
 350  
 
 351  2
     MappedEnumeration criterias = new MappedEnumeration(table
 352  
         .getSearchCriterionColumns()) {
 353  2
       public Object mapped(Object c) {
 354  6
         return ((Column) c).asField(criteria).withNullable(true);
 355  
       }
 356  
     };
 357  
 
 358  2
     context.put("criteria", EnumUtils.vectorOf(criterias));
 359  2
     PoemType searchColumnsType = getSearchColumnsType(database, table);
 360  
 
 361  2
     Vector orderings = new Vector();
 362  
     // NOTE Order by searchable columns, this could be summary columns
 363  2
     Enumeration searchColumns = searchColumnsType.possibleRaws();
 364  2
     int o = 0;
 365  8
     while (searchColumns.hasMoreElements()) {
 366  6
       String name = "order-" + o++;
 367  6
       orderings.addElement(new Field(searchColumns.nextElement(), 
 368  
           new BaseFieldAttributes(name, searchColumnsType)));
 369  6
     }
 370  
 
 371  2
     context.put("orderings", orderings);
 372  
 
 373  2
     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  54
     PoemType searchColumnsType = new ReferencePoemType(database
 381  
         .getColumnInfoTable(), false) {
 382  54
       protected Enumeration _possibleRaws() {
 383  8
         return new MappedEnumeration(table.getSearchCriterionColumns()) {
 384  8
           public Object mapped(Object column) {
 385  24
             return ((Column) column).getColumnInfo().getTroid();
 386  
           }
 387  
         };
 388  
       }
 389  
     };
 390  54
     return searchColumnsType;
 391  
   }
 392  
 
 393  
   /**
 394  
    * @return primary select template
 395  
    */
 396  
   protected static String selectionWindowPrimarySelectTemplate(
 397  
       ServletTemplateContext context, Melati melati) throws PoemException {
 398  2
     context.put("inPopup", Boolean.TRUE);
 399  2
     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  2
     selection(context, melati, true);
 408  2
     context.put("inPopup", Boolean.TRUE);
 409  2
     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  6
     Enumeration columns = melati.getTable().getDetailDisplayColumns();
 436  6
     Vector fields = new Vector();
 437  51
     while (columns.hasMoreElements()) {
 438  45
       Column column = (Column) columns.nextElement();
 439  45
       String stringValue = context.getForm("field_" + column.getName());
 440  45
       Object value = null;
 441  45
       if (stringValue != null)
 442  1
         value = column.getType().rawOfString(stringValue);
 443  44
       else if (column.getType() instanceof ColumnTypePoemType)
 444  1
         value = PoemTypeFactory.STRING.getCode();
 445  45
       fields.add(new Field(value, column));
 446  45
     }
 447  6
     if (melati.getTable() instanceof TableInfoTable) {
 448  1
       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  1
       final int troidHeight = 1;
 454  1
       final int troidWidth = 20;
 455  1
       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  1
       fields.add(troidNameField);
 461  
     }
 462  6
     context.put("fields", fields.elements());
 463  6
     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  6
     Persistent newPersistent = create(melati.getTable(), context);
 477  
 
 478  6
     if (melati.getTable() instanceof TableInfoTable)
 479  1
       melati.getDatabase().addTableAndCommit((TableInfo) newPersistent,
 480  
           context.getForm("field_troidName"));
 481  6
     if (melati.getTable() instanceof ColumnInfoTable)
 482  1
       ((ColumnInfo) newPersistent).getTableinfo().actualTable()
 483  
           .addColumnAndCommit((ColumnInfo) newPersistent);
 484  
 
 485  6
     context.put("object", newPersistent);
 486  6
     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  1
     Persistent object = melati.getObject();
 499  1
     object.preEdit();
 500  1
     Form.extractFields(context, object);
 501  1
     object.postEdit(false);
 502  1
     return adminTemplate("Updated");
 503  
   }
 504  
 
 505  
   protected static String deleteTemplate(ServletTemplateContext context,
 506  
       Melati melati) throws PoemException {
 507  
     try {
 508  6
       if (melati.getTable().getName().equals("tableinfo")) {
 509  2
         TableInfo tableInfo = (TableInfo) melati.getObject();
 510  2
         melati.getDatabase().deleteTableAndCommit(tableInfo);
 511  1
       } else if (melati.getTable().getName().equals("columninfo")) {
 512  2
         ColumnInfo columnInfo = (ColumnInfo) melati.getObject();
 513  2
         columnInfo.getTableinfo().actualTable().deleteColumnAndCommit(
 514  
             columnInfo);
 515  2
       } else
 516  2
         melati.getObject().delete();
 517  
 
 518  5
       return adminTemplate("Updated");
 519  1
     } catch (DeletionIntegrityPoemException e) {
 520  1
       context.put("object", e.object);
 521  1
       context.put("references", e.references);
 522  1
       context.put("returnURL", melati.getSameURL() + "?action=Delete");
 523  1
       return adminTemplate("DeleteFailure");
 524  
     }
 525  
   }
 526  
 
 527  
   protected static String duplicateTemplate(ServletTemplateContext context,
 528  
       Melati melati) throws PoemException {
 529  1
     Persistent dup = melati.getObject().duplicated();
 530  1
     Form.extractFields(context, dup);
 531  
     try {
 532  1
       dup.getTable().create(dup);
 533  0
     } catch (ExecutingSQLPoemException e) {
 534  0
       throw new NonUniqueKeyValueAnticipatedException(e);
 535  1
     }
 536  1
     context.put("object", dup);
 537  1
     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  8
     String action = melati.getRequest().getParameter("action");
 552  8
     if ("Update".equals(action))
 553  1
       return updateTemplate(context, melati);
 554  7
     if ("Delete".equals(action))
 555  6
       return deleteTemplate(context, melati);
 556  1
     if ("Duplicate".equals(action))
 557  1
       return duplicateTemplate(context, melati);
 558  
     else
 559  0
       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  2
     context.put("field", context.getForm("field"));
 567  2
     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  1
     String field = context.getForm("field");
 586  1
     context.put("field", field);
 587  1
     String url = "";
 588  1
     url = context.getMultipartForm("file").getDataURL();
 589  1
     if (url == null)
 590  0
       throw new NullUrlDataAdaptorException(context.getMultipartForm("file").getFormDataAdaptor());
 591  1
     context.put("url", url);
 592  1
     return adminTemplate("UploadDone");
 593  
   }
 594  
 
 595  1
   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  1
     screenStylesheetURL = melati.getDatabase().getSettingTable().ensure(
 611  1
         Admin.class.getName() + ".ScreenStylesheetURL", "/blue.css",
 612  
         "ScreenStylesheetURL",
 613  
         "path to stylesheet, relative to melati-static, starting with a slash")
 614  
         .getValue();
 615  1
     primaryDisplayTable = melati.getDatabase().getSettingTable().ensure(
 616  
         Admin.class.getName() + ".PrimaryDisplayTable", "setting",
 617  
         "PrimaryDisplayTable", "The default table to display").getValue();
 618  1