/
HtmlFormEntryController.java
343 lines (297 loc) · 13.8 KB
/
HtmlFormEntryController.java
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
package org.openmrs.module.htmlformentry.web.controller;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Encounter;
import org.openmrs.Form;
import org.openmrs.Patient;
import org.openmrs.User;
import org.openmrs.api.APIAuthenticationException;
import org.openmrs.api.context.Context;
import org.openmrs.module.htmlformentry.BadFormDesignException;
import org.openmrs.module.htmlformentry.FormEntryContext.Mode;
import org.openmrs.module.htmlformentry.FormEntrySession;
import org.openmrs.module.htmlformentry.FormSubmissionError;
import org.openmrs.module.htmlformentry.HtmlForm;
import org.openmrs.module.htmlformentry.HtmlFormEntryUtil;
import org.openmrs.module.htmlformentry.ValidationException;
import org.openmrs.parameter.EncounterSearchCriteriaBuilder;
import org.openmrs.util.OpenmrsUtil;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
/**
* The controller for entering/viewing a form.
* <p/>
* Handles {@code htmlFormEntry.form} requests. Renders view {@code htmlFormEntry.jsp}.
* <p/>
* TODO: This has a bit too much logic in the onSubmit method. Move that into the FormEntrySession.
*/
@Controller
public class HtmlFormEntryController {
protected final Log log = LogFactory.getLog(getClass());
public final static String closeDialogView = "/module/htmlformentry/closeDialog";
public final static String FORM_IN_PROGRESS_KEY = "HTML_FORM_IN_PROGRESS_KEY";
public final static String FORM_IN_PROGRESS_VALUE = "HTML_FORM_IN_PROGRESS_VALUE";
public final static String FORM_PATH = "/module/htmlformentry/htmlFormEntry.form";
// A place to store data that will persist longer than a session, but won't
// persist beyond application restart
private static Map<User, Map<String, Object>> volatileUserData = new WeakHashMap<User, Map<String, Object>>();
@RequestMapping(method = RequestMethod.GET, value = FORM_PATH)
public void showForm() {
// Intentionally blank. All work is done in the getFormEntrySession method
}
@ModelAttribute("command")
public FormEntrySession getFormEntrySession(HttpServletRequest request,
// @RequestParam doesn't pick up query parameters (in the url) in a POST, so I'm handling encounterId, modeParam, and which specially
/*@RequestParam(value="mode", required=false) String modeParam,*/
/*@RequestParam(value="encounterId", required=false) Integer encounterId,*/
/*@RequestParam(value="which", required=false) String which,*/
@RequestParam(value = "patientId", required = false) Integer patientId,
/*@RequestParam(value="personId", required=false) Integer personId,*/
@RequestParam(value = "formId", required = false) Integer formId,
@RequestParam(value = "htmlformId", required = false) Integer htmlFormId,
@RequestParam(value = "returnUrl", required = false) String returnUrl,
@RequestParam(value = "formModifiedTimestamp", required = false) Long formModifiedTimestamp,
@RequestParam(value = "encounterModifiedTimestamp", required = false) Long encounterModifiedTimestamp,
@RequestParam(value = "hasChangedInd", required = false) String hasChangedInd) throws Exception {
long ts = System.currentTimeMillis();
Mode mode = Mode.VIEW;
Integer personId = null;
if (StringUtils.hasText(request.getParameter("personId"))) {
personId = Integer.valueOf(request.getParameter("personId"));
}
String modeParam = request.getParameter("mode");
if ("enter".equalsIgnoreCase(modeParam)) {
mode = Mode.ENTER;
} else if ("edit".equalsIgnoreCase(modeParam)) {
mode = Mode.EDIT;
}
Patient patient = null;
Encounter encounter = null;
Form form = null;
HtmlForm htmlForm = null;
if (StringUtils.hasText(request.getParameter("encounterId"))) {
String encounterId = request.getParameter("encounterId");
try {
encounter = Context.getEncounterService().getEncounter(Integer.valueOf(encounterId));
}
catch (NumberFormatException ex) {
encounter = Context.getEncounterService().getEncounterByUuid(encounterId);
}
if (encounter == null)
throw new IllegalArgumentException("No encounter with id=" + encounterId);
patient = encounter.getPatient();
patientId = patient.getPatientId();
personId = patient.getPersonId();
if (formId != null) { // I think formId is allowed to differ from encounter.form.id because of HtmlFormFlowsheet
form = Context.getFormService().getForm(formId);
htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(form);
if (htmlForm == null)
throw new IllegalArgumentException("No HtmlForm associated with formId " + formId);
} else {
form = encounter.getForm();
htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(encounter.getForm());
if (htmlForm == null)
throw new IllegalArgumentException("The form for the specified encounter (" + encounter.getForm()
+ ") does not have an HtmlForm associated with it");
}
} else { // no encounter specified
// get person from patientId/personId (register module uses patientId, htmlformentry uses personId)
if (patientId != null) {
personId = patientId;
}
if (personId != null) {
patient = Context.getPatientService().getPatient(personId);
}
// determine form
if (htmlFormId != null) {
htmlForm = HtmlFormEntryUtil.getService().getHtmlForm(htmlFormId);
} else if (formId != null) {
form = Context.getFormService().getForm(formId);
htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(form);
}
if (htmlForm == null) {
throw new IllegalArgumentException(
"You must specify either an htmlFormId or a formId for a valid html form");
}
String which = request.getParameter("which");
if (StringUtils.hasText(which)) {
if (patient == null) {
throw new IllegalArgumentException("Cannot specify 'which' without specifying a person/patient");
}
EncounterSearchCriteriaBuilder b = new EncounterSearchCriteriaBuilder();
b.setPatient(patient).setEnteredViaForms(Collections.singleton(form)).setIncludeVoided(false);
List<Encounter> encs = Context.getEncounterService().getEncounters(b.createEncounterSearchCriteria());
if (which.equals("first")) {
encounter = encs.get(0);
} else if (which.equals("last")) {
encounter = encs.get(encs.size() - 1);
} else {
throw new IllegalArgumentException("which must be 'first' or 'last'");
}
}
}
if (mode != Mode.ENTER && patient == null)
throw new IllegalArgumentException("No patient with id of personId=" + personId + " or patientId=" + patientId);
FormEntrySession session = null;
if (mode == Mode.ENTER && patient == null) {
patient = new Patient();
}
if (encounter != null) {
session = new FormEntrySession(patient, encounter, mode, htmlForm, request.getSession());
} else {
session = new FormEntrySession(patient, htmlForm, request.getSession());
}
if (StringUtils.hasText(returnUrl)) {
session.setReturnUrl(returnUrl);
}
// Since we're not using a sessionForm, we need to check for the case where the underlying form was modified while a user was filling a form out
if (formModifiedTimestamp != null) {
if (!OpenmrsUtil.nullSafeEquals(formModifiedTimestamp, session.getFormModifiedTimestamp())) {
throw new RuntimeException(
Context.getMessageSourceService().getMessage("htmlformentry.error.formModifiedBeforeSubmission"));
}
}
// Since we're not using a sessionForm, we need to make sure this encounter hasn't been modified since the user opened it
if (encounter != null) {
if (encounterModifiedTimestamp != null
&& !OpenmrsUtil.nullSafeEquals(encounterModifiedTimestamp, session.getEncounterModifiedTimestamp())) {
throw new RuntimeException(Context.getMessageSourceService()
.getMessage("htmlformentry.error.encounterModifiedBeforeSubmission"));
}
}
if (hasChangedInd != null)
session.setHasChangedInd(hasChangedInd);
// ensure we've generated the form's HTML (and thus set up the submission actions, etc) before we do anything
session.getHtmlToDisplay();
setVolatileUserData(FORM_IN_PROGRESS_KEY, session);
log.info("Took " + (System.currentTimeMillis() - ts) + " ms");
return session;
}
/**
* Get a piece of information for the currently authenticated user. This information is stored only
* temporarily. When a new module is loaded or the server is restarted, this information will
* disappear. If there is not information by this key, null is returned TODO: This needs to be
* refactored/removed
*
* @param key identifying string for the information
* @return the information stored
*/
public static Object getVolatileUserData(String key) {
User u = Context.getAuthenticatedUser();
if (u == null) {
throw new APIAuthenticationException();
}
Map<String, Object> myData = volatileUserData.get(u);
if (myData == null) {
return null;
} else {
return myData.get(key);
}
}
/**
* Set a piece of information for the currently authenticated user. This information is stored only
* temporarily. When a new module is loaded or the server is restarted, this information will
* disappear
*
* @param key identifying string for this information
* @param value information to be stored
*/
public static void setVolatileUserData(String key, Object value) {
User u = Context.getAuthenticatedUser();
if (u == null) {
throw new APIAuthenticationException();
}
Map<String, Object> myData = volatileUserData.get(u);
if (myData == null) {
myData = new HashMap<String, Object>();
volatileUserData.put(u, myData);
}
myData.put(key, value);
}
/*
* I'm using a return type of ModelAndView so I can use RedirectView rather than "redirect:" and preserve the fact that
* returnUrl values from the pre-annotated-controller days will have the context path already
*/
@RequestMapping(method = RequestMethod.POST, value = FORM_PATH)
public ModelAndView handleSubmit(@ModelAttribute("command") FormEntrySession session, Errors errors,
HttpServletRequest request, Model model) throws Exception {
try {
List<FormSubmissionError> validationErrors = session.getSubmissionController()
.validateSubmission(session.getContext(), request);
if (validationErrors != null && validationErrors.size() > 0) {
errors.reject("Fix errors");
}
}
catch (Exception ex) {
log.error("Exception during form validation", ex);
errors.reject("Exception during form validation, see log for more details: " + ex);
}
if (errors.hasErrors()) {
return new ModelAndView(FORM_PATH, "command", session);
}
// no form validation errors, proceed with submission
session.prepareForSubmit();
if (session.getContext().getMode() == Mode.ENTER && session.hasPatientTag() && session.getPatient() == null
&& (session.getSubmissionActions().getPersonsToCreate() == null
|| session.getSubmissionActions().getPersonsToCreate().size() == 0))
throw new IllegalArgumentException("This form is not going to create an Patient");
if (session.getContext().getMode() == Mode.ENTER && session.hasEncouterTag()
&& (session.getSubmissionActions().getEncountersToCreate() == null
|| session.getSubmissionActions().getEncountersToCreate().size() == 0))
throw new IllegalArgumentException("This form is not going to create an encounter");
try {
session.getSubmissionController().handleFormSubmission(session, request);
HtmlFormEntryUtil.getService().applyActions(session);
String successView = session.getAfterSaveUrlTemplate();
if (successView != null) {
successView = successView.replaceAll("\\{\\{patient.id\\}\\}", session.getPatient().getId().toString());
successView = successView.replaceAll("\\{\\{encounter.id\\}\\}", session.getEncounter().getId().toString());
successView = request.getContextPath() + "/" + successView;
} else {
successView = session.getReturnUrlWithParameters();
}
if (successView == null)
successView = request.getContextPath() + "/patientDashboard.form" + getQueryPrameters(request, session);
if (StringUtils.hasText(request.getParameter("closeAfterSubmission"))) {
return new ModelAndView(closeDialogView, "dialogToClose", request.getParameter("closeAfterSubmission"));
} else {
return new ModelAndView(new RedirectView(successView));
}
}
catch (ValidationException ex) {
log.error("Invalid input:", ex);
errors.reject(ex.getMessage());
}
catch (BadFormDesignException ex) {
log.error("Bad Form Design:", ex);
errors.reject(ex.getMessage());
}
catch (Exception ex) {
log.error("Exception trying to submit form", ex);
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
errors.reject("Exception! " + ex.getMessage() + "<br/>" + sw.toString());
}
// if we get here it's because we caught an error trying to submit/apply
return new ModelAndView(FORM_PATH, "command", session);
}
protected String getQueryPrameters(HttpServletRequest request, FormEntrySession formEntrySession) {
return "?patientId=" + formEntrySession.getPatient().getPersonId();
}
}