diff --git a/src/main/java/org/olat/core/util/mail/ContactList.java b/src/main/java/org/olat/core/util/mail/ContactList.java
index 0ed63088c43be55125d9a6da5b32a1e7843c8f7a..30e5d09f78dcdf4fff3d58cf8882e561b7111661 100644
--- a/src/main/java/org/olat/core/util/mail/ContactList.java
+++ b/src/main/java/org/olat/core/util/mail/ContactList.java
@@ -27,6 +27,7 @@
 package org.olat.core.util.mail;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -272,7 +273,7 @@ public class ContactList extends LogDelegator {
 	 * 
 	 * @param listOfIdentity List containing Identites
 	 */
-	public void addAllIdentites(List<Identity> listOfIdentity) {
+	public void addAllIdentites(Collection<Identity> listOfIdentity) {
 		for (Identity identity:listOfIdentity) {
 			add(identity);
 		}
diff --git a/src/main/java/org/olat/repository/RepositoryService.java b/src/main/java/org/olat/repository/RepositoryService.java
index e5355cfeaa3ead99b941eb2c8689792d246f7691..13aa47c236a6f3d1087874a5091edb4a27da0aa5 100644
--- a/src/main/java/org/olat/repository/RepositoryService.java
+++ b/src/main/java/org/olat/repository/RepositoryService.java
@@ -225,6 +225,15 @@ public interface RepositoryService {
 	 */
 	public List<Identity> getMembers(RepositoryEntryRef re, String... roles);
 	
+	/**
+	 * Get the 
+	 * @param re
+	 * @param followBusinessGroups
+	 * @param roles
+	 * @return
+	 */
+	public List<Identity> getMembers(List<? extends RepositoryEntryRef> re, RepositoryEntryRelationType relationType, String... roles);
+	
 	/**
 	 * Return all the identities the specified role linked to a repository
 	 * entry.
diff --git a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties
index 8d7f87d6003c9462a8716d2db0b4dbd60d4fff29..d7f78427fcac826bd032e78dc412fdb0fb153af4 100644
--- a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties
@@ -163,6 +163,16 @@ cmd.import.ressource.desc=W\u00E4hlen Sie eine Lernressource f\u00FCr den Import
 comments=Kommentar
 confirmation.no.toolHelp=Aktion abbrechen.
 confirmation.yes.toolHelp=Lernressource l\u00F6schen.
+contact.attachment=$org.olat.modules.co\:contact.attachment
+contact.attachment.maxsize=$org.olat.modules.co\:contact.attachment.maxsize
+contact.body=$org.olat.modules.co\:contact.body
+contact.cp.from=$org.olat.modules.co\:contact.cp.from
+contact.from=$org.olat.modules.co\:contact.from
+contact.to=$org.olat.modules.co\:contact.to
+contact.to.owner=Alle Kurzbesitzer
+contact.to.coach=Alle Betreuer
+contact.to.participant=Alle Teilnehmer
+contact.subject=$org.olat.modules.co\:contact.subject
 copy.suffix=(Kopie)
 course.config.changed.text=Sie m\u00F6chten die Kurseinstellungen \u00E4ndern. Wenn Sie 'Ja' klicken, m\u00FCssen {0} Personen im Kurs den Kurs neu starten. Bei 'Nein' werden die \u00C4nderungen verworfen. Wollen Sie fortfahren? 
 course.config.changed.title=\u00C4nderung der Einstellungen
@@ -273,6 +283,7 @@ edit.lifecycle=Semester editieren
 edit.member=$org.olat.group.ui.main\:edit.member
 edit.member.groups=$org.olat.group.ui.main\:edit.member.groups
 error.atleastone=$org.olat.course.member\:error.atleastone
+error.contact.to.empty=Es wurde kein Empf\u00E4nger gefunden.
 error.course.alreadylocked=Dieser Kurs wird im Moment von {0} editiert und ist daher gesperrt.
 error.createcopy=Beim Kopieren des Objektes ist ein Fehler aufgetreten. Die Aktion wurde abgebrochen.
 error.download=Beim Download des Objektes ist ein Fehler aufgetreten. Die Aktion wurde abgebrochen.
@@ -598,6 +609,7 @@ tools.new.header=Herstellen
 tools.new.podcast=Podcast
 tools.new.portfolio=Portfoliovorlage
 tools.new.wiki=Wiki
+tools.send.mail=E-Mail versenden
 tools.restore=Wiederherstellen
 user.notfound=Folgende Benutzer wurden nicht gefunden\: {0}
 warn.config.reference.no.access=Beachten Sie bitte, dass die Konfiguration "Referenzierung m\u00F6glich" erst aktiv wird wenn der Zugriff auch f\u00FCr Autoren freigegeben wurde.
diff --git a/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties
index 2d52836b4d9e1056955f04358d2e1c0dcac78575..ab778c9684cd0ff8bc97518ebe5294e001abdc92 100644
--- a/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties
@@ -159,6 +159,16 @@ cmd.import.ressource.desc=Choose a learning resource to import\:<ul><li>OpenOLAT
 comments=Comments
 confirmation.no.toolHelp=Cancel action.
 confirmation.yes.toolHelp=Delete learning resource.
+contact.attachment=$org.olat.modules.co\:contact.attachment
+contact.attachment.maxsize=$org.olat.modules.co\:contact.attachment.maxsize
+contact.body=$org.olat.modules.co\:contact.body
+contact.cp.from=$org.olat.modules.co\:contact.cp.from
+contact.from=$org.olat.modules.co\:contact.from
+contact.to=$org.olat.modules.co\:contact.to
+contact.to.owner=Alle Kurzbesitzer
+contact.to.coach=Alle Betreuer
+contact.to.participant=Alle Teilnehmer
+contact.subject=$org.olat.modules.co\:contact.subject
 copy.suffix=(copy)
 course.config.changed.text=You want to modify your course settings. When clicking on 'Yes' {0} participants will have to restart this course. When clicking on 'No' all modifications will be discarded. Do you wish to proceed?
 course.config.changed.title=Modification of settings
@@ -269,6 +279,7 @@ edit.lifecycle=Edit semester
 edit.member=$org.olat.group.ui.main\:edit.member
 edit.member.groups=$org.olat.group.ui.main\:edit.member.groups
 error.atleastone=$org.olat.course.member\:error.atleastone
+error.contact.to.empty=No recipient can be found.
 error.course.alreadylocked=This course is currently edited by {0} and therefore locked.
 error.createcopy=An error occurred while trying to copy the object. Action canceled.
 error.download=An error occurred while trying to download the object. Action canceled.
@@ -595,6 +606,7 @@ tools.new.header=Create
 tools.new.podcast=Podcast
 tools.new.portfolio=Portfolio template
 tools.new.wiki=Wiki
+tools.send.mail=Send E-mail
 tools.restore=Restore
 user.notfound=The following users have not been found\: {0}
 warn.config.reference.no.access=Please note that the configuration "Can be referenced" is activated only after access to this resource is granted to other authors as well.
diff --git a/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java b/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java
index 81def30930e5286b2c0273cc5277744f9e42ca03..bf4d203dda9ad1ef4dcbd1bec981a93e7ef9d294 100644
--- a/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java
+++ b/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java
@@ -28,6 +28,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -420,6 +421,12 @@ public class RepositoryEntryRelationDAO {
 	}
 	
 	public List<Identity> getMembers(RepositoryEntryRef re, RepositoryEntryRelationType type, String... roles) {
+		return getMembers(Collections.singletonList(re), type, roles);
+	}
+	
+	public List<Identity> getMembers(List<? extends RepositoryEntryRef> res, RepositoryEntryRelationType type, String... roles) {
+		if(res == null || res.isEmpty()) return Collections.emptyList();
+		
 		List<String> roleList = GroupRoles.toList(roles);
 		
 		String def;
@@ -436,14 +443,15 @@ public class RepositoryEntryRelationDAO {
 		  .append(" inner join baseGroup.members as memberships")
 		  .append(" inner join memberships.identity as ident")
 		  .append(" inner join fetch ident.user as identUser")
-		  .append(" where v.key=:repoKey");
+		  .append(" where v.key in (:repoKeys)");
 		if(roleList.size() > 0) {
 				sb.append(" and memberships.role in (:roles)");
 		}
-			
+		
+		List<Long> repoKeys = res.stream().map(re -> re.getKey()).collect(Collectors.toList());	
 		TypedQuery<Identity> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Identity.class)
-				.setParameter("repoKey", re.getKey());
+				.setParameter("repoKeys", repoKeys);
 		if(roleList.size() > 0) {
 				query.setParameter("roles", roleList);
 		}
diff --git a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
index bfc6933b253efa78f889a1ea850f05fcb8c6276f..d84ac4f53899fd50f8412d0a5ce826b5c2f44744 100644
--- a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
+++ b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
@@ -609,6 +609,11 @@ public class RepositoryServiceImpl implements RepositoryService {
 		return reToGroupDao.getMembers(re, RepositoryEntryRelationType.defaultGroup, roles);
 	}
 
+	@Override
+	public List<Identity> getMembers(List<? extends RepositoryEntryRef> res, RepositoryEntryRelationType relationType, String... roles) {
+		return reToGroupDao.getMembers(res, relationType, roles);
+	}
+
 	@Override
 	public List<Identity> getIdentitiesWithRole(String role) {
 		return reToGroupDao.getIdentitiesWithRole(role);
diff --git a/src/main/java/org/olat/repository/ui/author/AuthorListController.java b/src/main/java/org/olat/repository/ui/author/AuthorListController.java
index 6130c5c4d16a3ec82097aec1307be360e844846a..dc1e936ba4eed01322cd25ee4c0f0254d52f97e7 100644
--- a/src/main/java/org/olat/repository/ui/author/AuthorListController.java
+++ b/src/main/java/org/olat/repository/ui/author/AuthorListController.java
@@ -127,6 +127,7 @@ public class AuthorListController extends FormBasicController implements Activat
 
 	private ToolsController toolsCtrl;
 	protected CloseableModalController cmc;
+	private SendMailController sendMailCtrl;
 	private StepsMainRunController wizardCtrl;
 	private AuthorSearchController searchCtrl;
 	private UserSearchController userSearchCtr;
@@ -143,7 +144,7 @@ public class AuthorListController extends FormBasicController implements Activat
 	
 	private Link importLink;
 	private Dropdown createDropdown;
-	private FormLink addOwnersButton, deleteButton, copyButton;
+	private FormLink sendMailButton, addOwnersButton, deleteButton, copyButton;
 
 	private LockResult lockResult;
 	private final AtomicInteger counter = new AtomicInteger();
@@ -331,6 +332,7 @@ public class AuthorListController extends FormBasicController implements Activat
 	
 	protected void initBatchButtons(FormItemContainer formLayout) {
 		if(hasAuthorRight) {
+			sendMailButton = uifactory.addFormLink("tools.send.mail", formLayout, Link.BUTTON);
 			addOwnersButton = uifactory.addFormLink("tools.add.owners", formLayout, Link.BUTTON);
 			copyButton = uifactory.addFormLink("details.copy", formLayout, Link.BUTTON);
 			deleteButton = uifactory.addFormLink("details.delete", formLayout, Link.BUTTON);
@@ -478,6 +480,9 @@ public class AuthorListController extends FormBasicController implements Activat
 			}
 			cmc.deactivate();
 			cleanUp();
+		} else if(sendMailCtrl == source) {
+			cmc.deactivate();
+			cleanUp();
 		} else if(toolsCtrl == source) {
 			if(event == Event.DONE_EVENT) {
 				toolsCalloutCtrl.deactivate();
@@ -516,6 +521,7 @@ public class AuthorListController extends FormBasicController implements Activat
 		removeAsListenerAndDispose(confirmDeleteCtrl);
 		removeAsListenerAndDispose(toolsCalloutCtrl);
 		removeAsListenerAndDispose(userSearchCtr);
+		removeAsListenerAndDispose(sendMailCtrl);
 		removeAsListenerAndDispose(createCtrl);
 		removeAsListenerAndDispose(importCtrl);
 		removeAsListenerAndDispose(wizardCtrl);
@@ -525,6 +531,7 @@ public class AuthorListController extends FormBasicController implements Activat
 		confirmDeleteCtrl = null;
 		toolsCalloutCtrl = null;
 		userSearchCtr = null;
+		sendMailCtrl = null;
 		createCtrl = null;
 		importCtrl = null;
 		wizardCtrl = null;
@@ -547,6 +554,13 @@ public class AuthorListController extends FormBasicController implements Activat
 			} else {
 				showWarning("bulk.update.nothing.selected");
 			}
+		} else if(sendMailButton == source) {
+			List<AuthoringEntryRow> rows = getMultiSelectedRows();
+			if(!rows.isEmpty()) {
+				doSendMail(ureq, rows);
+			} else {
+				showWarning("bulk.update.nothing.selected");
+			}
 		} else if(copyButton == source) {
 			List<AuthoringEntryRow> rows = getMultiSelectedRows();
 			if(!rows.isEmpty()) {
@@ -732,6 +746,20 @@ public class AuthorListController extends FormBasicController implements Activat
 		return rows;
 	}
 	
+	private void doSendMail(UserRequest ureq, List<AuthoringEntryRow> rows) {
+		if(sendMailCtrl != null) return;
+
+		removeAsListenerAndDispose(userSearchCtr);
+		sendMailCtrl = new SendMailController(ureq, getWindowControl(), rows);
+		listenTo(sendMailCtrl);
+		
+		String title = translate("tools.send.mail");
+		cmc = new CloseableModalController(getWindowControl(), translate("close"), sendMailCtrl.getInitialComponent(),
+				true, title);
+		listenTo(cmc);
+		cmc.activate();
+	}
+	
 	private void doAddOwners(UserRequest ureq, List<AuthoringEntryRow> rows) {
 		if(userSearchCtr != null) return;
 		
diff --git a/src/main/java/org/olat/repository/ui/author/SendMailController.java b/src/main/java/org/olat/repository/ui/author/SendMailController.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6875fe5c5d6f069c07e0ca783d9aa1d15f00627
--- /dev/null
+++ b/src/main/java/org/olat/repository/ui/author/SendMailController.java
@@ -0,0 +1,344 @@
+/**
+ * <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.repository.ui.author;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.olat.basesecurity.GroupRoles;
+import org.olat.core.commons.modules.bc.FileUploadController;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.FileElement;
+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.RichTextElement;
+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.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.util.CSSHelper;
+import org.olat.core.id.Identity;
+import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
+import org.olat.core.util.FileUtils;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.Util;
+import org.olat.core.util.mail.ContactList;
+import org.olat.core.util.mail.MailBundle;
+import org.olat.core.util.mail.MailContext;
+import org.olat.core.util.mail.MailContextImpl;
+import org.olat.core.util.mail.MailLoggingAction;
+import org.olat.core.util.mail.MailManager;
+import org.olat.core.util.mail.MailModule;
+import org.olat.core.util.mail.MailerResult;
+import org.olat.repository.RepositoryEntryRef;
+import org.olat.repository.RepositoryEntryRelationType;
+import org.olat.repository.RepositoryService;
+import org.olat.user.UserManager;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 27 avr. 2017<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class SendMailController extends FormBasicController {
+	
+	private static final String[] onKeys = new String[]{ "on" };
+	private static final String[] contactKeys = new String[]{ GroupRoles.owner.name(), GroupRoles.coach.name(), GroupRoles.participant.name() };
+	
+	private RichTextElement bodyEl;
+	private FileElement attachmentEl;
+	private TextElement subjectEl;
+	private MultipleSelectionElement contactEl, copyFromEl;
+	private FormLayoutContainer uploadCont;
+	
+	private int counter = 0;
+	private long attachmentSize = 0l;
+	private File attachementTempDir;
+	private final int contactAttachmentMaxSizeInMb;
+	private List<Attachment> attachments = new ArrayList<>();
+	private final List<? extends RepositoryEntryRef> repoEntries;
+	
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private MailManager mailService;
+	@Autowired
+	private MailModule mailModule;
+	@Autowired
+	private RepositoryService repositoryService;
+	
+	public SendMailController(UserRequest ureq, WindowControl wControl, List<? extends RepositoryEntryRef> repoEntries) {
+		super(ureq, wControl);
+		setTranslator(Util.createPackageTranslator(RepositoryService.class, getLocale(), getTranslator()));
+		this.repoEntries = repoEntries;
+		this.contactAttachmentMaxSizeInMb = mailModule.getMaxSizeForAttachement();
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		String fullName = userManager.getUserDisplayName(getIdentity());
+		if(StringHelper.containsNonWhitespace(fullName)) {
+			fullName = "[" + fullName + "]";
+		}
+		TextElement fromEl = uifactory.addTextElement("from", "contact.from", 255, fullName, formLayout);
+		fromEl.setEnabled(false);
+		
+		String[] contactValues = new String[] {
+				translate("contact.to.owner"),
+				translate("contact.to.coach"),
+				translate("contact.to.participant"),
+		};
+		contactEl = uifactory.addCheckboxesVertical("to", "contact.to", formLayout, contactKeys, contactValues, 1);
+		
+		subjectEl = uifactory.addTextElement("subject", "contact.subject", 255, "", formLayout);
+		subjectEl.setDisplaySize(255);
+		subjectEl.setMandatory(true);
+		bodyEl = uifactory.addRichTextElementForStringDataMinimalistic("body", "contact.body", "", 15, 8, formLayout, getWindowControl());
+		bodyEl.setMandatory(true);
+		
+		attachmentEl = uifactory.addFileElement(getWindowControl(), "file_upload_1", "contact.attachment", formLayout);
+		attachmentEl.addActionListener(FormEvent.ONCHANGE);
+		attachmentEl.setExampleKey("contact.attachment.maxsize", new String[]{ Integer.toString(contactAttachmentMaxSizeInMb) });
+		
+		String attachmentPage = velocity_root + "/attachments.html";
+		uploadCont = FormLayoutContainer.createCustomFormLayout("file_upload_inner", getTranslator(), attachmentPage);
+		uploadCont.setRootForm(mainForm);
+		uploadCont.setVisible(false);
+		uploadCont.contextPut("attachments", attachments);
+		formLayout.add(uploadCont);
+		
+		copyFromEl = uifactory.addCheckboxesHorizontal("copy.from", "contact.cp.from", formLayout, onKeys, new String[] { "" });
+		
+		FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator());
+		formLayout.add(buttonGroupLayout);
+		uifactory.addFormCancelButton("cancel", buttonGroupLayout, ureq, getWindowControl());
+		uifactory.addFormSubmitButton("tools.send.mail", buttonGroupLayout);
+	}
+	
+	@Override
+	protected void doDispose() {
+		if(attachementTempDir != null && attachementTempDir.exists()) {
+			FileUtils.deleteDirsAndFiles(attachementTempDir, true, true);
+		}
+	}
+	
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = true;
+		
+		subjectEl.clearError();
+		if(!StringHelper.containsNonWhitespace(subjectEl.getValue())) {
+			subjectEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		}
+		
+		bodyEl.clearError();
+		if(!StringHelper.containsNonWhitespace(bodyEl.getValue())) {
+			bodyEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		}
+		
+		contactEl.clearError();
+		if(contactEl.getSelectedKeys().isEmpty()) {
+			contactEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		}
+		
+		return allOk & super.validateFormLogic(ureq);
+	}
+	
+	private File[] getAttachments() {
+		File[] atttachmentArr = new File[attachments.size()];
+		for(int i=attachments.size(); i-->0; ) {
+			atttachmentArr[i] = attachments.get(i).getFile();
+		}
+		return atttachmentArr;
+	}
+
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(source == attachmentEl) {
+			doUploadAttachement();
+		} if(source instanceof FormLink) {
+			FormLink link = (FormLink)source;
+			String cmd = link.getCmd();
+			if("delete".equals(cmd)) {
+				Attachment attachment = (Attachment)link.getUserObject();
+				doDeleteAttachment(attachment);
+			}
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+	
+	private void doDeleteAttachment(Attachment attachment) {
+		attachmentSize -= attachment.getFile().length();
+		attachment.getFile().delete();
+		attachments.remove(attachment);
+		uploadCont.setVisible(attachments.size() > 0);
+		uploadCont.setDirty(true);
+	}
+	
+	private void doUploadAttachement() {
+		if(attachementTempDir == null) {
+			attachementTempDir = FileUtils.createTempDir("attachements", null, null);
+		}
+		
+		long size = attachmentEl.getUploadSize();
+		String filename = attachmentEl.getUploadFileName();
+		if(size + attachmentSize > (contactAttachmentMaxSizeInMb  * 1024 * 1024)) {
+			showWarning("contact.attachment,maxsize", Integer.toString(contactAttachmentMaxSizeInMb));
+			attachmentEl.reset();
+		} else {
+			File attachment = attachmentEl.moveUploadFileTo(attachementTempDir);
+			attachmentEl.reset();
+			if(attachment == null) {
+				logError("Could not move contact-form attachment to " + attachementTempDir.getAbsolutePath(), null);
+				setTranslator(Util.createPackageTranslator(FileUploadController.class, getLocale(), getTranslator()));
+				showError("FileMoveCopyFailed","");
+			} else {
+				attachmentSize += size;
+				FormLink removeFile = uifactory.addFormLink("delete_" + (++counter), "delete", "delete", null, uploadCont, Link.LINK);
+				removeFile.setIconLeftCSS("o_icon o_icon-fw o_icon_delete");
+				String css = CSSHelper.createFiletypeIconCssClassFor(filename);
+				Attachment wrapper = new Attachment(attachment, attachment.getName(), css, removeFile);
+				removeFile.setUserObject(wrapper);
+				attachments.add(wrapper);
+				uploadCont.setVisible(true);
+			}
+		}
+	}
+
+	private void handleAddressException(boolean success) {
+		StringBuilder error = new StringBuilder();
+		if (success) {
+			error.append(translate("error.msg.send.partially.nok"))
+			     .append("<br />")
+			     .append(translate("error.msg.send.invalid.rcps"));
+		} else {
+			error.append(translate("error.msg.send.nok"))
+			     .append("<br />")
+			     .append(translate("error.msg.send.553"));
+		}
+		getWindowControl().setError(error.toString());
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		ContactList contactList = new ContactList("");
+		Collection<String> roleList = contactEl.getSelectedKeys();
+		String[] roles = roleList.toArray(new String[roleList.size()]);
+		List<Identity> identities = repositoryService.getMembers(repoEntries, RepositoryEntryRelationType.both, roles);
+		if(identities.isEmpty()) {
+			showWarning("error.contact.to.empty");
+		} else {
+			Set<Identity> deduplicates = new HashSet<>(identities);
+			contactList.addAllIdentites(deduplicates);
+	
+			boolean success = false;
+			try {
+				File[] attachmentArr = getAttachments();
+				MailContext context = new MailContextImpl(getWindowControl().getBusinessControl().getAsString());
+				MailBundle bundle = new MailBundle();
+				bundle.setContext(context);
+				bundle.setFromId(getIdentity());						
+				bundle.setContactLists(Collections.singletonList(contactList));
+				bundle.setContent(subjectEl.getValue(), bodyEl.getValue(), attachmentArr);
+				MailerResult result = mailService.sendMessage(bundle);
+				if(copyFromEl.isAtLeastSelected(1)) {
+					MailBundle ccBundle = new MailBundle();
+					ccBundle.setContext(context);
+					ccBundle.setFromId(getIdentity()); 
+					ccBundle.setCc(getIdentity());							
+					ccBundle.setContent(subjectEl.getValue(), bodyEl.getValue(), attachmentArr);
+					MailerResult ccResult = mailService.sendMessage(ccBundle);
+					result.append(ccResult);
+				}
+				success = result.isSuccessful();
+			} catch (Exception e) {
+				//error in recipient email address(es)
+				handleAddressException(success);
+			}
+			
+			if (success) {
+				showInfo("msg.send.ok");
+				// do logging
+				ThreadLocalUserActivityLogger.log(MailLoggingAction.MAIL_SENT, getClass());
+				fireEvent(ureq, Event.DONE_EVENT);
+			} else {
+				showInfo("error.msg.send.nok");
+				fireEvent(ureq, Event.FAILED_EVENT);
+			}
+		}
+	}
+
+	@Override
+	protected void formCancelled(UserRequest ureq) {
+		fireEvent(ureq, Event.CANCELLED_EVENT);
+	}
+	
+	public static class Attachment {
+		
+		private final File file;
+		private final String filename;
+		private final String cssClass;
+		private final FormLink deleteLink;
+		
+		public Attachment(File file, String filename, String cssClass, FormLink deleteLink) {
+			this.file = file;
+			this.filename = filename;
+			this.cssClass = cssClass;
+			this.deleteLink = deleteLink;
+		}
+		
+		public File getFile() {
+			return file;
+		}
+
+		public String getCssClass() {
+			return cssClass;
+		}
+
+		public String getFilename() {
+			return filename;
+		}
+
+		public FormLink getDeleteLink() {
+			return deleteLink;
+		}
+		
+		public String getDeleteComponentName() {
+			return deleteLink.getComponent().getComponentName();
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/repository/ui/author/_content/attachments.html b/src/main/java/org/olat/repository/ui/author/_content/attachments.html
new file mode 100644
index 0000000000000000000000000000000000000000..3ae6db278f2b54d25c792a37a62fdba5ed7ba7ec
--- /dev/null
+++ b/src/main/java/org/olat/repository/ui/author/_content/attachments.html
@@ -0,0 +1,6 @@
+<ul class="list-unstyled">
+	#foreach ($attachment in $attachments)
+	<li><i class="o_icon o_icon-fw $attachment.cssClass"> </i> $attachment.filename $r.render(${attachment.getDeleteComponentName()})</li>
+	#end
+</ul>
+
diff --git a/src/main/java/org/olat/repository/ui/author/_content/entries.html b/src/main/java/org/olat/repository/ui/author/_content/entries.html
index 06b129dcb081910c85b9b1910ce185605eb53733..9bd02f0ce2a8e565dd5d9fb1da8e5db4951f2619 100644
--- a/src/main/java/org/olat/repository/ui/author/_content/entries.html
+++ b/src/main/java/org/olat/repository/ui/author/_content/entries.html
@@ -3,6 +3,9 @@
 #end
 $r.render("table")
 <div class="o_button_group clearfix">
+	#if($r.available("tools.send.mail"))
+		$r.render("tools.send.mail")
+	#end
 	#if($r.available("tools.add.owners"))
 		$r.render("tools.add.owners")
 	#end