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