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