##### Coverage Report - org.melati.admin.Admin

Classes in this File | Line Coverage | Branch Coverage | Complexity
Admin |
95% | 282/295
92% | 127/138
| 4.257142857142857;4.257
Admin$1 |
100% | 3/3
N/A | N/A
| 4.257142857142857;4.257
Admin$2 |
100% | 2/2
N/A | N/A
| 4.257142857142857;4.257
Admin$3 |
100% | 2/2
N/A | N/A
| 4.257142857142857;4.257
Admin$3$1 |
100% | 2/2
N/A | N/A
| 4.257142857142857;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 |  |
     }