From f48e6926f306feff0978b45fb37de2b8fe9d8cb3 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Fri, 23 Dec 2016 16:36:27 +0100
Subject: [PATCH] OO-2329: add validation and confirmation to the evaluation
 form

---
 .../flexible/elements/SingleSelection.java    |  4 +
 .../impl/elements/SelectboxSelectionImpl.java | 13 ++-
 .../impl/elements/SingleSelectionImpl.java    | 13 ++-
 .../modules/forms/handler/HTMLRawHandler.java |  7 +-
 .../modules/forms/handler/RubricHandler.java  |  8 +-
 .../modules/forms/handler/SpacerHandler.java  |  7 +-
 .../forms/handler/TextInputHandler.java       |  9 +-
 .../modules/forms/handler/TitleHandler.java   |  7 +-
 .../forms/ui/EvaluationFormController.java    | 94 +++++++++++++++++--
 .../forms/ui/_i18n/LocalStrings_de.properties |  2 +
 .../forms/ui/_i18n/LocalStrings_en.properties |  2 +
 .../handler/AbstractMediaHandler.java         |  9 +-
 .../handler/EvaluationFormHandler.java        | 39 ++++----
 .../portfolio/handler/ImageHandler.java       |  9 +-
 .../portfolio/handler/VideoHandler.java       |  9 +-
 .../portfolio/ui/PageRunController.java       | 17 +++-
 .../portfolio/ui/editor/PageController.java   | 32 +++++--
 .../ui/editor/PageElementHandler.java         |  3 +-
 .../portfolio/ui/editor/PageRunComponent.java | 50 ++++++++++
 .../ui/editor/PageRunControllerElement.java   | 54 +++++++++++
 .../portfolio/ui/editor/PageRunElement.java   | 42 +++++++++
 .../ui/editor/ValidatingController.java       | 38 ++++++++
 .../ui/editor/ValidationMessage.java          | 49 ++++++++++
 .../handler/HTMLRawPageElementHandler.java    |  7 +-
 .../editor/handler/SpacerElementHandler.java  |  7 +-
 .../handler/TitlePageElementHandler.java      |  7 +-
 26 files changed, 469 insertions(+), 69 deletions(-)
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/editor/PageRunComponent.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/editor/PageRunControllerElement.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/editor/PageRunElement.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/editor/ValidatingController.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/editor/ValidationMessage.java

diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/elements/SingleSelection.java b/src/main/java/org/olat/core/gui/components/form/flexible/elements/SingleSelection.java
index 154fdbe0d71..8afbd05f98f 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/elements/SingleSelection.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/elements/SingleSelection.java
@@ -56,6 +56,10 @@ public interface SingleSelection extends SelectionElement {
 	 * @param width
 	 */
 	public void setWidthInPercent(int width);
+	
+	public boolean isAllowNoSelection();
+	
+	public void setAllowNoSelection(boolean allowNoSelection);
 
 	
 	/**
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SelectboxSelectionImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SelectboxSelectionImpl.java
index 1013aa8787f..032828c9454 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SelectboxSelectionImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SelectboxSelectionImpl.java
@@ -49,6 +49,7 @@ public class SelectboxSelectionImpl extends FormItemImpl implements SingleSelect
 	private String original = null;
 	private boolean originalSelect = false;
 	private int selectedIndex = -1;
+	private boolean allowNoSelection = false;
 
 	private final SelectboxComponent component;
 	
@@ -155,6 +156,16 @@ public class SelectboxSelectionImpl extends FormItemImpl implements SingleSelect
 	public String getValue(int which) {
 		return values[which];
 	}
+	
+	@Override
+	public boolean isAllowNoSelection() {
+		return allowNoSelection;
+	}
+
+	@Override
+	public void setAllowNoSelection(boolean allowNoSelection) {
+		this.allowNoSelection = allowNoSelection;
+	}
 
 	/**
 	 * @see org.olat.core.gui.components.form.flexible.elements.SelectionContainer#isSelected(int)
@@ -227,7 +238,7 @@ public class SelectboxSelectionImpl extends FormItemImpl implements SingleSelect
 	
 	@Override
 	public void validate(List<ValidationStatus> validationResults) {
-		if (isVisible() && !isOneSelected()) {
+		if (!allowNoSelection && isVisible() && !isOneSelected()) {
 			validationResults.add(new ValidationStatusImpl(ValidationStatus.ERROR));
 			return;
 		}
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SingleSelectionImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SingleSelectionImpl.java
index a1d3a36ebb5..894b6fbd238 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SingleSelectionImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/SingleSelectionImpl.java
@@ -50,6 +50,7 @@ public class SingleSelectionImpl extends FormItemImpl implements SingleSelection
 	private String original = null;
 	private boolean originalSelect = false;
 	private int selectedIndex = -1;
+	private boolean allowNoSelection = false;
 
 	private final Layout layout;
 	private final SingleSelectionComponent component;
@@ -163,6 +164,16 @@ public class SingleSelectionImpl extends FormItemImpl implements SingleSelection
 		component.setWidthInPercent(width);
 	}
 
+	@Override
+	public boolean isAllowNoSelection() {
+		return allowNoSelection;
+	}
+
+	@Override
+	public void setAllowNoSelection(boolean allowNoSelection) {
+		this.allowNoSelection = allowNoSelection;
+	}
+
 	/**
 	 * @see org.olat.core.gui.components.form.flexible.elements.SelectionContainer#isSelected(int)
 	 */
@@ -230,7 +241,7 @@ public class SingleSelectionImpl extends FormItemImpl implements SingleSelection
 	
 	@Override
 	public void validate(List<ValidationStatus> validationResults) {
-		if (isVisible() && !isOneSelected()) {
+		if (!allowNoSelection && isVisible() && !isOneSelected()) {
 			validationResults.add(new ValidationStatusImpl(ValidationStatus.ERROR));
 			return;
 		}
diff --git a/src/main/java/org/olat/modules/forms/handler/HTMLRawHandler.java b/src/main/java/org/olat/modules/forms/handler/HTMLRawHandler.java
index 5cc37603970..517f1eaf5f9 100644
--- a/src/main/java/org/olat/modules/forms/handler/HTMLRawHandler.java
+++ b/src/main/java/org/olat/modules/forms/handler/HTMLRawHandler.java
@@ -31,9 +31,11 @@ import org.olat.core.util.CodeHelper;
 import org.olat.core.util.Util;
 import org.olat.modules.forms.model.xml.HTMLRaw;
 import org.olat.modules.forms.ui.HTMLRawEditorController;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 
 /**
@@ -55,12 +57,13 @@ public class HTMLRawHandler implements PageElementHandler, SimpleAddPageElementH
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		String content = "";
 		if(element instanceof HTMLRaw) {
 			content = ((HTMLRaw)element).getContent();
 		}
-		return TextFactory.createTextComponentFromString("htmlraw_" + CodeHelper.getRAMUniqueID(), content, null, false, null);
+		Component cmp = TextFactory.createTextComponentFromString("htmlraw_" + CodeHelper.getRAMUniqueID(), content, null, false, null);
+		return new PageRunComponent(cmp);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/forms/handler/RubricHandler.java b/src/main/java/org/olat/modules/forms/handler/RubricHandler.java
index 7ba9e23e7d0..588b968f1ec 100644
--- a/src/main/java/org/olat/modules/forms/handler/RubricHandler.java
+++ b/src/main/java/org/olat/modules/forms/handler/RubricHandler.java
@@ -23,7 +23,6 @@ import java.util.Locale;
 import java.util.UUID;
 
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.modules.forms.model.xml.Rubric;
@@ -31,8 +30,10 @@ import org.olat.modules.forms.model.xml.Rubric.SliderType;
 import org.olat.modules.forms.model.xml.Slider;
 import org.olat.modules.forms.ui.RubricController;
 import org.olat.modules.forms.ui.RubricEditorController;
+import org.olat.modules.portfolio.ui.editor.PageRunControllerElement;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 
 /**
@@ -60,9 +61,10 @@ public class RubricHandler implements PageElementHandler, SimpleAddPageElementHa
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		if(element instanceof Rubric) {
-			return new RubricController(ureq, wControl, (Rubric)element).getInitialComponent();
+			Controller ctrl = new RubricController(ureq, wControl, (Rubric)element);
+			return new PageRunControllerElement(ctrl);
 		}
 		return null;
 	}
diff --git a/src/main/java/org/olat/modules/forms/handler/SpacerHandler.java b/src/main/java/org/olat/modules/forms/handler/SpacerHandler.java
index 558461fe9d4..77afe28ce37 100644
--- a/src/main/java/org/olat/modules/forms/handler/SpacerHandler.java
+++ b/src/main/java/org/olat/modules/forms/handler/SpacerHandler.java
@@ -29,9 +29,11 @@ import org.olat.core.gui.components.form.flexible.impl.elements.SpacerElementCom
 import org.olat.core.gui.control.WindowControl;
 import org.olat.modules.forms.model.xml.Spacer;
 import org.olat.modules.forms.ui.SpacerEditorController;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 
 /**
@@ -55,9 +57,10 @@ public class SpacerHandler implements PageElementHandler, SimpleAddPageElementHa
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		if(element instanceof Spacer) {
-			return new SpacerElementComponent("spacer_" + idGenerator.incrementAndGet());
+			Component cmp = new SpacerElementComponent("spacer_" + idGenerator.incrementAndGet());
+			return new PageRunComponent(cmp);
 		}
 		return null;
 	}
diff --git a/src/main/java/org/olat/modules/forms/handler/TextInputHandler.java b/src/main/java/org/olat/modules/forms/handler/TextInputHandler.java
index a3f03bbee5c..003242b7caa 100644
--- a/src/main/java/org/olat/modules/forms/handler/TextInputHandler.java
+++ b/src/main/java/org/olat/modules/forms/handler/TextInputHandler.java
@@ -23,14 +23,16 @@ import java.util.Locale;
 import java.util.UUID;
 
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.modules.forms.model.xml.TextInput;
 import org.olat.modules.forms.ui.TextInputController;
 import org.olat.modules.forms.ui.TextInputEditorController;
+import org.olat.modules.portfolio.ui.editor.PageRunControllerElement;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 
 /**
@@ -52,9 +54,10 @@ public class TextInputHandler implements PageElementHandler, SimpleAddPageElemen
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		if(element instanceof TextInput) {
-			return new TextInputController(ureq, wControl, (TextInput)element).getInitialComponent();
+			Controller ctrl = new TextInputController(ureq, wControl, (TextInput)element);
+			return new PageRunControllerElement(ctrl);
 		}
 		return null;
 	}
diff --git a/src/main/java/org/olat/modules/forms/handler/TitleHandler.java b/src/main/java/org/olat/modules/forms/handler/TitleHandler.java
index c88c1ed9d9d..74549c03454 100644
--- a/src/main/java/org/olat/modules/forms/handler/TitleHandler.java
+++ b/src/main/java/org/olat/modules/forms/handler/TitleHandler.java
@@ -31,9 +31,11 @@ import org.olat.core.gui.translator.Translator;
 import org.olat.core.util.Util;
 import org.olat.modules.forms.model.xml.Title;
 import org.olat.modules.forms.ui.TitleEditorController;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 
 /**
@@ -57,12 +59,13 @@ public class TitleHandler implements PageElementHandler, SimpleAddPageElementHan
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		String content = "";
 		if(element instanceof Title) {
 			content = ((Title)element).getContent();
 		}
-		return TextFactory.createTextComponentFromString("title_" + idGenerator.incrementAndGet(), content, null, false, null);
+		Component cmp = TextFactory.createTextComponentFromString("title_" + idGenerator.incrementAndGet(), content, null, false, null);
+		return new PageRunComponent(cmp);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java b/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java
index 047f3e182af..235df3b71e8 100644
--- a/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java
+++ b/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java
@@ -43,6 +43,8 @@ import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.modal.DialogBoxController;
+import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.xml.XStreamHelper;
@@ -63,6 +65,9 @@ import org.olat.modules.forms.ui.model.EvaluationFormElementWrapper;
 import org.olat.modules.forms.ui.model.SliderWrapper;
 import org.olat.modules.forms.ui.model.TextInputWrapper;
 import org.olat.modules.portfolio.PageBody;
+import org.olat.modules.portfolio.ui.editor.ValidatingController;
+import org.olat.modules.portfolio.ui.editor.ValidationMessage;
+import org.olat.modules.portfolio.ui.editor.ValidationMessage.Level;
 import org.olat.repository.RepositoryEntry;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -72,7 +77,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class EvaluationFormController extends FormBasicController {
+public class EvaluationFormController extends FormBasicController implements ValidatingController {
 
 	private int count = 0;
 	private final Form form;
@@ -83,10 +88,13 @@ public class EvaluationFormController extends FormBasicController {
 	private final RepositoryEntry formEntry;
 	
 	private EvaluationFormSession session;
+	private List<EvaluationFormElementWrapper> elementWrapperList = new ArrayList<>();
 	private final Map<String, EvaluationFormResponse> identifierToResponses = new HashMap<>();
 	
 	private FormSubmit saveAsDoneButton;
 	
+	private DialogBoxController confirmDoneCtrl;
+	
 	@Autowired
 	private DB dbInstance;
 	@Autowired
@@ -155,7 +163,7 @@ public class EvaluationFormController extends FormBasicController {
 				elementWrappers.add(wrapper);
 			}
 		}
-
+		elementWrapperList = elementWrappers;
 		flc.contextPut("elements", elementWrappers);
 	}
 	
@@ -303,6 +311,7 @@ public class EvaluationFormController extends FormBasicController {
 		radioEl.setDomReplacementWrapperRequired(false);
 		radioEl.addActionListener(FormEvent.ONCHANGE);
 		radioEl.setEnabled(!readOnly);
+		radioEl.setAllowNoSelection(true);
 		int widthInPercent = EvaluationFormElementWrapper.getWidthInPercent(element);
 		radioEl.setWidthInPercent(widthInPercent);
 		if(response != null && response.getNumericalResponse() != null) {
@@ -326,15 +335,71 @@ public class EvaluationFormController extends FormBasicController {
 		//super.propagateDirtinessToContainer(fiSrc, fe);
 	}
 
+	@Override
+	public boolean validate(UserRequest ureq, List<ValidationMessage> messages) {
+		boolean allFiled = true;
+		for(EvaluationFormElementWrapper elementWrapper:elementWrapperList) {
+			if(elementWrapper.isTextInput()) {
+				TextInputWrapper wrapper = elementWrapper.getTextInputWrapper();
+				if(wrapper != null && !hasResponse(wrapper.getId())) {
+					allFiled &= false;
+				}
+			} else if(elementWrapper.getSliders() != null && elementWrapper.getSliders().size() > 0) {
+				for(SliderWrapper slider:elementWrapper.getSliders()) {
+					if(slider != null && !hasResponse(slider.getId())) {
+						allFiled &= false;
+					}
+				}
+			}
+		}
+		
+		if(!allFiled) {
+			String msg = translate("warning.form.not.completed");
+			messages.add(new ValidationMessage(Level.warning, msg));
+		}
+		return validateFormLogic(ureq);
+	}
+	
+	private boolean hasResponse(String id) {
+		if(id == null) return true;//not a field
+		
+		if(!identifierToResponses.containsKey(id)) {
+			return false;
+		}
+		EvaluationFormResponse response = identifierToResponses.get(id);
+		if(response == null ||
+				(response.getNumericalResponse() == null && !StringHelper.containsNonWhitespace(response.getStringuifiedResponse()))) {
+			return false;
+		}
+		return true;
+	}
+
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = true;
+		
+		return allOk & super.validateFormLogic(ureq);
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(confirmDoneCtrl == source) {
+			if(DialogBoxUIFactory.isYesEvent(event)) {
+				saveAsDone(ureq);
+			}
+		}
+		super.event(ureq, source, event);
+	}
+
 	@Override
 	protected void formOK(UserRequest ureq) {
-		doSaveAsDone(ureq);
+		doConfirmDone(ureq);
 	}
 
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
 		if(saveAsDoneButton == source) {
-			doSaveAsDone(ureq);
+			doConfirmDone(ureq);
 		} else if(source instanceof SingleSelection) {
 			SingleSelection radioEl = (SingleSelection)source;
 			Object uobject = radioEl.getUserObject();
@@ -379,10 +444,8 @@ public class EvaluationFormController extends FormBasicController {
 		}
 	}
 	
-	private void doSaveAsDone(UserRequest ureq) {
-		@SuppressWarnings("unchecked")
-		List<EvaluationFormElementWrapper> elementWrappers = (List<EvaluationFormElementWrapper>)flc.contextGet("elements");
-		for(EvaluationFormElementWrapper elementWrapper:elementWrappers) {
+	private void doConfirmDone(UserRequest ureq) {
+		for(EvaluationFormElementWrapper elementWrapper:elementWrapperList) {
 			if(elementWrapper.isTextInput()) {
 				TextInputWrapper wrapper = elementWrapper.getTextInputWrapper();
 				String value = wrapper.getTextEl().getValue();
@@ -390,6 +453,20 @@ public class EvaluationFormController extends FormBasicController {
 			}	
 		}
 		
+		StringBuilder sb = new StringBuilder();
+		sb.append("<p>").append(translate("confirm.done")).append("</p>");
+		
+		List<ValidationMessage> messages = new ArrayList<>();
+		validate(ureq, messages);
+		if(messages.size() > 0) {
+			for(ValidationMessage message:messages) {
+				sb.append("<p class='o_warning'>").append(message.getMessage()).append("</p>");
+			}
+		}
+		confirmDoneCtrl = activateYesNoDialog(ureq, null, sb.toString(), confirmDoneCtrl);
+	}
+	
+	private void saveAsDone(UserRequest ureq) {
 		//save text inputs
 		session = evaluationFormManager.changeSessionStatus(session, EvaluationFormSessionStatus.done);
 		readOnly = true;
@@ -398,7 +475,6 @@ public class EvaluationFormController extends FormBasicController {
 		updateElements();
 		saveAsDoneButton.setVisible(false);
 		dbInstance.commit();
-		
 		fireEvent(ureq, Event.DONE_EVENT);
 	}
 
diff --git a/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_de.properties
index 1840ac46fc1..8c06e768e24 100644
--- a/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_de.properties
@@ -4,6 +4,7 @@ add.formhtitle=Titel
 add.formrubric=Rubrik
 add.slider=Slider addieren
 end.label=End
+confirm.done=Wollen Sie die Einsch\u00E4tzung abgeben? Sobald sie abgegeben ist, kann sie nicht mehr bearbeitet werden\!
 evaluation.form.in.use=Die Ressource wird bereits f\u00FCr Einsch\u00E4tzung verwendet. Die Bearbeitung ist begrenzt.
 evaluation.in.progress=Die Einsch\u00E4tzung ist gerade in Bearbeitung
 evaluator=Benutzer {0}
@@ -27,3 +28,4 @@ slider.type=Typ
 start.label=Beginn
 textinput.rows=Zeile
 title.example=<h1>Anklicken um Titel zu bearbeiten</h1>
+warning.form.not.completed=Achtung! Sie haben nicht alle Felder ausgefüllt.
diff --git a/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_en.properties
index 94c8b3cb7e0..0c5bd5ac7fd 100644
--- a/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/forms/ui/_i18n/LocalStrings_en.properties
@@ -4,6 +4,7 @@ add.formhtitle=Title
 add.formrubric=Rubric
 add.slider=Add slider
 end.label=End
+confirm.done=Do want to submit your evaluation? Once it is submitted, it cannot be edited anymore\!
 evaluation.form.in.use=The resource is already used for assessment purpose. Editing is limited.
 evaluation.in.progress=The evaluation is in progress
 evaluator=User {0}
@@ -27,3 +28,4 @@ slider.type=Type
 start.label=Start
 textinput.rows=Rows
 title.example=<h1>Click to edit title</h1>
+warning.form.not.completed=Achtung! Sie haben nicht alle Felder ausgefüllt.
diff --git a/src/main/java/org/olat/modules/portfolio/handler/AbstractMediaHandler.java b/src/main/java/org/olat/modules/portfolio/handler/AbstractMediaHandler.java
index 1909871ebfb..aa48122ad1d 100644
--- a/src/main/java/org/olat/modules/portfolio/handler/AbstractMediaHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/handler/AbstractMediaHandler.java
@@ -20,7 +20,6 @@
 package org.olat.modules.portfolio.handler;
 
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.modules.portfolio.Media;
@@ -28,8 +27,10 @@ import org.olat.modules.portfolio.MediaHandler;
 import org.olat.modules.portfolio.MediaInformations;
 import org.olat.modules.portfolio.MediaLight;
 import org.olat.modules.portfolio.model.MediaPart;
+import org.olat.modules.portfolio.ui.editor.PageRunControllerElement;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 
 /**
  * 
@@ -56,13 +57,13 @@ public abstract class AbstractMediaHandler implements MediaHandler, PageElementH
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		if(element instanceof Media) {
-			return getMediaController(ureq, wControl, (Media)element).getInitialComponent();
+			return new PageRunControllerElement(getMediaController(ureq, wControl, (Media)element));
 		}
 		if(element instanceof MediaPart) {
 			MediaPart mediaPart = (MediaPart)element;
-			return getMediaController(ureq, wControl, mediaPart.getMedia()).getInitialComponent();
+			return new PageRunControllerElement(getMediaController(ureq, wControl, mediaPart.getMedia()));
 		}
 		return null;
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/handler/EvaluationFormHandler.java b/src/main/java/org/olat/modules/portfolio/handler/EvaluationFormHandler.java
index e58dc006184..500df59e0c9 100644
--- a/src/main/java/org/olat/modules/portfolio/handler/EvaluationFormHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/handler/EvaluationFormHandler.java
@@ -28,10 +28,8 @@ import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
-import org.olat.core.gui.control.generic.messages.MessageController;
 import org.olat.core.gui.control.generic.messages.MessageUIFactory;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
@@ -48,8 +46,10 @@ import org.olat.modules.portfolio.model.AccessRights;
 import org.olat.modules.portfolio.model.EvaluationFormPart;
 import org.olat.modules.portfolio.ui.MultiEvaluationFormController;
 import org.olat.modules.portfolio.ui.PortfolioHomeController;
+import org.olat.modules.portfolio.ui.editor.PageRunControllerElement;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.repository.RepositoryEntry;
 
 /**
@@ -71,7 +71,8 @@ public class EvaluationFormHandler implements PageElementHandler {
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+		Controller ctrl = null;
 		if(element instanceof EvaluationFormPart) {
 			PortfolioService portfolioService = CoreSpringFactory.getImpl(PortfolioService.class);
 			
@@ -87,24 +88,23 @@ public class EvaluationFormHandler implements PageElementHandler {
 			
 			List<AccessRights> accessRights = portfolioService.getAccessRights(page);
 			boolean anonym = assignment.isAnonymousExternalEvaluation();
-
 			if(pageStatus == null || pageStatus == PageStatus.draft) {
 				if(hasRole(PortfolioRoles.owner, ureq.getIdentity(), accessRights)) {
-					return new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, false, false).getInitialComponent();
+					ctrl = new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, false, false);
 				}
 			} else if (assignment.isOnlyAutoEvaluation()) {
 				// only the auto evaluation is shown
 				if(hasRole(PortfolioRoles.owner, ureq.getIdentity(), accessRights)) {
 					boolean readOnly = (pageStatus == PageStatus.published) || (pageStatus == PageStatus.closed) || (pageStatus == PageStatus.deleted);
-					return new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, readOnly, false).getInitialComponent();
+					ctrl =  new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, readOnly, false);
 				} else if(hasRole(PortfolioRoles.coach, ureq.getIdentity(), accessRights)) {
 					Identity owner = getOwner(accessRights);
-					return new EvaluationFormController(ureq, wControl, owner, body, re, true, false).getInitialComponent();
+					ctrl =  new EvaluationFormController(ureq, wControl, owner, body, re, true, false);
 				} else if(hasRole(PortfolioRoles.reviewer, ureq.getIdentity(), accessRights)
 						|| hasRole(PortfolioRoles.invitee, ureq.getIdentity(), accessRights)) {
 					if(assignment.isReviewerSeeAutoEvaluation()) {
 						Identity owner = getOwner(accessRights);
-						return new EvaluationFormController(ureq, wControl, owner, body, re, true, false).getInitialComponent();
+						ctrl = new EvaluationFormController(ureq, wControl, owner, body, re, true, false);
 					}
 				}
 			} else {
@@ -113,33 +113,36 @@ public class EvaluationFormHandler implements PageElementHandler {
 					Identity owner = getOwner(accessRights);
 					List<Identity> coachesAndReviewers = getCoachesAndReviewers(accessRights);
 					if(coachesAndReviewers.size() > 0) {
-						return new MultiEvaluationFormController(ureq, wControl, owner, coachesAndReviewers, body, re, false, readOnly, anonym).getInitialComponent();
+						ctrl = new MultiEvaluationFormController(ureq, wControl, owner, coachesAndReviewers, body, re, false, readOnly, anonym);
+					} else {
+						ctrl = new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, readOnly, false);
 					}
-					return new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, readOnly, false).getInitialComponent();
 				} else if(hasRole(PortfolioRoles.coach, ureq.getIdentity(), accessRights)) {
 					Identity owner = getOwner(accessRights);
 					List<Identity> coachesAndReviewers = getCoachesAndReviewers(accessRights);
 					boolean readOnly = (pageStatus == PageStatus.draft) || (pageStatus == PageStatus.closed) || (pageStatus == PageStatus.deleted);
-					return new MultiEvaluationFormController(ureq, wControl, owner, coachesAndReviewers, body, re, false, readOnly, anonym).getInitialComponent();
+					ctrl = new MultiEvaluationFormController(ureq, wControl, owner, coachesAndReviewers, body, re, false, readOnly, anonym);
 				} else if(hasRole(PortfolioRoles.reviewer, ureq.getIdentity(), accessRights)
 						|| hasRole(PortfolioRoles.invitee, ureq.getIdentity(), accessRights)) {
 					boolean readOnly = (pageStatus == PageStatus.draft) || (pageStatus == PageStatus.closed) || (pageStatus == PageStatus.deleted);
 					if(assignment.isReviewerSeeAutoEvaluation()) {
 						Identity owner = getOwner(accessRights);
 						List<Identity> reviewers = Collections.singletonList(ureq.getIdentity());
-						return new MultiEvaluationFormController(ureq, wControl, owner, reviewers, body, re, true, readOnly, anonym).getInitialComponent();
+						ctrl = new MultiEvaluationFormController(ureq, wControl, owner, reviewers, body, re, true, readOnly, anonym);
 					} else {
-						return new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, readOnly, !readOnly).getInitialComponent();
+						ctrl = new EvaluationFormController(ureq, wControl, ureq.getIdentity(), body, re, readOnly, !readOnly);
 					}
 				}
 			}
 		}
 		
-		Translator translator = Util.createPackageTranslator(PortfolioHomeController.class, ureq.getLocale());
-		String title = translator.translate("warning.evaluation.not.visible.title");
-		String text = translator.translate("warning.evaluation.not.visible.text");
-		MessageController msg = MessageUIFactory.createWarnMessage(ureq, wControl, title, text);
-		return msg.getInitialComponent();
+		if(ctrl == null) {
+			Translator translator = Util.createPackageTranslator(PortfolioHomeController.class, ureq.getLocale());
+			String title = translator.translate("warning.evaluation.not.visible.title");
+			String text = translator.translate("warning.evaluation.not.visible.text");
+			ctrl = MessageUIFactory.createWarnMessage(ureq, wControl, title, text);
+		}
+		return new PageRunControllerElement(ctrl);
 	}
 	
 	private Identity getOwner(List<AccessRights> accessRights) {
diff --git a/src/main/java/org/olat/modules/portfolio/handler/ImageHandler.java b/src/main/java/org/olat/modules/portfolio/handler/ImageHandler.java
index 22f12c1a5b3..99edd1a01ba 100644
--- a/src/main/java/org/olat/modules/portfolio/handler/ImageHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/handler/ImageHandler.java
@@ -29,7 +29,6 @@ import org.olat.core.commons.modules.bc.meta.MetaInfo;
 import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged;
 import org.olat.core.commons.services.image.Size;
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.image.ImageComponent;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
@@ -48,9 +47,11 @@ import org.olat.modules.portfolio.PortfolioLoggingAction;
 import org.olat.modules.portfolio.manager.MediaDAO;
 import org.olat.modules.portfolio.manager.PortfolioFileStorage;
 import org.olat.modules.portfolio.model.MediaPart;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
 import org.olat.modules.portfolio.ui.editor.InteractiveAddPageElementHandler;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementAddController;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.media.CollectImageMediaController;
 import org.olat.modules.portfolio.ui.media.ImageMediaController;
 import org.olat.modules.portfolio.ui.media.UploadMedia;
@@ -169,13 +170,13 @@ public class ImageHandler extends AbstractMediaHandler implements InteractiveAdd
 	}
 	
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {	
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {	
 		if(element instanceof Media) {
-			return getContent(ureq, (Media)element);
+			return new PageRunComponent(getContent(ureq, (Media)element));
 		}
 		if(element instanceof MediaPart) {
 			MediaPart mediaPart = (MediaPart)element;
-			return getContent(ureq, mediaPart.getMedia());
+			return new PageRunComponent(getContent(ureq, mediaPart.getMedia()));
 		}
 		return null;
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/handler/VideoHandler.java b/src/main/java/org/olat/modules/portfolio/handler/VideoHandler.java
index 20f0022c0d9..7afcba02ca2 100644
--- a/src/main/java/org/olat/modules/portfolio/handler/VideoHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/handler/VideoHandler.java
@@ -29,7 +29,6 @@ import org.olat.core.commons.modules.bc.meta.MetaInfo;
 import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged;
 import org.olat.core.commons.services.image.Size;
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.image.ImageComponent;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
@@ -48,9 +47,11 @@ import org.olat.modules.portfolio.PortfolioLoggingAction;
 import org.olat.modules.portfolio.manager.MediaDAO;
 import org.olat.modules.portfolio.manager.PortfolioFileStorage;
 import org.olat.modules.portfolio.model.MediaPart;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
 import org.olat.modules.portfolio.ui.editor.InteractiveAddPageElementHandler;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementAddController;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.media.CollectVideoMediaController;
 import org.olat.modules.portfolio.ui.media.UploadMedia;
 import org.olat.modules.portfolio.ui.media.VideoMediaController;
@@ -167,13 +168,13 @@ public class VideoHandler extends AbstractMediaHandler implements InteractiveAdd
 	}
 	
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {	
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {	
 		if(element instanceof Media) {
-			return getContent(ureq, (Media)element);
+			return new PageRunComponent(getContent(ureq, (Media)element));
 		}
 		if(element instanceof MediaPart) {
 			MediaPart mediaPart = (MediaPart)element;
-			return getContent(ureq, mediaPart.getMedia());
+			return new PageRunComponent(getContent(ureq, mediaPart.getMedia()));
 		}
 		return null;
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/PageRunController.java b/src/main/java/org/olat/modules/portfolio/ui/PageRunController.java
index 52f88919f85..6bbf6d00358 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/PageRunController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/PageRunController.java
@@ -69,7 +69,9 @@ import org.olat.modules.portfolio.ui.editor.PageElementAddController;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
 import org.olat.modules.portfolio.ui.editor.PageProvider;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
+import org.olat.modules.portfolio.ui.editor.ValidationMessage;
 import org.olat.modules.portfolio.ui.editor.handler.HTMLRawPageElementHandler;
 import org.olat.modules.portfolio.ui.editor.handler.SpacerElementHandler;
 import org.olat.modules.portfolio.ui.editor.handler.TitlePageElementHandler;
@@ -371,8 +373,21 @@ public class PageRunController extends BasicController implements TooledControll
 	}
 	
 	private void doConfirmPublish(UserRequest ureq) {
+		List<ValidationMessage> messages = new ArrayList<>();
+		pageCtrl.validateElements(ureq, messages);
+		
 		String title = translate("publish.confirm.title");
 		String text = translate("publish.confirm.descr", new String[]{ StringHelper.escapeHtml(page.getTitle()) });
+		
+		if(messages.size() > 0) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("<p>").append(text).append("</p>");
+			for(ValidationMessage message:messages) {
+				sb.append("<p class='o_warning'>").append(message.getMessage()).append("</p>");
+			}
+			text = sb.toString();
+		}
+
 		confirmPublishCtrl = activateYesNoDialog(ureq, title, text, confirmPublishCtrl);
 	}
 	
@@ -625,7 +640,7 @@ public class PageRunController extends BasicController implements TooledControll
 		}
 
 		@Override
-		public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+		public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 			return null;
 		}
 
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/PageController.java b/src/main/java/org/olat/modules/portfolio/ui/editor/PageController.java
index d203ae0d131..62752bc1d77 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/editor/PageController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/PageController.java
@@ -68,16 +68,26 @@ public class PageController extends BasicController {
 		//
 	}
 	
+	public boolean validateElements(UserRequest ureq, List<ValidationMessage> messages) {
+		boolean allOk = true;
+		for(PageFragment fragment:fragments) {
+			if(!fragment.validate(ureq, messages)) {
+				allOk &= false;
+			}
+		}
+		return allOk;
+	}
+	
 	public void loadElements(UserRequest ureq) {
 		List<? extends PageElement> elements = provider.getElements();
 		List<PageFragment> newFragments = new ArrayList<>(elements.size());
 		for(PageElement element:elements) {
 			PageElementHandler handler = handlerMap.get(element.getType());
 			if(handler != null) {
-				Component cmp = handler.getContent(ureq, getWindowControl(), element);
+				PageRunElement runElement = handler.getContent(ureq, getWindowControl(), element);
 				String cmpId = "cpt-" + (++counter);
-				newFragments.add(new PageFragment(cmpId, cmp));
-				mainVC.put(cmpId, cmp);
+				newFragments.add(new PageFragment(cmpId, runElement));
+				mainVC.put(cmpId, runElement.getComponent());
 			}
 		}
 		fragments = newFragments;
@@ -87,11 +97,11 @@ public class PageController extends BasicController {
 	public static final class PageFragment {
 		
 		private final String componentName;
-		private final Component component;
+		private final PageRunElement runElement;
 		
-		public PageFragment(String componentName, Component component) {
+		public PageFragment(String componentName, PageRunElement runElement) {
 			this.componentName = componentName;
-			this.component = component;
+			this.runElement = runElement;
 		}
 		
 		public String getComponentName() {
@@ -99,7 +109,15 @@ public class PageController extends BasicController {
 		}
 		
 		public Component getComponent() {
-			return component;
+			return runElement.getComponent();
+		}
+		
+		public PageRunElement getPageRunElement() {
+			return runElement;
+		}
+		
+		public boolean validate(UserRequest ureq, List<ValidationMessage> messages) {
+			return runElement.validate(ureq, messages);
 		}
 	}
 }
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/PageElementHandler.java b/src/main/java/org/olat/modules/portfolio/ui/editor/PageElementHandler.java
index 28fd4cb4cce..6595b64aa2e 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/editor/PageElementHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/PageElementHandler.java
@@ -20,7 +20,6 @@
 package org.olat.modules.portfolio.ui.editor;
 
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 
@@ -36,7 +35,7 @@ public interface PageElementHandler {
 	
 	public String getIconCssClass();
 	
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element);
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element);
 	
 	public Controller getEditor(UserRequest ureq, WindowControl wControl, PageElement element);
 	
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunComponent.java b/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunComponent.java
new file mode 100644
index 00000000000..ec4f9134393
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunComponent.java
@@ -0,0 +1,50 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <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 the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <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>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.portfolio.ui.editor;
+
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+
+/**
+ * 
+ * Initial date: 23 déc. 2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class PageRunComponent implements PageRunElement {
+	
+	private final Component component;
+	
+	public PageRunComponent(Component component) {
+		this.component = component;
+	}
+
+	@Override
+	public Component getComponent() {
+		return component;
+	}
+
+	@Override
+	public boolean validate(UserRequest ureq, List<ValidationMessage> messages) {
+		return true;
+	}
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunControllerElement.java b/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunControllerElement.java
new file mode 100644
index 00000000000..594bf8d45fd
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunControllerElement.java
@@ -0,0 +1,54 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <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 the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <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>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.portfolio.ui.editor;
+
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.control.Controller;
+
+/**
+ * 
+ * Initial date: 23 déc. 2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class PageRunControllerElement implements PageRunElement {
+	
+	private Controller controller;
+	
+	public PageRunControllerElement(Controller controller) {
+		this.controller = controller;
+	}
+
+	@Override
+	public Component getComponent() {
+		return controller.getInitialComponent();
+	}
+
+	@Override
+	public boolean validate(UserRequest ureq, List<ValidationMessage> messages) {
+		if(controller instanceof ValidatingController) {
+			return ((ValidatingController)controller).validate(ureq, messages);
+		}
+		return true;
+	}
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunElement.java b/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunElement.java
new file mode 100644
index 00000000000..45c71c834c6
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/PageRunElement.java
@@ -0,0 +1,42 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <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 the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <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>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.portfolio.ui.editor;
+
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+
+/**
+ * 
+ * Initial date: 23 déc. 2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface PageRunElement {
+	
+	/**
+	 * @return The component to be rendered on the page.
+	 */
+	public Component getComponent();
+	
+	public boolean validate(UserRequest ureq, List<ValidationMessage> messages);
+
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/ValidatingController.java b/src/main/java/org/olat/modules/portfolio/ui/editor/ValidatingController.java
new file mode 100644
index 00000000000..bd37773b761
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/ValidatingController.java
@@ -0,0 +1,38 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <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 the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <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>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.portfolio.ui.editor;
+
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.control.Controller;
+
+/**
+ * 
+ * Initial date: 23 déc. 2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface ValidatingController extends Controller {
+	
+	
+	public boolean validate(UserRequest ureq, List<ValidationMessage> messages);
+
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/ValidationMessage.java b/src/main/java/org/olat/modules/portfolio/ui/editor/ValidationMessage.java
new file mode 100644
index 00000000000..0c5a17d2407
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/ValidationMessage.java
@@ -0,0 +1,49 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <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 the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <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>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.portfolio.ui.editor;
+
+/**
+ * 
+ * Initial date: 23 déc. 2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ValidationMessage {
+	
+	private final Level level;
+	private final String message;
+	
+	public ValidationMessage(Level level, String message) {
+		this.level = level;
+		this.message = message;
+	}
+	
+	public Level getLevel() {
+		return level;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+	public enum Level {
+		warning
+	}
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/handler/HTMLRawPageElementHandler.java b/src/main/java/org/olat/modules/portfolio/ui/editor/handler/HTMLRawPageElementHandler.java
index 0a87bc0cd9e..8b2e8ef30ea 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/editor/handler/HTMLRawPageElementHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/handler/HTMLRawPageElementHandler.java
@@ -34,6 +34,8 @@ import org.olat.modules.portfolio.ui.editor.HTMLRawEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 
 /**
@@ -57,13 +59,14 @@ public class HTMLRawPageElementHandler implements PageElementHandler, SimpleAddP
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		String content = "";
 		if(element instanceof HTMLPart) {
 			content = ((HTMLPart)element).getContent();
 			content = Formatter.formatLatexFormulas(content);
 		}
-		return TextFactory.createTextComponentFromString("htmlRawCmp" + CodeHelper.getRAMUniqueID(), content, null, false, null);
+		Component cmp = TextFactory.createTextComponentFromString("htmlRawCmp" + CodeHelper.getRAMUniqueID(), content, null, false, null);
+		return new PageRunComponent(cmp);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/handler/SpacerElementHandler.java b/src/main/java/org/olat/modules/portfolio/ui/editor/handler/SpacerElementHandler.java
index 2a10533f008..d3babb0bd9f 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/editor/handler/SpacerElementHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/handler/SpacerElementHandler.java
@@ -30,6 +30,8 @@ import org.olat.modules.portfolio.model.SpacerPart;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 import org.olat.modules.portfolio.ui.editor.SpacerEditorController;
 
@@ -54,9 +56,10 @@ public class SpacerElementHandler implements PageElementHandler, SimpleAddPageEl
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		if(element instanceof SpacerPart) {
-			return new SpacerElementComponent("spacer_" + idGenerator.incrementAndGet());
+			Component cmp = new SpacerElementComponent("spacer_" + idGenerator.incrementAndGet());
+			return new PageRunComponent(cmp);
 		}
 		return null;
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/handler/TitlePageElementHandler.java b/src/main/java/org/olat/modules/portfolio/ui/editor/handler/TitlePageElementHandler.java
index c870d13983c..3dbdbb3d2df 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/editor/handler/TitlePageElementHandler.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/handler/TitlePageElementHandler.java
@@ -32,6 +32,8 @@ import org.olat.modules.portfolio.model.TitlePart;
 import org.olat.modules.portfolio.ui.editor.PageElement;
 import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
 import org.olat.modules.portfolio.ui.editor.PageElementHandler;
+import org.olat.modules.portfolio.ui.editor.PageRunComponent;
+import org.olat.modules.portfolio.ui.editor.PageRunElement;
 import org.olat.modules.portfolio.ui.editor.SimpleAddPageElementHandler;
 import org.olat.modules.portfolio.ui.editor.TitleEditorController;
 
@@ -56,12 +58,13 @@ public class TitlePageElementHandler implements PageElementHandler, SimpleAddPag
 	}
 
 	@Override
-	public Component getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
+	public PageRunElement getContent(UserRequest ureq, WindowControl wControl, PageElement element) {
 		String content = "";
 		if(element instanceof TitlePart) {
 			content = ((TitlePart)element).getContent();
 		}
-		return TextFactory.createTextComponentFromString("title_" + idGenerator.incrementAndGet(), content, null, false, null);
+		Component cmp = TextFactory.createTextComponentFromString("title_" + idGenerator.incrementAndGet(), content, null, false, null);
+		return new PageRunComponent(cmp);
 	}
 
 	@Override
-- 
GitLab