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 package info.magnolia.module.form.templates.components;
35
36 import info.magnolia.cms.i18n.I18nContentSupportFactory;
37 import info.magnolia.cms.i18n.Messages;
38 import info.magnolia.cms.i18n.MessagesManager;
39 import info.magnolia.cms.i18n.MessagesUtil;
40 import info.magnolia.cms.security.SilentSessionOp;
41 import info.magnolia.context.MgnlContext;
42 import info.magnolia.jcr.util.NodeTypes.Renderable;
43 import info.magnolia.jcr.util.NodeUtil;
44 import info.magnolia.jcr.util.PropertyUtil;
45 import info.magnolia.module.form.FormModule;
46 import info.magnolia.module.form.engine.FormDataBinder;
47 import info.magnolia.module.form.engine.FormField;
48 import info.magnolia.module.form.engine.FormStepState;
49 import info.magnolia.module.form.validators.ValidationResult;
50 import info.magnolia.module.form.validators.Validator;
51 import info.magnolia.repository.RepositoryConstants;
52 import info.magnolia.util.EscapeUtil;
53
54 import java.util.Iterator;
55 import java.util.Locale;
56
57 import javax.jcr.Node;
58 import javax.jcr.Property;
59 import javax.jcr.RepositoryException;
60 import javax.jcr.Session;
61 import javax.jcr.Value;
62
63 import org.apache.commons.lang.StringUtils;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67
68
69
70
71 public class DefaultFormDataBinder implements FormDataBinder {
72
73 private static final Logger log = LoggerFactory.getLogger(DefaultFormDataBinder.class);
74
75 private static final String CONTENT_NAME_TEXT_FIELD_GROUP = "edits";
76
77 private static final String DEFAULT_PATH = "info.magnolia.module.form.messages";
78
79 private static final String PATH_TO_TEMPLATES = "/modules/%s/templates/%s";
80
81 private String i18nBasename;
82
83 public void setI18nBasename(String i18nBasename) {
84 this.i18nBasename = i18nBasename;
85 }
86
87 public static String getDefaultPath(){
88 return DEFAULT_PATH;
89 }
90
91 @Override
92 public FormStepState bindAndValidate(Node component) throws RepositoryException {
93 FormStepState step = new FormStepState();
94 step.setParagraphUuid(NodeUtil.getNodeIdentifierIfPossible(component));
95 if (component.hasNode("fieldsets")) {
96 Iterator<Node> itFieldsets = NodeUtil.getNodes(component.getNode("fieldsets")).iterator();
97 bindAndValidateFieldset(itFieldsets, step);
98 }
99 return step;
100 }
101
102 private void bindAndValidateFieldset(Iterator<Node> itFieldsets, FormStepState step) throws RepositoryException {
103 while (itFieldsets.hasNext()) {
104 Node fieldset = itFieldsets.next();
105 if (fieldset.hasNode("fields")) {
106 Iterator<Node> iterator = NodeUtil.getNodes(fieldset.getNode("fields")).iterator();
107 bindAndValidateFields(iterator, step);
108 }
109 }
110 }
111
112
113
114
115
116 protected void bindAndValidateFields(Iterator<Node> iterator, FormStepState step) throws RepositoryException {
117 while (iterator.hasNext()) {
118 final Node node = iterator.next();
119
120 if (node.hasProperty("controlName")) {
121 final String controlName = node.getProperty("controlName").getString();
122
123 Node config = getFieldConfiguration(node);
124 boolean escapeHtml = true;
125
126 if (config != null) {
127 escapeHtml = PropertyUtil.getBoolean(config, "escapeHtml", true);
128 }
129
130 final String values = StringUtils.join(MgnlContext.getParameterValues(controlName), "__");
131 final String value = escapeHtml ? EscapeUtil.escapeXss(values) : values;
132
133 FormField field = new FormField();
134 field.setName(controlName);
135 field.setValue(value);
136 step.add(field);
137
138 if (StringUtils.isEmpty(value) && isMandatory(node)) {
139 field.setErrorMessage(getErrorMessage("mandatory", node));
140 } else if (value != null && node.hasProperty("validation")) {
141 for (String validatorName : getValidatorNames(node)) {
142 Validator validator = FormModule.getInstance().getValidatorByName(validatorName);
143 if (validator != null) {
144 ValidationResult validationResult = validator.validateWithResult(value);
145 if (!validationResult.isSuccess()) {
146 field.setErrorMessage(getValidatorErrorMessage(validator, validationResult, node));
147 }
148 }
149 }
150 } else if (node.hasNode(CONTENT_NAME_TEXT_FIELD_GROUP)) {
151 Iterator<Node> textFieldGroup = NodeUtil.getNodes(node.getNode(CONTENT_NAME_TEXT_FIELD_GROUP)).iterator();
152 bindAndValidateFields(textFieldGroup, step);
153 }
154 }
155 }
156 }
157
158
159
160
161 private String[] getValidatorNames(Node node) throws RepositoryException {
162 Property validationProperty = node.getProperty("validation");
163
164 Value[] values;
165 if (validationProperty.isMultiple()) {
166 values = validationProperty.getValues();
167 } else {
168 values = new Value[]{validationProperty.getValue()};
169 }
170
171 return PropertyUtil.getValuesStringList(values).toArray(new String[values.length]);
172 }
173
174 protected boolean isMandatory(Node node) {
175 return PropertyUtil.getBoolean(node, "mandatory", false);
176 }
177
178 private String getValidatorErrorMessage(Validator validator, ValidationResult validationResult, Node node) {
179
180
181 if (StringUtils.isNotEmpty(validationResult.getErrorMessage())) {
182 return getErrorMessage(validationResult.getErrorMessage(), "invalid input", node, validator.getI18nBasename());
183 }
184
185
186 return getErrorMessage(validator.getName(), node);
187 }
188
189 protected String getErrorMessage(String message, Node node) {
190 return getErrorMessage("form.user.errorMessage." + message, "invalid input", node, null);
191 }
192
193 private String getErrorMessage(String key, String defaultMsg, Node node, String overridingResourceBundle) {
194
195 Locale locale = I18nContentSupportFactory.getI18nSupport().getLocale();
196
197 Messages messages = MessagesManager.getMessages(getDefaultPath());
198
199 messages = MessagesUtil.chain(MessagesManager.getMessages(i18nBasename, locale), messages);
200
201 if (overridingResourceBundle != null) {
202 messages = MessagesUtil.chain(MessagesManager.getMessages(overridingResourceBundle, locale), messages);
203 }
204
205 String errorMessage = messages.getWithDefault(key, defaultMsg);
206 String title = PropertyUtil.getString(node, "title");
207 return title + ": " + errorMessage;
208 }
209
210
211
212
213 protected Node getFieldConfiguration(Node node) {
214
215 try {
216 String template = Renderable.getTemplate(node);
217 if (template == null) {
218 log.warn("Could not find mgnl:template for node at {}", node.getPath());
219 return null;
220 }
221 String[] idAndPath = template.split(":");
222
223 final String path;
224
225 if (idAndPath.length > 1) {
226 path = String.format(PATH_TO_TEMPLATES, idAndPath[0], idAndPath[1]);
227 } else {
228 path = String.format(PATH_TO_TEMPLATES, "form", idAndPath[0]);
229 }
230
231 log.debug("Trying to get field configuration at {}", path);
232 return MgnlContext.doInSystemContext(new SilentSessionOp<Node>(RepositoryConstants.CONFIG) {
233
234 @Override
235 public Node doExec(Session session) throws Throwable {
236 return session.getNode(path);
237 }
238 });
239
240 } catch (RepositoryException e) {
241 e.printStackTrace();
242 }
243 return null;
244 }
245 }