/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.gui.components.form.flexible.impl;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import org.apache.commons.io.IOUtils;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.ComponentCollection;
import org.olat.core.gui.components.form.flexible.FormBaseComponentIdProvider;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.Submit;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.media.ServletUtil;
import org.olat.core.gui.translator.Translator;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.ArrayHelper;
import org.olat.core.util.CodeHelper;
import org.olat.core.util.StringHelper;
import org.olat.core.util.ValidationStatus;
import org.olat.core.util.WebappHelper;
import org.olat.core.util.component.FormComponentTraverser;
import org.olat.core.util.component.FormComponentVisitor;
/**
* <h4>Description:<h4>
* This Form is responsible for creation of the form header and footer. It can
* not hold form elements. Instead one has to create a form container and put
* the form elements there. E.g. use FormLayoutContainer or
* FormVelocityContainer.
* <p>
* This Form is the Component which gets dispatched by the framework. It then
* dispatches further to the really clicked FormComponent. The Form implements
* the following phases:
* <ol>
* <li>let all Formelements evaluate the Formrequest</li>
* <li>dispatch to the correct FormComponent</li>
* <li>the dispatched FormComponent may decide to SUBMIT the form, e.g. let all
* FormComponents validate its input and report error, or taken other actions.</li>
* <li>during the validatition phase each FormComponent can register an action</li>
* <li>after the validation, all actions are applied</li>
* <li>an event is thrown if the form validated or not</li>
* </ol>
* <p>
* FormComponent and FormContainer form the same composite pattern as already
* used for the core.Component and core.Container, and take notice that the
* FormComponent itself is also a core.Component!<br>
* As a consequence of this, each element which want to live inside of a form
* must be a FormComponent but has also a Component side to the rendering
* framework.
* <p>
* The goals of the new form infrastructure are
* <ul>
* <li>to allow complete freedom for layouting forms.</li>
* <li>easy-migration path for existing forms</li>
* <li>easy-usage for developers</li>
* <li>easy-layouting for designers</li>
* <li>allow subworkflows in forms without loosing form input</li>
* <li>allow AJAX features for form elements (completion, on blur etc.)</li>
* </ul>
* Some extra care had to be taken to fullfill these requirements and still
* beeing compliant with the already existing AJAX component replacement.<br>
* It was decided that a FormComponent consist of
* <ul>
* <li>Formelement, e.g. input field, radio button, select box, a link!</li>
* <li>Label for the Formelement</li>
* <li>Error for the Formelement</li>
* <li>Example for the Formelement</li>
* </ul>
* <p>
* <h4>Multipart and file upload</h4>
* Since release 6.1 the form infrastructure does also support multipart form
* data (file uploads). The form switches to the multipart mode as soon as a
* form element of type FormMultipartItem is added. In this case, all file
* uploads and form parameters are parsed by the form class and added to the
* requestParams and requestMultipartFiles maps. If no multipart element is in the form, the
* normal non-multipart way is used (less overhead, stability).
* <p>
* Therefore it is important to always use the form.getParameter() methods and
* not the getParameter() methods from the user request directly. Normally you
* don't have to deal with this because the implemented form elements already
* take care of this issue.
* <p>
* All submitted files are saved to a temporary location in the userdata/tmp/
* directory. During the dispatch phase in evalFormRequest() this files can be
* access using the getMultipartFilesSet() and getMultipartFile() methods. The
* files must be moved to another location within the execution of the
* evalFormRequest() because at the end of the method call, the temporary files
* will be removed. The temporary files have a random file name, use the
* getMultipartFileName() to retrieve the original file name.
* <p>
* When using the FileElement this is all already encapsulated, see the
* documentation there.
* <p>
* Initial Date: 27.11.2006 <br>
*
* @author patrickb
*/
public class Form {
private static final OLog log = Tracing.createLoggerFor(Form.class);
//
public final static String FORMCMD = "fid";
public final static String FORMID = "ofo_";
public final static String FORM_UNDEFINED="undefined";
public final static int REQUEST_ERROR_NO_ERROR = -1;
public final static int REQUEST_ERROR_GENERAL = 1;
public final static int REQUEST_ERROR_FILE_EMPTY = 2;
public final static int REQUEST_ERROR_UPLOAD_LIMIT_EXCEEDED = 3;
private String formName;
private String dispatchFieldId;
private String eventFieldId;
// the real form
private FormItemContainer formLayout;
private FormWrapperContainer formWrapperComponent;
private Integer action;
private boolean hasAlreadyFired;
private List<FormBasicController> formListeners;
private boolean isValidAndSubmitted=true;
private boolean isDirtyMarking=true;
private boolean isHideDirtyMarkingMessage = false;
private boolean multipartEnabled = false;
// temporary form data, only valid within execution of evalFormRequest()
private Map<String,String[]> requestParams = new HashMap<String,String[]>();
private Map<String, File> requestMultipartFiles = new HashMap<String,File>();
private Map<String, String> requestMultipartFileNames = new HashMap<String,String>();
private Map<String, String> requestMultipartFileMimeTypes = new HashMap<String,String>();
private int requestError = REQUEST_ERROR_NO_ERROR;
private Form() {
//
}
/**
* create a new form, where the caller is attached as component listener.
* Caller receives form validation success or failure events.
*
* @param name
* @param translator
* @param rootFormContainer if null the default layout is choosen, otherwise
* the given layouting container is taken.
* @param listener the component listener of this form, typically the caller
* @return
*/
public static Form create(String id, String name, FormItemContainer formLayout, Controller listener) {
Form form = new Form();
// this is where the formitems go to
form.formLayout = formLayout;
form.formLayout.setRootForm(form);
form.formListeners = new ArrayList<FormBasicController>(1);
if(listener instanceof FormBasicController){
form.formListeners.add((FormBasicController)listener);
}
Translator translator = formLayout.getTranslator();
if (translator == null) { throw new AssertException("please provide a translator in the FormItemContainer <" + formLayout.getName()
+ ">"); }
// renders header + <formLayout> + footer of html form
form.formWrapperComponent = new FormWrapperContainer(id, name, translator, form);
form.formWrapperComponent.addListener(listener);
//form.formWrapperComponent.put(formLayout.getComponent().getComponentName(), formLayout.getComponent());
// generate name for form and dispatch uri hidden field
form.formName = Form.FORMID + form.formWrapperComponent.getDispatchID();
form.dispatchFieldId = form.formName + "_dispatchuri";
form.eventFieldId = form.formName +"_eventval";
return form;
}
/**
* @param ureq
*/
public void evalFormRequest(UserRequest ureq) {
// Initialize temporary request parameters
if (isMultipartEnabled() && isMultipartContent(ureq.getHttpReq())) {
doInitRequestMultipartDataParameter(ureq);
} else {
doInitRequestParameter(ureq);
}
String dispatchUri = getRequestParameter("dispatchuri");
String dispatchAction = getRequestParameter("dispatchevent");
boolean invalidDispatchUri = dispatchUri == null || dispatchUri.equals(FORM_UNDEFINED);
boolean invalidDispatchAct = dispatchAction == null || dispatchAction.equals(FORM_UNDEFINED);
boolean implicitFormSubmit = false;//see also OLAT-3141
if(invalidDispatchAct && invalidDispatchUri){
//case if:
//enter was pressed in Safari / IE
//crawler tries form links
SubmitFormComponentVisitor efcv = new SubmitFormComponentVisitor();
new FormComponentTraverser(efcv, formLayout, false).visitAll(ureq);
Submit submitFormItem = efcv.getSubmit();
if(submitFormItem != null) {
//if we have submit form item
//assume a click on this item
dispatchUri = FormBaseComponentIdProvider.DISPPREFIX + submitFormItem.getComponent().getDispatchID();
action = FormEvent.ONCLICK;
}else{
// instead of
// throw new AssertException("You have an input field but no submit item defined! this is no good if enter is pressed.");
// assume a desired implicit form submit
// see also OLAT-3141
implicitFormSubmit = true;
}
} else {
try {
action = Integer.valueOf(dispatchAction);
} catch(Exception e) {
throw new InvalidRequestParameterException();
}
}
hasAlreadyFired = false;
isValidAndSubmitted = false;
//
// step 1: call evalFormRequest(ureq) on each FormComponent this gives
// ....... for each element the possibility to intermediate save a value.
// ....... As a sideeffect the formcomponent to be dispatched is found.
EvaluatingFormComponentVisitor efcv = new EvaluatingFormComponentVisitor(dispatchUri);
FormComponentTraverser ct = new FormComponentTraverser(efcv, formLayout, false);
ct.visitAll(ureq);
// step 2: dispatch to the form component
// ......... only one component to be dispatched can be found, e.g. clicked
// ......... element....................................................
// ......... dispatch changes server model -> rerendered
// ......... dispatch may also request a form validation by
// ......... calling the submit
FormItem dispatchFormItem = efcv.getDispatchToComponent();
//.......... doDispatchFormRequest is called on the found item
//.......... which in turn may call submit(UserRequest ureq).
//.......... After submitting, which fires a ok/nok event
//.......... the code goes further with step 3.........................
if (implicitFormSubmit) {
//implicit Submit (Press Enter without on a Field without submit item.)
// see also OLAT-3141
submit(ureq);
}else{
if (dispatchFormItem == null) {
// source not found. This "never happens". Try to produce some hints.
String fbc = new String();
for (FormBasicController i: formListeners) {
if (fbc.length()>0) {
fbc += ",";
}
fbc+=(i.getClass().getName());
}
log.warn("OLAT-5061: Could not determine request source in FlexiForm >"+formName+"<. Check >"+fbc+"<", null);
// TODO: what now?
// Assuming the same as "implicitFormSubmit" for now.
submit(ureq);
} else {
// ****************************************
// explicit Submit or valid form dispatch *
// ****************************************
dispatchFormItem.doDispatchFormRequest(ureq);
// step 3: find parent container of dispatched component
// .......... check dependency rules
// .......... apply dependency rules
FindParentFormComponentVisitor fpfcv = new FindParentFormComponentVisitor(
dispatchFormItem);
ct = new FormComponentTraverser(fpfcv, formLayout, false);
ct.visitAll(ureq);
fpfcv.getParent().evalDependencyRuleSetFor(ureq, dispatchFormItem);
}
}
//
action = -1;
// End of request dispatch: cleanup temp files: ureq requestParams and multipart files
doClearRequestParameterAndMultipartData();
}
private void doInitRequestMultipartDataParameter(UserRequest ureq) {
HttpServletRequest req = ureq.getHttpReq();
try {
for(Part part:req.getParts()) {
String name = part.getName();
String contentType = part.getContentType();
String fileName = getSubmittedFileName(part);
if(StringHelper.containsNonWhitespace(fileName)) {
File tmpFile = new File(WebappHelper.getTmpDir(), "upload-" + CodeHelper.getGlobalForeverUniqueID());
part.write(tmpFile.getAbsolutePath());
// Cleanup IE filenames that are absolute
int slashpos = fileName.lastIndexOf("/");
if (slashpos != -1) fileName = name.substring(slashpos + 1);
slashpos = fileName.lastIndexOf("\\");
if (slashpos != -1) fileName = fileName.substring(slashpos + 1);
requestMultipartFiles.put(name, tmpFile);
requestMultipartFileNames.put(name, fileName);
requestMultipartFileMimeTypes.put(name, contentType);
} else {
String value = IOUtils.toString(part.getInputStream(), "UTF-8");
addRequestParameter(name, value);
}
part.delete();
}
} catch (IOException | ServletException e) {
log.error("", e);
}
}
private String getSubmittedFileName(Part part) {
final String disposition = part.getHeader("Content-Disposition");
if (disposition != null) {
if (disposition.startsWith("form-data")) {
String fileName = ServletUtil.extractQuotedValueFromHeader(disposition, "filename");
if (fileName != null) {
return fileName;
}
}
}
return null;
}
private boolean isMultipartContent(HttpServletRequest request) {
if (!"POST".equalsIgnoreCase(request.getMethod())) {
return false;
}
String contentType = request.getContentType();
if (contentType == null) {
return false;
}
if (contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")) {
return true;
}
return false;
}
/**
* Get parameters the standard way
* @param ureq
*/
private void doInitRequestParameter(UserRequest ureq) {
Set<String> keys = ureq.getParameterSet();
for (String key : keys) {
String[] values = ureq.getHttpReq().getParameterValues(key);
if (values != null) {
requestParams.put(key, values);
} else {
addRequestParameter(key, ureq.getParameter(key));
}
}
}
/**
* Internal helper to add the request parameters to the request param map.
* Takes care of multi value parameters
*
* @param key
* @param value
*/
private void addRequestParameter(String key, String value) {
String[] values = requestParams.get(key);
if (values == null) {
// First element for this key
values = new String[]{value};
//use log.debug instead of System.out.println("PARAMS:"+key+" :: "+value);
}
else {
// A multi-key element (e.g. radio button)
values = ArrayHelper.addToArray(values, value, true);
//use log.debug instead of System.out.println("PARAMS:"+key+" ::[array of values]");
}
requestParams.put(key, values);
}
/**
* Internal helper to clear the temporary request parameter and file maps.
* Will delete all uploaded files if they have not been removed by the
* responsible FormItem.
*/
private void doClearRequestParameterAndMultipartData() {
for (Entry<String, File> entry : requestMultipartFiles.entrySet()) {
File tmpFile = entry.getValue();
if (tmpFile.exists()) tmpFile.delete();
}
requestMultipartFiles.clear();
requestMultipartFileNames.clear();
requestMultipartFileMimeTypes.clear();
requestParams.clear();
requestError = REQUEST_ERROR_NO_ERROR;
}
/**
* Check if there was an error while parsing this request. See REQUEST_ERROR_*
* constants
*
* @return the last error code
*/
public int getLastRequestError() {
return requestError;
}
/**
* @param ureq
*/
public void submit(UserRequest ureq) {
submit(ureq, org.olat.core.gui.components.form.Form.EVNT_VALIDATION_OK);
}
public void submitAndNext(UserRequest ureq) {
submit(ureq, org.olat.core.gui.components.form.Form.EVNT_VALIDATION_NEXT);
}
public void submitAndFinish(UserRequest ureq) {
submit(ureq, org.olat.core.gui.components.form.Form.EVNT_VALIDATION_FINISH);
}
private final void submit(UserRequest ureq, Event validationOkEvent) {
ValidatingFormComponentVisitor vfcv = new ValidatingFormComponentVisitor();
FormComponentTraverser ct = new FormComponentTraverser(vfcv, formLayout, false);
ct.visitAll(ureq);
// validate all form elements and gather validation status
ValidationStatus[] status = vfcv.getStatus();
//
boolean isValid = status == null || status.length == 0;
// let the businesslogic validate this is implemented by the outside listener
for (Iterator<FormBasicController> iterator = formListeners.iterator(); iterator.hasNext();) {
FormBasicController fbc = iterator.next();
//let all listeners validate and calc the total isValid
//let further validate even if one fails.
isValid = fbc.validateFormLogic(ureq) && isValid;
}
formWrapperComponent.fireValidation(ureq, isValid, validationOkEvent);
isValidAndSubmitted = isValid;
hasAlreadyFired = true;
}
/**
* @param ureq
*/
public void reset(UserRequest ureq) {
ResettingFormComponentVisitor rfcv = new ResettingFormComponentVisitor();
FormComponentTraverser ct = new FormComponentTraverser(rfcv, formLayout, false);
ct.visitAll(ureq);//calls reset on all elements!
//
evalAllFormDependencyRules(ureq);
//
formWrapperComponent.fireFormEvent(ureq, FormEvent.RESET);
hasAlreadyFired = true;
}
/**
* @return
*/
ComponentCollection getFormLayout() {
return (ComponentCollection) formLayout.getComponent();
}
FormItemContainer getFormItemContainer() {
return formLayout;
}
public Component getInitialComponent() {
return formWrapperComponent;
}
/**
* add another listener then the default listener, which is added at construction
* time.
* @param listener
*/
public void addListener(Controller listener){
formWrapperComponent.addListener(listener);
}
public void removeListener(Controller listener){
formWrapperComponent.removeListener(listener);
}
/**
* Return the form parameter for a certain key. This takes care if a multipart
* form has been used or a normal form.
* <p>
* LiveCycle scope: only within one call of evalFormRequest() !
*
* @param key
* @return the value of the parameter with key 'key'
*/
public String getRequestParameter(String key) {
String[] values = requestParams.get(key);
if (values != null) return values[0];
else return null;
}
/**
* Return the form parameter values for a certain key. This takes care if a
* multipart form has been used or a normal form. <br />
* This method is used to retrieve multi-value elements, e.g. radio buttons.<br />
* Use the getRequestParameter() to retrieve single value elements, e.g. input
* type=text elements
*
* @param key
* @return Array of values for this key
*/
public String[] getRequestParameterValues(String key) {
return requestParams.get(key);
}
/**
* Return the form parameter set. This takes care if a multipart form has been
* used or a normal form.
* <p>
* LiveCycle scope: only within one call of evalFormRequest() !
*
* @return the Set of parameters
*/
public Set<String> getRequestParameterSet() {
return requestParams.keySet();
}
/**
* Return the multipart file for this key
* <p>LiveCycle scope: only within one call of evalFormRequest() !
* @param key
* @return
*/
public File getRequestMultipartFile(String key) {
return requestMultipartFiles.get(key);
}
public MultipartFileInfos getRequestMultipartFileInfos(String key) {
File file = requestMultipartFiles.get(key);
String mimeType = requestMultipartFileMimeTypes.get(key);
String filename = requestMultipartFileNames.get(key);
return new MultipartFileInfos(file, filename, mimeType);
}
/**
* Return the multipart file name for this key:
* <p>LiveCycle scope: only within one call of evalFormRequest() !
* @param key
* @return
*/
public String getRequestMultipartFileName(String key) {
return requestMultipartFileNames.get(key);
}
/**
* Return the multipart file mime type (content type) for this key:
* <p>LiveCycle scope: only within one call of evalFormRequest() !
* @param key
* @return
*/
public String getRequestMultipartFileMimeType(String key) {
return requestMultipartFileMimeTypes.get(key);
}
/**
* @return The set of multipart file identifyers
*/
public Set<String> getRequestMultipartFilesSet() {
return requestMultipartFiles.keySet();
}
/**
* Description:<br>
* TODO: patrickb Class Description for EvaluatingFormComponentVisitor
* <P>
* Initial Date: 04.12.2006 <br>
*
* @author patrickb
*/
private class EvaluatingFormComponentVisitor implements FormComponentVisitor {
private boolean foundDispatchItem = false;
private FormItem dispatchFormItem = null;
private String dispatchId;
public EvaluatingFormComponentVisitor(String dispatchUri) {
this.dispatchId = dispatchUri;
}
public FormItem getDispatchToComponent() {
return dispatchFormItem;
}
@Override
public boolean visit(FormItem fi, UserRequest ureq) {
/*
* check if this is the FormItem to be dispatched
*/
Component tmp = fi.getComponent();
String tmpD = FormBaseComponentIdProvider.DISPPREFIX + tmp.getDispatchID();
if (!foundDispatchItem && tmpD.equals(dispatchId)) {
dispatchFormItem = fi;
foundDispatchItem = true;
}
/*
* let the form item evaluate the form request, e.g. get out its data
*/
fi.evalFormRequest(ureq);
return true;// visit further
}
}
private static class SubmitFormComponentVisitor implements FormComponentVisitor {
private Submit submit;
public Submit getSubmit() {
return submit;
}
@Override
public boolean visit(FormItem fi, UserRequest ureq) {
if(fi instanceof Submit) {
submit = (Submit)fi;
return false;
}
return true;
}
}
private class FindParentFormComponentVisitor implements FormComponentVisitor {
private FormItem child;
private FormItemContainer parentContainer = formLayout;
public FindParentFormComponentVisitor(FormItem child){
this.child = child;
}
public FormItemContainer getParent(){
return parentContainer;
}
public boolean visit(FormItem comp, UserRequest ureq) {
if (comp instanceof FormItemContainer) {
FormItemContainer new_name = (FormItemContainer) comp;
if(new_name.hasFormComponent(child)){
parentContainer = (FormItemContainer)comp;
return false;
}
}
return true;
}
}
private static class FormDependencyRulesInitComponentVisitor implements FormComponentVisitor {
public boolean visit(FormItem comp, UserRequest ureq) {
if (comp instanceof FormItemContainer) {
FormItemContainer fic = (FormItemContainer)comp;
Iterable<FormItem> pairs = fic.getFormItems();
//go to next container if no elements inside
if(pairs != null) {
//otherwise iterate overall elements and evaluate dependency rules
for (FormItem item : pairs) {
fic.evalDependencyRuleSetFor(ureq, item);
}
}
}
return true;
}
}
private static class ValidatingFormComponentVisitor implements FormComponentVisitor {
final List<ValidationStatus> tmp = new ArrayList<ValidationStatus>();
public ValidationStatus[] getStatus() {
return sort(tmp);
}
@Override
public boolean visit(FormItem comp, UserRequest ureq) {
if(comp.isVisible() && comp.isEnabled()){
//validate only if form item is visible and enabled
comp.validate(tmp);
}
return true;
}
private ValidationStatus[] sort(List<ValidationStatus> statusList) {
Collections.sort(statusList, new ErrorsGtWarningGtInfo());
/*
* remove NOERRORS size bigger 1; NOERROR -> Level == OFF > SEVERE > WARNING >
* INFO
*/
if (statusList.size() > 1) {
for(int i=0;i<statusList.size();i++) {
if(statusList.get(i)==ValidationStatus.NOERROR) {
//aha found one to remove. Remove shifts all elements to the left, e.g. subtracts one of the indices
statusList.remove(i);
//thus we have to loop on place to remove all NOERRORS which shift to the ith place.
while(statusList.get(i)==ValidationStatus.NOERROR) {
statusList.remove(i);
}
}
}
}
return statusList.toArray(new ValidationStatus[statusList.size()]);
}
private static class ErrorsGtWarningGtInfo implements Comparator<ValidationStatus> {
@Override
public int compare(ValidationStatus s1, ValidationStatus s2) {
return s2.getLevel().intValue() - s1.getLevel().intValue();
}
}
}
private static class ResettingFormComponentVisitor implements FormComponentVisitor {
public boolean visit(FormItem comp, UserRequest ureq) {
//reset all fields including also non visible and disabled form items!
comp.reset();
return true;
}
}
public String getFormId() {
return formWrapperComponent.getDispatchID();
}
public String getDispatchFieldId() {
return dispatchFieldId;
}
public String getFormName() {
return formName;
}
public void fireFormEvent(UserRequest ureq, FormEvent event) {
formWrapperComponent.fireFormEvent(ureq, event);
hasAlreadyFired = true;
}
boolean hasAlreadyFired(){
return hasAlreadyFired;
}
/**
* @return Returns the eventFieldId.
*/
public String getEventFieldId() {
return eventFieldId;
}
public int getAction() {
return action;
}
/**
* @param ureq
*/
void evalAllFormDependencyRules(UserRequest ureq) {
FormDependencyRulesInitComponentVisitor fdrocv = new FormDependencyRulesInitComponentVisitor();
FormComponentTraverser ct = new FormComponentTraverser(fdrocv, formLayout, false);
ct.visitAll(ureq);//visit all container and eval container with its elements!
}
public boolean isSubmittedAndValid(){
return isValidAndSubmitted;
}
public void forceSubmittedAndValid() {
isValidAndSubmitted = true;
}
/**
* true if the form should not loose unsubmitted changes, if another link
* is clicked which throws away the changes.
* @return
*/
public boolean isDirtyMarking() {
return isDirtyMarking;
}
public void setDirtyMarking(boolean isDirtyMarking){
this.isDirtyMarking = isDirtyMarking;
}
/**
* By default a dirty-marking renders the submit button differently as soon
* as the form gets dirty plus it shows a message to the user when he did
* not save the form but tries to navigate to some other places. Without the
* message he would loose data. However, in search forms or the loginform
* this message is not desired. Set this variable to true to prevent the
* message from popping up.
*
* @return
*/
public boolean isHideDirtyMarkingMessage() {
return isHideDirtyMarkingMessage;
}
public void setHideDirtyMarkingMessage(boolean isHideDirtyMarkingMessage) {
this.isHideDirtyMarkingMessage = isHideDirtyMarkingMessage;
}
public void addSubFormListener(FormBasicController formBasicController) {
this.formListeners.add(formBasicController);
addListener(formBasicController);
}
public void removeSubFormListener(FormBasicController formBasicController) {
this.formListeners.remove(formBasicController);
removeListener(formBasicController);
}
public void setMultipartEnabled(boolean multipartEnabled) {
this.multipartEnabled = multipartEnabled;
}
public boolean isMultipartEnabled() {
return multipartEnabled;
}
}