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