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