From ecd439a44d9febaa9555b7f212ab1f52f4a82776 Mon Sep 17 00:00:00 2001
From: aboeckle <alexander.boeckle@frentix.com>
Date: Tue, 14 Apr 2020 22:56:53 +0200
Subject: [PATCH] OO-4603 Add data privacy policy to imprint

---
 .../impressum/ContactController.java          |  30 ++---
 .../impressum/ContactExtension.java           |  50 ++++++++
 .../impressum/ImpressumAdminController.java   | 116 +++++++++++++++++-
 .../impressum/ImpressumExtension.java         |  69 +++++++++++
 .../impressum/ImpressumModule.java            |  44 ++++++-
 .../impressum/PrivacyPolicyController.java    |  69 +++++++++++
 .../impressum/PrivacyPolicyExtension.java     |  70 +++++++++++
 .../impressum/TermsOfUseExtension.java        |  70 +++++++++++
 .../_i18n/LocalStrings_de.properties          |  40 +++---
 .../_i18n/LocalStrings_en.properties          |  27 ++--
 .../impressum/_spring/impressumContext.xml    |  28 ++++-
 .../java/org/olat/core/util/StringHelper.java |  12 +-
 .../resources/serviceconfig/olat.properties   |   8 ++
 13 files changed, 571 insertions(+), 62 deletions(-)
 create mode 100644 src/main/java/org/olat/core/commons/controllers/impressum/ContactExtension.java
 create mode 100644 src/main/java/org/olat/core/commons/controllers/impressum/ImpressumExtension.java
 create mode 100644 src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyController.java
 create mode 100644 src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyExtension.java
 create mode 100644 src/main/java/org/olat/core/commons/controllers/impressum/TermsOfUseExtension.java

diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/ContactController.java b/src/main/java/org/olat/core/commons/controllers/impressum/ContactController.java
index d5641f9bf1d..37d9933b581 100644
--- a/src/main/java/org/olat/core/commons/controllers/impressum/ContactController.java
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/ContactController.java
@@ -19,20 +19,17 @@
  */
 package org.olat.core.commons.controllers.impressum;
 
-import org.olat.core.configuration.PersistedProperties;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
-import org.olat.core.logging.OLATRuntimeException;
-import org.olat.core.util.StringHelper;
-import org.olat.core.util.WebappHelper;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.mail.ContactList;
 import org.olat.core.util.mail.ContactMessage;
 import org.olat.modules.co.ContactFormController;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * <h3>Description:</h3> This controller shows a contact form and has a
@@ -48,6 +45,9 @@ public class ContactController extends BasicController implements GenericEventLi
 	private final VelocityContainer content;
 	private ContactFormController contactForm;
 	private static String contactEmail = null;
+	
+	@Autowired
+	private ImpressumModule impressumModule;
 
 	/**
 	 * Creates this controller.
@@ -61,25 +61,8 @@ public class ContactController extends BasicController implements GenericEventLi
 
 		// load configuration only once
 		if (contactEmail == null) {
-			// Read the destination e-mail address from the configuration file.
-			PersistedProperties contactConfiguration = new PersistedProperties(this);
-			contactConfiguration.init();
-			contactEmail = contactConfiguration.getStringPropertyValue("contact.to.address", true);
-			if (!StringHelper.containsNonWhitespace(contactEmail)) {
-				// fallback to standard email
-				contactEmail = WebappHelper.getMailConfig("mailSupport");
-				if (!StringHelper.containsNonWhitespace(contactEmail)) {
-					throw new OLATRuntimeException(
-							"could not find valid contact email address, configure property 'contact.to.address' in olatdata/system/configuration/"
-									+ this.getClass().getName() + ".properties", null);
-				} else {
-					logInfo("Initialize impressum email with standard support address::" + contactEmail
-							+ " You can configure a specific impressum email in the property 'contact.to.address' in olatdata/system/configuration/"
-							+ this.getClass().getName() + ".properties");
-				}
-			} else {
-				logInfo("Initialize impressum email with address::" + contactEmail);
-			}
+			// Read the destination email from the impressModule
+			contactEmail = impressumModule.getContactMail();
 		}
 
 		// Initialize a few contact list management objects.
@@ -117,6 +100,7 @@ public class ContactController extends BasicController implements GenericEventLi
 	/**
 	 * @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event)
 	 */
+	@Override
 	public void event(Event event) {
 	// nothing to do, the persisted properties used in this controller are
 	// read-only, no GUI to modify the properties yet
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/ContactExtension.java b/src/main/java/org/olat/core/commons/controllers/impressum/ContactExtension.java
new file mode 100644
index 00000000000..a16e15032d5
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/ContactExtension.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.core.commons.controllers.impressum;
+
+import org.olat.core.extensions.action.GenericActionExtension;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+
+/* 
+ * Initial date: 12 Apr 2020<br>
+ * @author aboeckle, alexander.boeckle@frentix.com
+ */
+public class ContactExtension extends GenericActionExtension {
+
+	
+	private final ImpressumModule impressumModule;
+	
+	public ContactExtension(ImpressumModule impressumModule) {
+		this.impressumModule = impressumModule;
+	}
+	
+
+	@Override
+	public Controller createController(UserRequest ureq, WindowControl wControl, Object arg) {
+		return new ContactController(ureq, wControl);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return impressumModule.isContactEnabled();
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumAdminController.java b/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumAdminController.java
index 08c97ecef81..7479391e6b4 100644
--- a/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumAdminController.java
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumAdminController.java
@@ -35,14 +35,17 @@ import org.olat.core.gui.components.form.flexible.FormItemContainer;
 import org.olat.core.gui.components.form.flexible.elements.FormLink;
 import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
 import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
 import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
 import org.olat.core.gui.components.form.flexible.impl.FormEvent;
 import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit;
 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.closablewrapper.CloseableModalController;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.filter.FilterFactory;
 import org.olat.core.util.i18n.I18nManager;
 import org.olat.core.util.i18n.I18nModule;
@@ -65,13 +68,19 @@ public class ImpressumAdminController extends FormBasicController {
 	
 	private SingleSelection positionEl;
 	private MultipleSelectionElement enableEl;
-	private FormLayoutContainer termsCont, impressumCont;
+	private MultipleSelectionElement contactEnableEl;
+	private TextElement contactMailEl;
+	private FormLayoutContainer termsCont;
+	private FormLayoutContainer impressumCont;
+	private FormLayoutContainer dataPrivacyPolicyCont;
+	private FormSubmit formSubmit;
 
 	private CloseableModalController cmc;
 	private HTMLEditorController editorCtrl;
 	
 	private final VFSContainer impressumDir;
 	private final VFSContainer termsOfUseDir;
+	private final VFSContainer dataPrivacyPolicyDir;
 	
 	@Autowired
 	private I18nModule i18nModule;
@@ -84,6 +93,7 @@ public class ImpressumAdminController extends FormBasicController {
 		super(ureq, wControl);
 		impressumDir = new LocalFolderImpl(impressumModule.getImpressumDirectory());
 		termsOfUseDir = new LocalFolderImpl(impressumModule.getTermsOfUseDirectory());
+		dataPrivacyPolicyDir = new LocalFolderImpl(impressumModule.getPrivacyPolicyDirectory());
 		initForm(ureq);
 	}
 
@@ -92,6 +102,7 @@ public class ImpressumAdminController extends FormBasicController {
 		setFormTitle("menu.impressum");
 		setFormDescription("config.hint");
 		boolean enabled = impressumModule.isEnabled();
+		boolean contactEnabled = impressumModule.isContactEnabled();
 		
 		String[] enableKeys = new String[]{ "on" };
 		enableEl = uifactory.addCheckboxesHorizontal("enable", "enable.impressum", formLayout,
@@ -102,6 +113,7 @@ public class ImpressumAdminController extends FormBasicController {
 		String[] positionValues = new String[]{ translate("position.top"), translate("position.footer") };
 		positionEl = uifactory.addDropdownSingleselect("position", "position", formLayout, positionKeys, positionValues, null);
 		positionEl.addActionListener(FormEvent.ONCHANGE);
+		positionEl.setVisible(enabled);
 		if(impressumModule.getPosition() != null) {
 			switch(impressumModule.getPosition()) {
 				case top: positionEl.select(positionKeys[0], true); break;
@@ -168,6 +180,47 @@ public class ImpressumAdminController extends FormBasicController {
 			deleteLink.setUserObject(group);
 			termsOfUseButtons.add(group);
 		}
+		
+		dataPrivacyPolicyCont = FormLayoutContainer.createCustomFormLayout("dataprivacies", getTranslator(), velocity_root + "/buttongroups.html");
+		dataPrivacyPolicyCont.setLabel("dataprivacy.file", null);
+		dataPrivacyPolicyCont.setVisible(enabled);
+		formLayout.add(dataPrivacyPolicyCont);
+		
+		List<ButtonGroup> dataPrivacyPolicyButtons = new ArrayList<>();
+		dataPrivacyPolicyCont.contextPut("buttons", dataPrivacyPolicyButtons);
+		
+		for(String lang:i18nModule.getEnabledLanguageKeys()) {
+			FormLink editLink = uifactory.addFormLink("dataprivacy." + lang, "dataprivacy", getTranslated(lang), "dataprivacy.file", dataPrivacyPolicyCont, Link.BUTTON | Link.NONTRANSLATED);
+			editLink.setLabel(null, null);
+			String filePath = "index_" + lang + ".html";
+			boolean hasDataPrivacyPolicy = checkContent(dataPrivacyPolicyDir.resolve(filePath));
+			if(hasDataPrivacyPolicy) {
+				editLink.setIconLeftCSS("o_icon o_icon_check");
+			}
+			
+			FormLink deleteLink = uifactory
+					.addFormLink("impressum.del." + lang, "delete-dataprivacy", "", "dataprivacy.file", dataPrivacyPolicyCont, Link.BUTTON | Link.NONTRANSLATED);
+			deleteLink.setLabel(null, null);
+			deleteLink.setIconLeftCSS("o_icon o_icon_delete_item");
+			deleteLink.setVisible(hasDataPrivacyPolicy);
+
+			ButtonGroup group = new ButtonGroup(lang, editLink, deleteLink);
+			editLink.setUserObject(group);
+			deleteLink.setUserObject(group);
+			dataPrivacyPolicyButtons.add(group);
+		}
+		
+		contactEnableEl = uifactory.addCheckboxesHorizontal("contactenable", "enable.contact", formLayout,
+				enableKeys, new String[]{ translate("enable") });
+		contactEnableEl.addActionListener(FormEvent.ONCHANGE);
+		contactEnableEl.select(enableKeys[0], contactEnabled);
+		contactEnableEl.setVisible(enabled);
+		
+		contactMailEl = uifactory.addTextElement("contact.mail", 255, impressumModule.getContactMail(), formLayout);
+		contactMailEl.setVisible(contactEnabled && enabled);
+		contactMailEl.setMandatory(contactEnabled && enabled);
+		
+		formSubmit = uifactory.addFormSubmitButton("submit", formLayout);
 	}
 	
 	private String getTranslated(String lang) {
@@ -188,18 +241,59 @@ public class ImpressumAdminController extends FormBasicController {
 
 	@Override
 	protected void formOK(UserRequest ureq) {
-		//
+		if (contactEnableEl.getSelectedKeys().contains("on")) {
+			impressumModule.setContactEnabled(true);
+			impressumModule.setContactMail(contactMailEl.getValue());
+		} else {
+			impressumModule.setContactEnabled(false);
+		}		
+	}
+	
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = super.validateFormLogic(ureq);
+		
+		allOk = validateTextInput(contactMailEl, contactMailEl.getMaxLength(), true);
+		
+		return allOk;
+	}
+	
+	private boolean validateTextInput(TextElement textElement, int lenght, boolean isMail) {		
+		textElement.clearError();
+		if(StringHelper.containsNonWhitespace(textElement.getValue())) {
+			if (isMail) {
+				if (!StringHelper.checkMailFormat(textElement.getValue())) {
+					textElement.setErrorKey("input.wrong.mail", null);
+					return false;
+				}
+			} if(lenght != -1 && textElement.getValue().length() > lenght) {
+				textElement.setErrorKey("input.toolong", new String[]{ String.valueOf(lenght) });
+				return false;
+			}
+		} else if (textElement.isMandatory()) {
+			textElement.setErrorKey("form.legende.mandatory", null);
+			return false;
+		}
+
+		return true;
 	}
 
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
 		if(enableEl == source) {
 			boolean enabled = enableEl.isAtLeastSelected(1);
+			boolean contactEnabled = impressumModule.isContactEnabled();
 			impressumModule.setEnabled(enabled);
 			
-			positionEl.setEnabled(enabled);
+			positionEl.setVisible(enabled);
 			termsCont.setVisible(enabled);
 			impressumCont.setVisible(enabled);
+			dataPrivacyPolicyCont.setVisible(enabled);
+			contactEnableEl.setVisible(enabled);
+			contactEnableEl.select("on", contactEnabled);
+			contactMailEl.setVisible(contactEnabled && enabled);
+			contactMailEl.setMandatory(contactEnabled && enabled);
+			formSubmit.setVisible(enabled && contactEnabled);
 			
 			getWindowControl().getWindowBackOffice().getWindow().setDirty(true);
 			getWindowControl().getWindowBackOffice().getChiefController().wishReload(ureq, true);
@@ -210,6 +304,16 @@ public class ImpressumAdminController extends FormBasicController {
 				getWindowControl().getWindowBackOffice().getWindow().setDirty(true);
 				getWindowControl().getWindowBackOffice().getChiefController().wishReload(ureq, true);
 			}
+		} else if (contactEnableEl == source) {
+			boolean contactEnabled = contactEnableEl.isAtLeastSelected(1);
+			impressumModule.setContactEnabled(contactEnabled);
+			
+			contactMailEl.setVisible(contactEnabled);
+			contactMailEl.setMandatory(contactEnabled);
+			formSubmit.setVisible(contactEnabled);
+
+			getWindowControl().getWindowBackOffice().getWindow().setDirty(true);
+			getWindowControl().getWindowBackOffice().getChiefController().wishReload(ureq, true);
 		} else if(source instanceof FormLink) {
 			FormLink link = (FormLink)source;
 			String cmd = link.getCmd();
@@ -227,6 +331,12 @@ public class ImpressumAdminController extends FormBasicController {
 				doDelete(termsOfUseDir, lang);
 				group.getEditButton().setIconLeftCSS(null);
 				group.getDeleteButton().setVisible(false);
+			} else if ("dataprivacy".equals(cmd)) {
+				doEdit(ureq, link, dataPrivacyPolicyDir, lang);
+			} else if ("delete-dataprivacy".equals(cmd)) {
+				doDelete(dataPrivacyPolicyDir, lang);
+				group.getEditButton().setIconLeftCSS(null);
+				group.getDeleteButton().setVisible(false);
 			}
 		}
 		super.formInnerEvent(ureq, source, event);
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumExtension.java b/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumExtension.java
new file mode 100644
index 00000000000..d5d51ba3970
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumExtension.java
@@ -0,0 +1,69 @@
+/**
+ * <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.core.commons.controllers.impressum;
+
+import java.io.File;
+
+import org.olat.core.extensions.ExtensionElement;
+import org.olat.core.extensions.action.GenericActionExtension;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+
+/* 
+ * Initial date: 12 Apr 2020<br>
+ * @author aboeckle, alexander.boeckle@frentix.com
+ */
+public class ImpressumExtension extends GenericActionExtension {
+	
+	private final ImpressumModule impressumModule;
+	
+	public ImpressumExtension(ImpressumModule impressumModule) {
+		this.impressumModule = impressumModule;
+	}
+	
+
+	@Override
+	public Controller createController(UserRequest ureq, WindowControl wControl, Object arg) {
+		return new ImpressumController(ureq, wControl);
+	}
+	
+	@Override
+	public ExtensionElement getExtensionFor(String extensionPoint, UserRequest ureq) {
+		boolean enabled = false;
+		
+		if (impressumModule.isEnabled()) {
+			enabled = true;
+			
+			File baseFolder = impressumModule.getImpressumDirectory();
+			if (new File(baseFolder, "index_" + ureq.getLocale().getLanguage() + ".html").exists()) {
+				enabled &= true;
+			} else if(new File (baseFolder, "index_de.html").exists()) {
+					enabled &= true;
+			} else if(new File (baseFolder, "index_en.html").exists()) {
+					enabled &= true;
+			} else {
+				enabled &= false;
+			}
+		}
+		
+		return enabled ? super.getExtensionFor(extensionPoint, ureq) : null;
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumModule.java b/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumModule.java
index 946c6cf6228..f673b1e287f 100644
--- a/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumModule.java
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/ImpressumModule.java
@@ -42,11 +42,17 @@ public class ImpressumModule extends AbstractSpringModule implements ConfigOnOff
 
 	private static final String ENABLED = "impressum.enabled";
 	private static final String POSITION = "impressum.position";
+	private static final String CONTACT_ENABLED = "impressum.contact.enabled";
+	private static final String CONTACT_MAIL = "impressum.contact.mail";
 	
-	@Value("${topnav.impressum:true}")
+	@Value("${impressum.enabled:true}")
 	private boolean enabled;
 	@Value("${impressum.position:footer}")
 	private String position;
+	@Value("${impressum.contact.enabled:true}")
+	private boolean contactEnabled;
+	@Value("${impressum.contact.mail:webmaster@your.domain}")
+	private String contactMail;
 	
 	@Autowired
 	public ImpressumModule(CoordinatorManager coordinatorManager) {
@@ -64,6 +70,16 @@ public class ImpressumModule extends AbstractSpringModule implements ConfigOnOff
 		if(StringHelper.containsNonWhitespace(positionObj)) {
 			position = positionObj;
 		}
+		
+		String contactEnabledObj = getStringPropertyValue(CONTACT_ENABLED, true);
+		if (StringHelper.containsNonWhitespace(contactEnabledObj)) {
+			contactEnabled = "true".equals(contactEnabledObj);
+		}
+		
+		String contactMailObj = getStringPropertyValue(CONTACT_MAIL, true);
+		if (StringHelper.containsNonWhitespace(contactMailObj)) {
+			contactMail = contactMailObj;
+		} 
 	}
 
 	@Override
@@ -87,6 +103,14 @@ public class ImpressumModule extends AbstractSpringModule implements ConfigOnOff
 		return dir;
 	}
 	
+	public File getPrivacyPolicyDirectory() {
+		File dir = Paths.get(WebappHelper.getUserDataRoot(), "customizing", "data_privacy_policy").toFile();
+		if(!dir.exists()) {
+			dir.mkdirs();
+		}
+		return dir;
+	}
+	
 	@Override
 	public boolean isEnabled() {
 		return enabled;
@@ -97,6 +121,24 @@ public class ImpressumModule extends AbstractSpringModule implements ConfigOnOff
 		setStringProperty(ENABLED, Boolean.toString(enabled), true);
 	}
 	
+	public boolean isContactEnabled() {
+		return contactEnabled;
+	}
+	
+	public void setContactEnabled(boolean enabled) {
+		this.contactEnabled = enabled;
+		setStringProperty(CONTACT_ENABLED, Boolean.toString(contactEnabled), true);
+	}
+	
+	public String getContactMail() {
+		return contactMail;
+	}
+	
+	public void setContactMail(String contactMail) {
+		this.contactMail = contactMail;
+		setStringProperty(CONTACT_MAIL, contactMail, true);
+	}
+	
 	public Position getPosition() {
 		if("top".equals(position)) {
 			return Position.top;
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyController.java b/src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyController.java
new file mode 100644
index 00000000000..d172a2b3d98
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyController.java
@@ -0,0 +1,69 @@
+/**
+ * <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.core.commons.controllers.impressum;
+
+import java.io.File;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.control.generic.iframe.IFrameDisplayController;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/* 
+ * Initial date: 12 Apr 2020<br>
+ * @author aboeckle, alexander.boeckle@frentix.com
+ */
+public class PrivacyPolicyController extends BasicController {
+	
+	@Autowired
+	private ImpressumModule impressumModule;
+
+	public PrivacyPolicyController(UserRequest ureq, WindowControl control) {
+		super(ureq, control);
+		File baseFolder = impressumModule.getPrivacyPolicyDirectory();
+		IFrameDisplayController iframe = new IFrameDisplayController(ureq, getWindowControl(), baseFolder);
+		listenTo(iframe);
+		
+		String langCode = ureq.getLocale().getLanguage();
+		String fileName = "index_" + langCode + ".html";
+		if (new File (baseFolder, fileName).exists()){
+			iframe.setCurrentURI(fileName);
+		} else if(new File (baseFolder, "index_de.html").exists()) {
+			iframe.setCurrentURI("index_de.html");
+		} else if(new File (baseFolder, "index_en.html").exists()) {
+			iframe.setCurrentURI("index_en.html");
+		}
+		
+		putInitialPanel(iframe.getInitialComponent());
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyExtension.java b/src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyExtension.java
new file mode 100644
index 00000000000..b47021b9b0e
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/PrivacyPolicyExtension.java
@@ -0,0 +1,70 @@
+/**
+ * <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.core.commons.controllers.impressum;
+
+import java.io.File;
+
+import org.olat.core.extensions.ExtensionElement;
+import org.olat.core.extensions.action.GenericActionExtension;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+
+/* 
+ * Initial date: 12 Apr 2020<br>
+ * @author aboeckle, alexander.boeckle@frentix.com
+ */
+public class PrivacyPolicyExtension extends GenericActionExtension {
+
+	
+	private final ImpressumModule impressumModule;
+	
+	public PrivacyPolicyExtension(ImpressumModule impressumModule) {
+		this.impressumModule = impressumModule;
+	}
+	
+
+	@Override
+	public Controller createController(UserRequest ureq, WindowControl wControl, Object arg) {
+		return new PrivacyPolicyController(ureq, wControl);
+	}
+
+	@Override
+	public ExtensionElement getExtensionFor(String extensionPoint, UserRequest ureq) {		
+		boolean enabled = false;
+		
+		if (impressumModule.isEnabled()) {
+			enabled = true;
+			
+			File baseFolder = impressumModule.getPrivacyPolicyDirectory();
+			if (new File(baseFolder, "index_" + ureq.getLocale().getLanguage() + ".html").exists()) {
+				enabled &= true;
+			} else if(new File (baseFolder, "index_de.html").exists()) {
+					enabled &= true;
+			} else if(new File (baseFolder, "index_en.html").exists()) {
+					enabled &= true;
+			} else {
+				enabled &= false;
+			}
+		}
+		
+		return enabled ? super.getExtensionFor(extensionPoint, ureq) : null;
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/TermsOfUseExtension.java b/src/main/java/org/olat/core/commons/controllers/impressum/TermsOfUseExtension.java
new file mode 100644
index 00000000000..563f4a1e38d
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/TermsOfUseExtension.java
@@ -0,0 +1,70 @@
+/**
+ * <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.core.commons.controllers.impressum;
+
+import java.io.File;
+
+import org.olat.core.extensions.ExtensionElement;
+import org.olat.core.extensions.action.GenericActionExtension;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+
+/* 
+ * Initial date: 12 Apr 2020<br>
+ * @author aboeckle, alexander.boeckle@frentix.com
+ */
+public class TermsOfUseExtension extends GenericActionExtension {
+
+	
+	private final ImpressumModule impressumModule;
+	
+	public TermsOfUseExtension(ImpressumModule impressumModule) {
+		this.impressumModule = impressumModule;
+	}
+	
+
+	@Override
+	public Controller createController(UserRequest ureq, WindowControl wControl, Object arg) {
+		return new TermsOfUseController(ureq, wControl);
+	}
+
+	@Override
+	public ExtensionElement getExtensionFor(String extensionPoint, UserRequest ureq) {		
+		boolean enabled = false;
+		
+		if (impressumModule.isEnabled()) {
+			enabled = true;
+			
+			File baseFolder = impressumModule.getImpressumDirectory();
+			if (new File(baseFolder, "index_" + ureq.getLocale().getLanguage() + ".html").exists()) {
+				enabled &= true;
+			} else if(new File (baseFolder, "index_de.html").exists()) {
+					enabled &= true;
+			} else if(new File (baseFolder, "index_en.html").exists()) {
+					enabled &= true;
+			} else {
+				enabled &= false;
+			}
+		}
+		
+		return enabled ? super.getExtensionFor(extensionPoint, ureq) : null;
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_de.properties
index 59ece622b8e..18f2d07aed1 100644
--- a/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_de.properties
@@ -1,21 +1,27 @@
-contactform.title=Kontakt
-contactform.intro=Kontaktformular f\u00fcr allgemeine Anfragen.
-menu.impressum=Impressum
-menu.impressum.alt=$\:menu.impressum
-main.menu.title=$\:menu.impressum
-main.menu.title.alt=$\:menu.impressum
-ImpressumController.menu.title=$\:menu.impressum
-ImpressumController.menu.title.alt=$\:menu.impressum
+config.hint=Hier k\u00F6nnen Sie das Impressum ein- und ausschalten, positionieren und den Impressumstext \u00E4ndern.
+contact.mail=Kontakt E-Mail
 contact.to=Sekretariat
-ContactController.menu.title=Kontakt
 ContactController.menu.title.alt=Kontakt
-TermsOfUseController.menu.title=Nutzungsbedingungen
-TermsOfUseController.menu.title.alt=Nutzungsbedingungen
-position=Position
-position.top=Top
-position.footer=Footer
+ContactController.menu.title=Kontakt
+contactform.intro=Kontaktformular f\u00fcr allgemeine Anfragen.
+contactform.title=Kontakt
+dataprivacy.file=Datenschutz Erkl\u00E4rung
+enable.contact=Kontaktformular
+enable.impressum=Impressum einschalten
+enable=ein
 impressum.file=Impressum auf
+ImpressumController.menu.title.alt=$\:menu.impressum
+ImpressumController.menu.title=$\:menu.impressum
+input.wrong.mail=Bitte geben Sie eine g\u00fcltige E-Mail-Adresse ein.
+main.menu.title.alt=$\:menu.impressum
+main.menu.title=$\:menu.impressum
+menu.impressum.alt=$\:menu.impressum
+menu.impressum=Impressum
+position.footer=Footer
+position.top=Top
+position=Position
+PrivacyPolicyController.menu.title.alt=Datenschutzerkl\u00E4rung
+PrivacyPolicyController.menu.title=Datenschutzerkl\u00E4rung
 termofuse.file=Term of use
-enable=ein
-enable.impressum=Impressum einschalten
-config.hint=Hier k\u00F6nnen Sie das Impressum ein- und ausschalten, positionieren und den Impressumstext \u00E4ndern.
+TermsOfUseController.menu.title.alt=Nutzungsbedingungen
+TermsOfUseController.menu.title=Nutzungsbedingungen
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_en.properties
index 9c635b37af2..f69cac98b98 100644
--- a/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/_i18n/LocalStrings_en.properties
@@ -1,22 +1,27 @@
-#Tue Mar 17 07:51:49 CET 2020
-ContactController.menu.title=Contact
-ContactController.menu.title.alt=Contact
-ImpressumController.menu.title=$\:menu.impressum
-ImpressumController.menu.title.alt=$\:menu.impressum
-TermsOfUseController.menu.title=Usage terms
-TermsOfUseController.menu.title.alt=Terms of use
 config.hint=Here you can enable or disable the imprint page, choose the position and change the Impress text.
+contact.mail=Contact mail
 contact.to=Secretary
+ContactController.menu.title.alt=Contact
+ContactController.menu.title=Contact
 contactform.intro=Contact form for general requests.
 contactform.title=Contact
-enable=on
+dataprivacy.file=Privacy policy
+enable.contact=Contact form
 enable.impressum=Enable imprint
+enable=on
 impressum.file=Imprint in
-main.menu.title=$\:menu.impressum
+ImpressumController.menu.title.alt=$\:menu.impressum
+ImpressumController.menu.title=$\:menu.impressum
+input.wrong.mail=Please enter a valid mail address.
 main.menu.title.alt=$\:menu.impressum
-menu.impressum=Imprint
+main.menu.title=$\:menu.impressum
 menu.impressum.alt=$\:menu.impressum
-position=Position
+menu.impressum=Imprint
 position.footer=Footer
 position.top=Top
+position=Position
+PrivacyPolicyController.menu.title.alt=Privacy policy
+PrivacyPolicyController.menu.title=Privacy policy
 termofuse.file=Term of use
+TermsOfUseController.menu.title.alt=Terms of use
+TermsOfUseController.menu.title=Usage terms
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/commons/controllers/impressum/_spring/impressumContext.xml b/src/main/java/org/olat/core/commons/controllers/impressum/_spring/impressumContext.xml
index a2aceae71a1..cc8cd7d493e 100644
--- a/src/main/java/org/olat/core/commons/controllers/impressum/_spring/impressumContext.xml
+++ b/src/main/java/org/olat/core/commons/controllers/impressum/_spring/impressumContext.xml
@@ -4,7 +4,8 @@
 	xsi:schemaLocation="http://www.springframework.org/schema/beans 
                         http://www.springframework.org/schema/beans/spring-beans.xsd ">
 
-	<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
+	<bean class="org.olat.core.commons.controllers.impressum.ImpressumExtension" init-method="initExtensionPoints">
+		<constructor-arg index="0" ref="impressumModule" />
 		<property name="actionController">	
 			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
 				<property name="className" value="org.olat.core.commons.controllers.impressum.ImpressumController"/>
@@ -19,7 +20,8 @@
 		<property name="order" value="701"/>
 	</bean>
 	
-	<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
+	<bean class="org.olat.core.commons.controllers.impressum.TermsOfUseExtension" init-method="initExtensionPoints">
+		<constructor-arg index="0" ref="impressumModule" />
 		<property name="actionController">	
 			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
 				<property name="className" value="org.olat.core.commons.controllers.impressum.TermsOfUseController"/>
@@ -34,20 +36,36 @@
 		<property name="order" value="702"/>
 	</bean>
 	
-	<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
+	<bean class="org.olat.core.commons.controllers.impressum.PrivacyPolicyExtension" init-method="initExtensionPoints">
+		<constructor-arg index="0" ref="impressumModule" />
 		<property name="actionController">	
 			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
-				<property name="className" value="org.olat.core.commons.controllers.impressum.ContactController"/>
+				<property name="className" value="org.olat.core.commons.controllers.impressum.PrivacyPolicyController"/>
 			</bean>
 		</property>
 		<property name="extensionPoints">
 			<list>	
-				<value>org.olat.core.commons.controllers.impressum.ImpressumMainController</value>		
+				<value>org.olat.core.commons.controllers.impressum.ImpressumMainController</value>
+				<value>org.olat.core.commons.controllers.impressum.ImpressumDmzMainController</value>
 			</list>
 		</property>
 		<property name="order" value="703"/>
 	</bean>
 	
+	<bean class="org.olat.core.commons.controllers.impressum.ContactExtension" init-method="initExtensionPoints">
+		<constructor-arg index="0" ref="impressumModule" />
+		<property name="actionController">	
+			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
+				<property name="className" value="org.olat.core.commons.controllers.impressum.ContactController"/>
+			</bean>
+		</property>
+		<property name="extensionPoints">
+			<list>	
+				<value>org.olat.core.commons.controllers.impressum.ImpressumMainController</value>		
+			</list>
+		</property>
+		<property name="order" value="704"/>
+	</bean>
 
 	<bean class="org.olat.core.extensions.action.GenericActionExtension"  init-method="initExtensionPoints">
 		<property name="order" value="7208" />
diff --git a/src/main/java/org/olat/core/util/StringHelper.java b/src/main/java/org/olat/core/util/StringHelper.java
index e9ca976bf4e..6ed15fbfb1e 100644
--- a/src/main/java/org/olat/core/util/StringHelper.java
+++ b/src/main/java/org/olat/core/util/StringHelper.java
@@ -46,12 +46,12 @@ import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.logging.log4j.Logger;
 import org.olat.core.id.Identity;
 import org.olat.core.logging.AssertException;
-import org.apache.logging.log4j.Logger;
 import org.olat.core.logging.Tracing;
-import org.olat.core.util.filter.impl.HtmlScanner;
 import org.olat.core.util.filter.FilterFactory;
+import org.olat.core.util.filter.impl.HtmlScanner;
 import org.olat.core.util.filter.impl.OWASPAntiSamyXSSFilter;
 import org.olat.user.UserManager;
 
@@ -70,6 +70,8 @@ public class StringHelper {
 	private static final NumberFormat numFormatter;
 	private static final String WHITESPACE_REGEXP = "^\\s*$";
 	private static final Pattern WHITESPACE_PATTERN = Pattern.compile(WHITESPACE_REGEXP);
+	private static final String EMAIL_REGEXP = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
+	private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEXP);
 	
 	private static final int LONG_MAX_LENGTH = Long.toString(Long.MAX_VALUE).length();
 	
@@ -653,4 +655,10 @@ public class StringHelper {
 		}
 		return shortenedText;
 	}
+	
+	public static boolean checkMailFormat(String mail) {
+		Matcher m = EMAIL_PATTERN.matcher(mail);
+
+        return m.matches();
+	}
 }
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index c44b2e36425..8c37267866e 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -1701,3 +1701,11 @@ live.stream.player.profile.values=both,stream1,stream2
 ###############################################################################
 vfs.largefiles.upperborder=157286400
 vfs.largefiles.lowerborder=26214400
+
+##############################################################################
+# Options for the imprint
+###############################################################################
+impressum.enabled=${topnav.impressum}
+impressum.position=footer
+impressum.contact.enabled=true
+impressum.contact.mail=${supportemail}
\ No newline at end of file
-- 
GitLab