001/* 002 * Copyright 2021 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.forms.question.types; 017 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.regex.Pattern; 022 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.cocoon.xml.XMLUtils; 026import org.apache.commons.lang.StringUtils; 027import org.xml.sax.ContentHandler; 028import org.xml.sax.SAXException; 029 030import org.ametys.core.user.CurrentUserProvider; 031import org.ametys.core.user.User; 032import org.ametys.core.user.UserIdentity; 033import org.ametys.core.user.UserManager; 034import org.ametys.plugins.forms.helper.FormElementDefinitionHelper; 035import org.ametys.plugins.forms.repository.FormQuestion; 036import org.ametys.runtime.config.DisableCondition; 037import org.ametys.runtime.config.DisableCondition.OPERATOR; 038import org.ametys.runtime.config.DisableConditions; 039import org.ametys.runtime.i18n.I18nizableText; 040import org.ametys.runtime.model.ElementDefinition; 041import org.ametys.runtime.model.ModelItem; 042import org.ametys.runtime.model.SimpleViewItemGroup; 043import org.ametys.runtime.model.StaticEnumerator; 044import org.ametys.runtime.model.ViewElement; 045import org.ametys.runtime.model.type.ModelItemTypeConstants; 046import org.ametys.runtime.parameter.DefaultValidator; 047 048/** 049 * Class for creating simple text questions 050 */ 051public class SimpleTextQuestionType extends AbstractFormQuestionType 052{ 053 /** Constant for regexp attribute. */ 054 public static final String ATTRIBUTE_REGEXP = "regexp"; 055 056 /** Constant for custom regexp attribute. */ 057 public static final String ATTRIBUTE_CUSTOM_REGEX = "custom-regex"; 058 059 /** Constant for autofill attribute. */ 060 public static final String ATTRIBUTE_AUTOFILL = "autofill"; 061 062 /** Constant for default value attribute. */ 063 public static final String ATTRIBUTE_DEFAULT_VALUE = "default-value"; 064 065 /** Name of empty regexStaticEnumerator entry */ 066 public static final String EMPTY_REGEX_VALUE = " "; 067 068 /** Name of email regexStaticEnumerator entry */ 069 public static final String EMAIL_REGEX_VALUE = "email"; 070 071 /** Name of phone regexStaticEnumerator entry */ 072 public static final String PHONE_REGEX_VALUE = "phone"; 073 074 /** Name of regexStaticEnumerator entry that enable custom regex field */ 075 public static final String CUSTOM_REGEX_VALUE = "custom"; 076 077 /** Name of empty autofillStaticEnumerator entry */ 078 public static final String EMPTY_AUTOFILL_VALUE = " "; 079 080 /** Name of email autofillStaticEnumerator entry */ 081 public static final String EMAIL_AUTOFILL_VALUE = "email"; 082 083 /** Name of id autofillStaticEnumerator entry */ 084 public static final String ID_AUTOFILL_VALUE = "id"; 085 086 /** Name of fullname autofillStaticEnumerator entry */ 087 public static final String FULLNAME_AUTOFILL_VALUE = "fullName"; 088 089 /** Name of firstName autofillStaticEnumerator entry */ 090 public static final String FIRSTNAME_AUTOFILL_VALUE = "firstName"; 091 092 /** Name of lastName autofillStaticEnumerator entry */ 093 public static final String LASTNAME_AUTOFILL_VALUE = "lastName"; 094 095 /** Name of autofillStaticEnumerator entry that enable default value field */ 096 public static final String CUSTOM_AUTOFILL_VALUE = "custom"; 097 098 /** Constant for placeholder attribute. */ 099 public static final String ATTRIBUTE_PLACEHOLDER = "placeholder"; 100 101 /** Constant for default title */ 102 public static final String DEFAULT_TITLE = "PLUGIN_FORMS_QUESTION_DEFAULT_TITLE_SIMPLE_TEXT"; 103 104 /** The current user provider */ 105 protected CurrentUserProvider _currentUserProvider; 106 107 /** The users manager */ 108 protected UserManager _userManager; 109 110 @Override 111 public void service(ServiceManager manager) throws ServiceException 112 { 113 super.service(manager); 114 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 115 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 116 } 117 118 @Override 119 protected List<ModelItem> _getModelItems() 120 { 121 List<ModelItem> modelItems = super._getModelItems(); 122 123 //PLACEHOLDER 124 ElementDefinition placeholder = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_PLACEHOLDER, ModelItemTypeConstants.STRING_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_QUESTION_PLACEHOLDER", "PLUGINS_FORMS_QUESTIONS_DIALOG_QUESTION_PLACEHOLDER_DESC", null); 125 modelItems.add(placeholder); 126 127 //AUTOFILL 128 ElementDefinition<String> autofill = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_AUTOFILL, ModelItemTypeConstants.STRING_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_DESC", null); 129 130 StaticEnumerator<String> autofillStaticEnumerator = new StaticEnumerator<>(); 131 autofillStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_EMPTY_TEXT"), EMPTY_AUTOFILL_VALUE); 132 autofillStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_EMAIL"), EMAIL_AUTOFILL_VALUE); 133 autofillStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_ID"), ID_AUTOFILL_VALUE); 134 autofillStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_FULLNAME"), FULLNAME_AUTOFILL_VALUE); 135 autofillStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_FIRSTNAME"), FIRSTNAME_AUTOFILL_VALUE); 136 autofillStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_LASTNAME"), LASTNAME_AUTOFILL_VALUE); 137 autofillStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_AUTOFILL_CUSTOM"), CUSTOM_AUTOFILL_VALUE); 138 autofill.setEnumerator(autofillStaticEnumerator); 139 Map<String, I18nizableText> widgetParameters = new HashMap<>(); 140 widgetParameters.put("naturalOrder", new I18nizableText("true")); 141 autofill.setWidgetParameters(widgetParameters); 142 autofill.setDefaultValue(EMPTY_AUTOFILL_VALUE); 143 144 modelItems.add(autofill); 145 146 //DEFAULT VALUE 147 ElementDefinition defaultValue = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_DEFAULT_VALUE, ModelItemTypeConstants.STRING_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_DEFAULT_VALUE", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_DEFAULT_VALUE_DESC", null); 148 DisableConditions disableConditions = new DisableConditions(); 149 DisableCondition condition = new DisableCondition(ATTRIBUTE_AUTOFILL, OPERATOR.NEQ, CUSTOM_AUTOFILL_VALUE); 150 disableConditions.getConditions().add(condition); 151 defaultValue.setDisableConditions(disableConditions); 152 modelItems.add(defaultValue); 153 154 //REGEX 155 ElementDefinition<String> regexp = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_REGEXP, ModelItemTypeConstants.STRING_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE_DESC", null); 156 157 StaticEnumerator<String> regexStaticEnumerator = new StaticEnumerator<>(); 158 regexStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE_EMPTY_TEXT"), EMPTY_REGEX_VALUE); 159 regexStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE_EMAIL"), EMAIL_REGEX_VALUE); 160 regexStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE_PHONE"), PHONE_REGEX_VALUE); 161 regexStaticEnumerator.add(new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE_CUSTOM"), CUSTOM_REGEX_VALUE); 162 regexp.setEnumerator(regexStaticEnumerator); 163 Map<String, I18nizableText> regexpWidgetParameters = new HashMap<>(); 164 regexpWidgetParameters.put("naturalOrder", new I18nizableText("true")); 165 regexp.setWidgetParameters(regexpWidgetParameters); 166 regexp.setDefaultValue(EMPTY_REGEX_VALUE); 167 modelItems.add(regexp); 168 169 //CUSTOM REGEX 170 ElementDefinition customRegex = FormElementDefinitionHelper.getElementDefinition(ATTRIBUTE_CUSTOM_REGEX, ModelItemTypeConstants.STRING_TYPE_ID, "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE_CUSTOM", "PLUGINS_FORMS_QUESTIONS_DIALOG_SIMPLE_TEXT_REGEXTYPE_CUSTOM_DESC", new DefaultValidator("^\\/.+\\/[gi]*$", new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_REGEXP_ERROR_DESCRIPTION"), false)); 171 DisableConditions regexDisableConditions = new DisableConditions(); 172 DisableCondition regexCondition = new DisableCondition(ATTRIBUTE_REGEXP, OPERATOR.NEQ, CUSTOM_REGEX_VALUE); 173 regexDisableConditions.getConditions().add(regexCondition); 174 customRegex.setDisableConditions(regexDisableConditions); 175 modelItems.add(customRegex); 176 177 return modelItems; 178 } 179 180 @Override 181 protected SimpleViewItemGroup _getAdvancedTab() 182 { 183 SimpleViewItemGroup advancedFieldset = super._getAdvancedTab(); 184 185 ViewElement placeholder = new ViewElement(); 186 placeholder.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_PLACEHOLDER)); 187 advancedFieldset.addViewItem(placeholder); 188 189 ViewElement autofill = new ViewElement(); 190 autofill.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_AUTOFILL)); 191 advancedFieldset.addViewItem(autofill); 192 193 ViewElement defaultValue = new ViewElement(); 194 defaultValue.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_DEFAULT_VALUE)); 195 advancedFieldset.addViewItem(defaultValue); 196 197 ViewElement regexp = new ViewElement(); 198 regexp.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_REGEXP)); 199 advancedFieldset.addViewItem(regexp); 200 201 ViewElement customRegex = new ViewElement(); 202 customRegex.setDefinition((ElementDefinition< ? >) getModel().getModelItem(ATTRIBUTE_CUSTOM_REGEX)); 203 advancedFieldset.addViewItem(customRegex); 204 205 return advancedFieldset; 206 } 207 208 public String getStorageType(FormQuestion question) 209 { 210 return ModelItemTypeConstants.STRING_TYPE_ID; 211 } 212 213 @Override 214 protected ModelItem _getEntryModelItem(FormQuestion question) 215 { 216 ModelItem item = super._getEntryModelItem(question); 217 ((ElementDefinition) item).setValidator(new DefaultValidator(_getEntryRegExpPattern(question), isMandatory(question))); 218 return item; 219 } 220 221 @Override 222 public void saxAdditionalInfos(ContentHandler contentHandler, FormQuestion question) throws SAXException 223 { 224 super.saxAdditionalInfos(contentHandler, question); 225 226 if (StringUtils.isEmpty(question.getValue(ATTRIBUTE_DEFAULT_VALUE))) 227 { 228 UserIdentity userIdentity = _currentUserProvider.getUser(); 229 User user = userIdentity != null ? _userManager.getUser(userIdentity) : null; 230 String autofill = user != null ? _getAutofillValue(question, user) : null; 231 if (StringUtils.isNotBlank(autofill)) 232 { 233 XMLUtils.createElement(contentHandler, "default-value", autofill); 234 } 235 } 236 else 237 { 238 XMLUtils.createElement(contentHandler, "default-value", question.<String>getValue(ATTRIBUTE_DEFAULT_VALUE)); 239 } 240 241 String pattern = _getRegExpPattern(question); 242 if (StringUtils.isNotEmpty(pattern)) 243 { 244 XMLUtils.createElement(contentHandler, "pattern", pattern); 245 } 246 } 247 248 /** 249 * Get autofill value 250 * @param question the question 251 * @param user the user 252 * @return the autofill value.<code>null</code> if there is not autofill value 253 */ 254 protected String _getAutofillValue(FormQuestion question, User user) 255 { 256 String autofill = question.getValue(ATTRIBUTE_AUTOFILL); 257 if (autofill != null) 258 { 259 switch (autofill) 260 { 261 case "email": 262 return user.getEmail(); 263 case "id": 264 return user.getIdentity().getLogin(); 265 case "fullName": 266 return user.getFullName(); 267 case "firstName": 268 return user.getFirstName(); 269 case "lastName": 270 return user.getLastName(); 271 default: 272 return null; 273 } 274 } 275 return null; 276 } 277 278 /** 279 * Get the validation pattern. 280 * @param question the question 281 * @return the validation pattern. 282 */ 283 protected String _getRegExpPattern (FormQuestion question) 284 { 285 String regexpType = question.getValue(ATTRIBUTE_REGEXP); 286 if (regexpType == null) 287 { 288 return null; 289 } 290 else if ("email".equals(regexpType)) 291 { 292 return "/^([a-zA-Z0-9_\\.\\-\\+])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,4})+$/"; 293 } 294 else if ("phone".equals(regexpType)) 295 { 296 return "/^(\\+?\\(?[0-9]{1,3}\\)?([\\s]?)(\\(0\\))?|0)([\\s]?)([0-9\\-\\+\\s]{4,})+$/"; 297 } 298 else if ("custom".equals(regexpType) && !("".equals(question.getValue(ATTRIBUTE_CUSTOM_REGEX)))) 299 { 300 return question.getValue(ATTRIBUTE_CUSTOM_REGEX); 301 } 302 303 return null; 304 } 305 306 /** 307 * Get the validation pattern for entries. 308 * @param question the question 309 * @return the validation pattern. 310 */ 311 protected String _getEntryRegExpPattern (FormQuestion question) 312 { 313 String regexpType = question.getValue(ATTRIBUTE_REGEXP); 314 if (regexpType == null) 315 { 316 return null; 317 } 318 else if ("email".equals(regexpType)) 319 { 320 return "^([a-zA-Z0-9_\\.\\-\\+])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,4})+$"; 321 } 322 else if ("phone".equals(regexpType)) 323 { 324 return "^(\\+?\\(?[0-9]{1,3}\\)?([\\s]?)(\\(0\\))?|0)([\\s]?)([0-9\\-\\+\\s]{4,})+$"; 325 } 326 else if ("custom".equals(regexpType) && !("".equals(question.getValue(ATTRIBUTE_CUSTOM_REGEX)))) 327 { 328 String custom = question.getValue(ATTRIBUTE_CUSTOM_REGEX); 329 return custom.substring(1, custom.length() - 1); 330 } 331 332 return null; 333 } 334 335 @Override 336 public void validateQuestionValues(Map<String, Object> values, Map<String, I18nizableText> errors) 337 { 338 super.validateQuestionValues(values, errors); 339 340 if (StringUtils.isNotBlank((String) values.get(ATTRIBUTE_CUSTOM_REGEX)) 341 && !Pattern.matches("^\\/.+\\/[gi]*$", (CharSequence) values.get(ATTRIBUTE_CUSTOM_REGEX))) 342 { 343 errors.put(ATTRIBUTE_CUSTOM_REGEX, new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTIONS_REGEXP_ERROR_DESCRIPTION")); 344 } 345 } 346 347 public I18nizableText getDefaultTitle() 348 { 349 return new I18nizableText("plugin.forms", DEFAULT_TITLE); 350 } 351 352 @Override 353 public List<String> getFieldToDisableIfFormPublished(FormQuestion question) 354 { 355 List<String> fieldNames = super.getFieldToDisableIfFormPublished(question); 356 fieldNames.add(ATTRIBUTE_REGEXP); 357 fieldNames.add(ATTRIBUTE_CUSTOM_REGEX); 358 return fieldNames; 359 } 360}