some db code rewriting
[mir.git] / source / mir / servlet / ServletModule.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License,
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23  * (or with modified versions of the above that use the same license as the above),
24  * and distribute linked combinations including the two.  You must obey the
25  * GNU General Public License in all respects for all of the code used other than
26  * the above mentioned libraries.  If you modify this file, you may extend this
27  * exception to your version of the file, but you are not obligated to do so.
28  * If you do not wish to do so, delete this exception statement from your version.
29  */
30 package mir.servlet;
31
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse;
42 import javax.servlet.http.HttpSession;
43
44 import mir.config.MirPropertiesConfiguration;
45 import mir.entity.adapter.EntityAdapterEngine;
46 import mir.entity.adapter.EntityAdapterModel;
47 import mir.log.LoggerWrapper;
48 import mir.module.AbstractModule;
49 import mir.module.ModuleExc;
50 import mir.storage.Database;
51 import mir.util.HTTPRequestParser;
52 import mir.util.URLBuilder;
53 import mircoders.global.MirGlobal;
54 import mircoders.localizer.MirLocalizerExc;
55 import mircoders.servlet.ServletHelper;
56
57 /**
58  *
59  */
60
61 public abstract class ServletModule {
62   public String defaultAction;
63   protected LoggerWrapper logger;
64   protected static MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();
65   private static Locale fallbackLocale = new Locale(configuration.getString("Mir.Admin.FallbackLanguage", "en"), "");
66
67   protected AbstractModule mainModule;
68   protected String definition;
69   protected EntityAdapterModel model;
70
71   protected String listGenerator;
72   protected String editGenerator;
73   protected String deleteConfirmationGenerator;
74   protected int nrEntitiesPerListPage;
75
76   /** the list of parameters that need to be propagated in the list */
77   protected List propagatedParameters = new ArrayList();
78
79   public ServletModule(){
80     propagatedParameters.add("searchfield");
81     propagatedParameters.add("searchtext");
82     propagatedParameters.add("searchispublished");
83     propagatedParameters.add("searchstatus");
84     propagatedParameters.add("searchorder");
85
86     definition = null;
87     try {
88       model = MirGlobal.localizer().dataModel().adapterModel();
89     }
90     catch (MirLocalizerExc e) {
91       logger.error("Can't create model: " + e.toString());
92       throw new ServletModuleFailure("Can't retrieve model", e);
93     }
94
95     listGenerator = configuration.getString("ServletModule."+getOperationModuleName()+".ListTemplate");
96     editGenerator = configuration.getString("ServletModule."+getOperationModuleName()+".EditTemplate");
97     deleteConfirmationGenerator = configuration.getString("ServletModule."+getOperationModuleName()+".DeleteConfirmationTemplate");
98     nrEntitiesPerListPage =
99         configuration.getInt("ServletModule."+getOperationModuleName()+".ListSize",
100         configuration.getInt("ServletModule.Default.ListSize", 20));
101
102   }
103
104
105   public void logAdminUsage(HttpServletRequest aRequest, String anObject, String aDescription) {
106     MirGlobal.logAdminUsage(ServletHelper.getUser(aRequest), getOperationModuleName() + ":" + anObject, aDescription);
107   }
108
109   /**
110    * Singleton instance retrievel method. MUST be overridden in subclasses.
111    *
112    * @return ServletModule the single instance of the servletmodule class
113    */
114   public static ServletModule getInstance() {
115     return null;
116   }
117
118   /**
119    * Return the module name
120    */
121   protected String getOperationModuleName() {
122     return getClass().getName().substring((new String("mircoders.servlet.ServletModule")).length());
123   }
124
125   public static Locale[] getLocales(HttpServletRequest aRequest) {
126     return new Locale[] { getLocale(aRequest), fallbackLocale };
127   }
128
129   /**
130    * Return the locale either from the session or the accept-language header ot the request
131    * this supersedes getLanguage for the new i18n
132    */
133   public static Locale getLocale(HttpServletRequest aRequest) {
134     Locale locale = null;
135     HttpSession session = aRequest.getSession(false);
136     if (session != null) {
137       // session can be null in case of logout
138       locale = (Locale) session.getAttribute("locale");
139     }
140     // if there is nothing in the session get it fron the accept-language
141     if (locale == null) {
142       locale = aRequest.getLocale();
143     }
144     return locale;
145   }
146
147   /**
148    * get the locale either from the session or the accept-language header ot the request
149    * this supersedes getLanguage for the new i18n
150    */
151   public Locale getFallbackLocale(HttpServletRequest aRequest) {
152     return fallbackLocale;
153   }
154
155   /**
156    * Function to specify the default ordering for lists. May be overridden.
157    *
158    *
159    * @return
160    */
161   public String getDefaultListOrdering() {
162
163     if (mainModule!=null && mainModule.getStorageObject()!=null) {
164       if (mainModule.getStorageObject().hasField("webdb_create"))
165         return "webdb_create desc";
166     }
167
168     return "id asc";
169   }
170
171   /**
172    * Generic list servlet method
173    */
174
175   public void list(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletModuleExc {
176     HTTPRequestParser requestParser = new HTTPRequestParser(aRequest);
177
178     String where = requestParser.getParameter("where");
179     String order = requestParser.getParameterWithDefault("order", getDefaultListOrdering());
180     int offset = requestParser.getIntegerWithDefault("offset", 0);
181
182     returnList(aRequest, aResponse, where, order, offset);
183   }
184
185
186   public void returnList(HttpServletRequest aRequest, HttpServletResponse aResponse,
187      String aWhereClause, String anOrderByClause, int anOffset) throws ServletModuleExc {
188     returnList(aRequest, aResponse, aWhereClause, anOrderByClause, anOffset, Collections.EMPTY_MAP);
189   }
190
191   public void returnList(HttpServletRequest aRequest, HttpServletResponse aResponse,
192      String aWhereClause, String anOrderByClause, int anOffset, Map anOverridingRequestParameters) throws ServletModuleExc {
193     HTTPRequestParser requestParser = new HTTPRequestParser(aRequest, anOverridingRequestParameters);
194     URLBuilder urlBuilder = new URLBuilder();
195
196     try {
197       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, getLocales(aRequest));
198
199       List list =
200          EntityAdapterEngine.retrieveAdapterList(model, definition, aWhereClause, anOrderByClause, nrEntitiesPerListPage, anOffset);
201
202       responseData.put("nexturl", null);
203       responseData.put("prevurl", null);
204       responseData.put("module", getOperationModuleName());
205
206       urlBuilder.setValue("module", getOperationModuleName());
207       urlBuilder.setValue("do", "list");
208       urlBuilder.setValue("where", aWhereClause);
209       urlBuilder.setValue("order", anOrderByClause);
210
211
212       urlBuilder.setValue("offset", anOffset);
213       responseData.put("offset" , new Integer(anOffset).toString());
214       responseData.put("thisurl" , urlBuilder.getQuery());
215
216       propagateFields(requestParser, urlBuilder, responseData);
217
218       if (list.size()>=nrEntitiesPerListPage) {
219         urlBuilder.setValue("offset", anOffset + nrEntitiesPerListPage);
220         responseData.put("nexturl" , urlBuilder.getQuery());
221       }
222
223       if (anOffset>0) {
224         urlBuilder.setValue("offset", Math.max(anOffset - nrEntitiesPerListPage, 0));
225         responseData.put("prevurl" , urlBuilder.getQuery());
226       }
227
228       responseData.put("entities", list);
229       responseData.put("from" , Integer.toString(anOffset+1));
230       responseData.put("count", "?");
231       responseData.put("to", Integer.toString(anOffset+list.size()-1));
232
233       ServletHelper.generateResponse(aResponse.getWriter(), responseData, listGenerator);
234     }
235     catch (Throwable e) {
236       throw new ServletModuleFailure(e);
237     }
238   }
239
240   public void editObject(HttpServletRequest aRequest, HttpServletResponse aResponse, String anId) throws ServletModuleExc {
241     try {
242       editObject(aRequest, aResponse, model.makeEntityAdapter(definition, mainModule.getById(anId)), false, anId);
243     }
244     catch (Throwable t) {
245       throw new ServletModuleFailure(t);
246     }
247   }
248
249   public void editObject(HttpServletRequest aRequest, HttpServletResponse aResponse, Object anObject, boolean anIsNew, String anId) throws ServletModuleExc {
250     HTTPRequestParser requestParser = new HTTPRequestParser(aRequest);
251     URLBuilder urlBuilder = new URLBuilder();
252
253     try {
254       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, getLocales(aRequest));
255
256       responseData.put("module", getOperationModuleName());
257       responseData.put("entity", anObject);
258       responseData.put("new", new Boolean(anIsNew));
259
260
261       urlBuilder.setValue("module", getOperationModuleName());
262       urlBuilder.setValue("returnurl", requestParser.getParameter("returnurl"));
263       if (anIsNew)
264         urlBuilder.setValue("do", "add");
265       else {
266         urlBuilder.setValue("id", anId);
267         urlBuilder.setValue("do", "edit");
268       }
269       responseData.put("returnurl", requestParser.getParameter("returnurl"));
270       responseData.put("thisurl", urlBuilder.getQuery());
271
272       ServletHelper.generateResponse(aResponse.getWriter(), responseData, editGenerator);
273     }
274     catch (Throwable e) {
275       throw new ServletModuleFailure(e);
276     }
277   }
278
279   /**
280    * Generic add servlet method
281    */
282   public void add(HttpServletRequest aRequest, HttpServletResponse aResponse)
283       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
284
285     Map object = new HashMap();
286
287     Iterator i = mainModule.getStorageObject().getFieldNames().iterator();
288
289     while (i.hasNext())
290       object.put(i.next(), "");
291
292     initializeNewObject(object, aRequest, aResponse);
293
294     editObject(aRequest, aResponse, object, true, null);
295   }
296
297   protected void initializeNewObject(Map aNewObject, HttpServletRequest aRequest, HttpServletResponse aResponse) {
298   }
299
300   /**
301    * Generic edit servlet method
302    */
303   public void edit(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
304     edit(aRequest, aResponse, aRequest.getParameter("id"));
305   }
306
307   /**
308    */
309   public void edit(HttpServletRequest aRequest, HttpServletResponse aResponse, String anIdentifier)
310       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
311     try {
312       editObject(aRequest, aResponse, model.makeEntityAdapter(definition, mainModule.getById(anIdentifier)), false, anIdentifier);
313     }
314     catch (Throwable e) {
315       throw new ServletModuleFailure(e);
316     }
317   }
318
319   /**
320    * Generic update servlet method
321    */
322   public void update(HttpServletRequest aRequest, HttpServletResponse aResponse)
323       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
324     try {
325       HTTPRequestParser requestParser = new HTTPRequestParser(aRequest);
326
327       String id = aRequest.getParameter("id");
328       Map withValues = getIntersectingValues(aRequest, mainModule.getStorageObject());
329       mainModule.set(withValues);
330
331       logAdminUsage(aRequest, id, "object modified");
332
333       String returnUrl = requestParser.getParameter("returnurl");
334
335       if (returnUrl!=null) {
336         ServletHelper.redirect(aResponse, returnUrl);
337       }
338       else {
339         edit(aRequest, aResponse, id);
340       }
341     }
342     catch (Throwable e) {
343       throw new ServletModuleFailure(e);
344     }
345   }
346
347   /**
348    * Generic insert servlet method
349    */
350   public void insert(HttpServletRequest aRequest, HttpServletResponse aResponse)
351       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
352     try {
353       HTTPRequestParser requestParser = new HTTPRequestParser(aRequest);
354
355       Map object = getIntersectingValues(aRequest, mainModule.getStorageObject());
356
357       String id = processInstertedObject(object, aRequest, aResponse);
358
359       logAdminUsage(aRequest, id, "object inserted");
360
361       String returnUrl = requestParser.getParameter("returnurl");
362
363       if (returnUrl!=null) {
364         ServletHelper.redirect(aResponse, returnUrl);
365       }
366       else {
367         edit(aRequest, aResponse, id);
368       }
369     }
370     catch (Throwable e) {
371       throw new ServletModuleFailure(e);
372     }
373   }
374
375   /**
376    *
377    */
378   public String processInstertedObject(Map anObject, HttpServletRequest aRequest, HttpServletResponse aResponse) {
379     try {
380       return mainModule.add(anObject);
381     }
382     catch (ModuleExc t) {
383       throw new ServletModuleFailure(t);
384     }
385   }
386
387   /**
388    * Generic delete confirmation servlet method
389    */
390   public void confirmdelete(HttpServletRequest aRequest, HttpServletResponse aResponse) {
391     try {
392       String idParam = aRequest.getParameter("id");
393       String confirmParam = aRequest.getParameter("confirm");
394
395       if (confirmParam != null && !confirmParam.equals("")) {
396         mainModule.deleteById(idParam);
397         logAdminUsage(aRequest, idParam, "object deleted");
398         ServletHelper.redirect(aResponse, aRequest.getParameter("okurl"));
399       }
400       else
401         ServletHelper.redirect(aResponse, aRequest.getParameter("cancelurl"));
402     }
403     catch (Throwable t) {
404       throw new ServletModuleFailure(t);
405     }
406   }
407
408   /**
409    * Generic delete servlet method
410    */
411   public void delete(HttpServletRequest aRequest, HttpServletResponse aResponse)
412       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure  {
413     try {
414       String idParam = aRequest.getParameter("id");
415
416       if (idParam == null)
417         throw new ServletModuleExc("Invalid call to delete: no id supplied");
418
419       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, getLocales(aRequest));
420
421       responseData.put("module", getOperationModuleName());
422       responseData.put("id", idParam);
423       responseData.put("cancelurl", aRequest.getParameter("cancelurl"));
424       responseData.put("okurl", aRequest.getParameter("okurl"));
425
426       ServletHelper.generateResponse(aResponse.getWriter(), responseData, deleteConfirmationGenerator);
427     }
428     catch (Throwable e) {
429       throw new ServletModuleFailure(e);
430     }
431   }
432
433   /**
434    */
435   public String defaultAction() {
436     return defaultAction;
437   }
438
439   /**
440    * Gets the fields from a httprequest and matches them with the metadata from
441    * the database object. Returns the keys that match, with their values.
442    *
443    * @return Map with the values
444    */
445   public Map getIntersectingValues(HttpServletRequest aRequest, Database theStorage)
446       throws ServletModuleExc, ServletModuleFailure {
447
448     HTTPRequestParser parser;
449     List theFieldList;
450
451     parser = new HTTPRequestParser(aRequest);
452
453     theFieldList = theStorage.getFieldNames();
454
455     Map withValues = new HashMap();
456     String aField, aValue;
457
458     for (int i = 0; i < theFieldList.size(); i++) {
459       aField = (String) theFieldList.get(i);
460
461       aValue = parser.getParameter(aField);
462       if (aValue != null)
463         withValues.put(aField, aValue);
464     }
465     return withValues;
466   }
467
468   private void propagateFields(HTTPRequestParser aRequest, URLBuilder aUrlBuilder, Map aResponseData) {
469     Iterator i = propagatedParameters.iterator();
470     while (i.hasNext()) {
471       String parameter = (String) i.next();
472       String value = aRequest.getParameter(parameter);
473       aUrlBuilder.setValue(parameter, value);
474       aResponseData.put(parameter, value);
475     }
476   }
477 }