/** * <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.util.mail.ui; import java.io.File; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.UUID; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.events.SingleIdentityChosenEvent; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.modules.bc.FileSelection; import org.olat.core.commons.modules.bc.FolderConfig; import org.olat.core.commons.modules.bc.commands.CmdSendMail; import org.olat.core.commons.modules.bc.commands.FolderCommand; import org.olat.core.commons.modules.bc.commands.FolderCommandHelper; import org.olat.core.commons.modules.bc.commands.FolderCommandStatus; import org.olat.core.commons.modules.bc.components.FolderComponent; import org.olat.core.commons.modules.bc.meta.MetaInfo; import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged; 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.FormLink; 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.control.generic.closablewrapper.CloseableCalloutWindowController; import org.olat.core.gui.control.generic.folder.FolderHelper; import org.olat.core.gui.translator.Translator; import org.olat.core.gui.util.CSSHelper; import org.olat.core.id.Identity; import org.olat.core.id.UserConstants; import org.olat.core.id.context.BusinessControlFactory; import org.olat.core.id.context.ContextEntry; import org.olat.core.util.CodeHelper; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.mail.MailBundle; import org.olat.core.util.mail.MailHelper; import org.olat.core.util.mail.MailManager; import org.olat.core.util.mail.MailModule; import org.olat.core.util.mail.MailerResult; import org.olat.core.util.vfs.LocalFileImpl; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSManager; import org.olat.user.UserManager; /** * * <h3>Description:</h3> * <p> * <p> * Initial Date: 7 feb. 2011 <br> * * @author srosse, stephane.rosse@frentix.com, www.frentix.com */ public class SendDocumentsByEMailController extends FormBasicController implements CmdSendMail { private TextElement bodyElement; private FormLink addEmailLink; private TextElement subjectElement; private FormLayoutContainer userListBox; private FormLayoutContainer attachmentsLayout; private EMailCalloutCtrl emailCalloutCtrl; private CloseableCalloutWindowController calloutCtrl; private final DecimalFormat formatMb = new DecimalFormat("0.00"); private int status = FolderCommandStatus.STATUS_SUCCESS; private List<VFSLeaf> files; private FileSelection selection; private List<File> attachments; private List<IdentityWrapper> toValues = new ArrayList<IdentityWrapper>(); private final MailManager mailManager; private final BaseSecurity securityManager; private final boolean allowAttachments; public SendDocumentsByEMailController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl, null, Util.createPackageTranslator(MetaInfo.class, ureq.getLocale(), Util.createPackageTranslator(MailModule.class, ureq.getLocale()))); setBasePackage(MailModule.class); mailManager = CoreSpringFactory.getImpl(MailManager.class); securityManager = BaseSecurityManager.getInstance(); allowAttachments = !FolderConfig.getSendDocumentLinkOnly(); initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { setFormDescription("send.mail.description"); setFormStyle("o_send_documents"); int emailCols = 25; String toPage = velocity_root + "/tos.html"; userListBox = FormLayoutContainer.createCustomFormLayout("send.mail.to.auto", getTranslator(), toPage); userListBox.setLabel("send.mail.to", null); userListBox.setRootForm(mainForm); userListBox.contextPut("tos", toValues); formLayout.add(userListBox); addEmailLink = uifactory.addFormLink("add.email", userListBox); addEmailLink.setIconLeftCSS("o_icon o_icon_add"); subjectElement = uifactory.addTextElement("tsubject", "send.mail.subject", 255, "", formLayout); bodyElement = uifactory.addTextAreaElement("tbody", "send.mail.body", -1, 20, emailCols, false, "", formLayout); if (allowAttachments) { String page = Util.getPackageVelocityRoot(MailModule.class) + "/sendattachments.html"; attachmentsLayout = FormLayoutContainer.createCustomFormLayout("attachments", getTranslator(), page); attachmentsLayout.setRootForm(mainForm); attachmentsLayout.setLabel("send.mail.attachments", null); formLayout.add(attachmentsLayout); } FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator()); formLayout.add(buttonGroupLayout); uifactory.addFormSubmitButton("ok", buttonGroupLayout); uifactory.addFormCancelButton("cancel", buttonGroupLayout, ureq, getWindowControl()); } @Override protected void doDispose() { // } @Override public int getStatus() { return status; } @Override public boolean runsModal() { return false; } @Override public String getModalTitle() { return translate("send.mail.title"); } public Controller execute(FolderComponent folderComponent, UserRequest ureq, WindowControl wControl, Translator translator) { VFSContainer currentContainer = folderComponent.getCurrentContainer(); VFSContainer rootContainer = folderComponent.getRootContainer(); if (!VFSManager.exists(currentContainer)) { status = FolderCommandStatus.STATUS_FAILED; showError(translator.translate("FileDoesNotExist")); return null; } status = FolderCommandHelper.sanityCheck(wControl, folderComponent); if (status == FolderCommandStatus.STATUS_FAILED) { return null; } selection = new FileSelection(ureq, folderComponent.getCurrentContainerPath()); status = FolderCommandHelper.sanityCheck3(wControl, folderComponent, selection); if (status == FolderCommandStatus.STATUS_FAILED) { return null; } boolean selectionWithContainer = false; List<String> filenames = selection.getFiles(); List<VFSLeaf> leafs = new ArrayList<VFSLeaf>(); for (String file : filenames) { VFSItem item = currentContainer.resolve(file); if (item instanceof VFSContainer) { selectionWithContainer = true; } else if (item instanceof VFSLeaf) { leafs.add((VFSLeaf) item); } } if (selectionWithContainer) { if (leafs.isEmpty()) { wControl.setError(getTranslator().translate("send.mail.noFileSelected")); return null; } else { setFormWarning(getTranslator().translate("send.mail.selectionContainsFolder")); } } setFiles(rootContainer, leafs); return this; } protected void setFiles(VFSContainer rootContainer, List<VFSLeaf> leafs) { this.files = leafs; StringBuilder subjectSb = new StringBuilder(); if (StringHelper.containsNonWhitespace(subjectElement.getValue())) { subjectSb.append(subjectElement.getValue()).append('\n').append('\n'); } StringBuilder bodySb = new StringBuilder(); if (StringHelper.containsNonWhitespace(bodyElement.getValue())) { bodySb.append(bodyElement.getValue()).append('\n').append('\n'); } attachments = new ArrayList<File>(); long fileSize = 0l; for (VFSLeaf file : files) { MetaInfo infos = null; if (file instanceof MetaTagged) { infos = ((MetaTagged) file).getMetaInfo(); } // subject appendToSubject(file, infos, subjectSb); // body appendMetadatas(file, infos, bodySb); appendBusinessPath(rootContainer, file, bodySb); bodySb.append('\n').append('\n'); fileSize += file.getSize(); if (allowAttachments && file instanceof LocalFileImpl) { File f = ((LocalFileImpl) file).getBasefile(); attachments.add(f); } } int mailQuota = CoreSpringFactory.getImpl(MailModule.class).getMaxSizeForAttachement(); long fileSizeInMB = fileSize / (1024l * 1024l); if (allowAttachments) { if (fileSizeInMB > mailQuota) { attachments.clear(); setFormWarning("send.mail.fileToBigForAttachments", new String[] { String.valueOf(mailQuota), String.valueOf(fileSizeInMB) }); } else { List<FileInfo> infos = new ArrayList<FileInfo>(files.size()); for (VFSLeaf file : files) { final String name = file.getName(); final double size = file.getSize() / (1024.0 * 1024.0); final String sizeStr = formatMb.format(size); final String cssClass = CSSHelper.createFiletypeIconCssClassFor(file.getName()); infos.add(new FileInfo(name, sizeStr, cssClass)); } attachmentsLayout.contextPut("attachments", infos); } } subjectElement.setValue(subjectSb.toString()); bodyElement.setValue(bodySb.toString()); } protected void appendToSubject(VFSLeaf file, MetaInfo infos, StringBuilder sb) { if (sb.length() > 0) sb.append(", "); if (infos != null && StringHelper.containsNonWhitespace(infos.getTitle())) { sb.append(infos.getTitle()); } else { sb.append(file.getName()); } } protected void appendMetadatas(VFSLeaf file, MetaInfo infos, StringBuilder sb) { if (infos == null) { appendMetadata("mf.filename", file.getName(), sb); } else { appendMetadata("mf.filename", infos.getName(), sb); String title = infos.getTitle(); if (StringHelper.containsNonWhitespace(title)) { appendMetadata("mf.title", title, sb); } String comment = infos.getComment(); if (StringHelper.containsNonWhitespace(comment)) { appendMetadata("mf.comment", comment, sb); } String creator = infos.getCreator(); if (StringHelper.containsNonWhitespace(creator)) { appendMetadata("mf.creator", creator, sb); } String publisher = infos.getPublisher(); if (StringHelper.containsNonWhitespace(publisher)) { appendMetadata("mf.publisher", publisher, sb); } String source = infos.getSource(); if (StringHelper.containsNonWhitespace(source)) { appendMetadata("mf.source", source, sb); } String city = infos.getCity(); if (StringHelper.containsNonWhitespace(city)) { appendMetadata("mf.city", city, sb); } appendPublicationDate(infos, sb); String pages = infos.getPages(); if (StringHelper.containsNonWhitespace(pages)) { appendMetadata("mf.pages", pages, sb); } String language = infos.getLanguage(); if (StringHelper.containsNonWhitespace(language)) { appendMetadata("mf.language", language, sb); } String url = infos.getUrl(); if (StringHelper.containsNonWhitespace(url)) { appendMetadata("mf.url", url, sb); } String author = infos.getHTMLFormattedAuthor(); if (StringHelper.containsNonWhitespace(author)) { appendMetadata("mf.author", author, sb); } String size = Formatter.formatBytes(file.getSize()); appendMetadata("mf.size", size, sb); long lastModifiedDate = infos.getLastModified(); if (lastModifiedDate > 0) { appendMetadata("mf.lastModified", StringHelper.formatLocaleDate(lastModifiedDate, getLocale()), sb); } String type = FolderHelper.extractFileType(file.getName(), getLocale()); if (StringHelper.containsNonWhitespace(type)) { appendMetadata("mf.type", type, sb); } int downloads = infos.getDownloadCount(); if (infos.getDownloadCount() >= 0) { appendMetadata("mf.downloads", String.valueOf(downloads), sb); } } } protected void appendMetadata(String i18nKey, String value, StringBuilder sb) { sb.append(translate(i18nKey)).append(": ").append(value).append('\n'); } protected void appendPublicationDate(MetaInfo infos, StringBuilder sb) { String[] publicationDate = infos.getPublicationDate(); if (publicationDate == null || publicationDate.length != 2) return; String month = publicationDate[1]; String year = publicationDate[0]; if (StringHelper.containsNonWhitespace(month) || StringHelper.containsNonWhitespace(year)) { sb.append(translate("mf.publishDate")).append(":"); if (StringHelper.containsNonWhitespace(month)) { sb.append(" ").append(translate("mf.month").replaceAll(" ", "")).append(" ").append(month); } if (StringHelper.containsNonWhitespace(year)) { sb.append(" ").append(translate("mf.year").replaceAll(" ", "")).append(" ").append(year); } sb.append('\n'); } } protected void appendBusinessPath(VFSContainer rootContainer, VFSLeaf file, StringBuilder sb) { BusinessControlFactory bCF = BusinessControlFactory.getInstance(); String businnessPath = getWindowControl().getBusinessControl().getAsString(); String relPath = getRelativePath(rootContainer, file); businnessPath += "[path=" + relPath + "]"; List<ContextEntry> ces = bCF.createCEListFromString(businnessPath); String uri = bCF.getAsURIString(ces, true); this.appendMetadata("mf.url", uri, sb); } protected String getRelativePath(VFSContainer rootContainer, VFSLeaf file) { String sb = "/" + file.getName(); VFSContainer parent = file.getParentContainer(); while (parent != null && !rootContainer.isSame(parent)) { sb = "/" + parent.getName() + sb; parent = parent.getParentContainer(); } return sb; } @Override protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; String subject = subjectElement.getValue(); subjectElement.clearError(); if (!StringHelper.containsNonWhitespace(subject)) { subjectElement.setErrorKey("form.legende.mandatory", null); allOk &= false; } else if(subject != null && subject.length() > subjectElement.getMaxLength()) { subjectElement.setErrorKey("text.element.error.notlongerthan", new String[]{ Integer.toString(subjectElement.getMaxLength()) }); allOk &= false; } String body = bodyElement.getValue(); bodyElement.clearError(); if (!StringHelper.containsNonWhitespace(body)) { bodyElement.setErrorKey("form.legende.mandatory", null); allOk &= false; } List<Identity> invalidTos = getInvalidToAddressesFromTextBoxList(); userListBox.clearError(); if (invalidTos.size() > 0) { String[] invalidTosArray = new String[invalidTos.size()]; userListBox.setErrorKey("mailhelper.error.addressinvalid", invalidTos.toArray(invalidTosArray)); allOk &= false; } else if(toValues == null || toValues.isEmpty()) { userListBox.setErrorKey("form.legende.mandatory", null); allOk &= false; } return allOk & super.validateFormLogic(ureq); } /** * returns a list of invalid Values within the textboxlist. * values are either email-addresses (manually added, thus external) or * usernames (from autocompletion, thus olat users) * * @return */ private List<Identity> getInvalidToAddressesFromTextBoxList() { List<Identity> invalidTos = new ArrayList<Identity>(); // the toValues are either usernames (from autocompletion, thus OLAT // users) or email-addresses (external) if (FolderConfig.getSendDocumentToExtern()) { for (IdentityWrapper toValue : toValues) { Identity id = toValue.getIdentity(); if (!MailHelper.isValidEmailAddress(id.getUser().getProperty(UserConstants.EMAIL, null)) && !securityManager.isIdentityVisible(id)) { invalidTos.add(id); } } } else { for (IdentityWrapper toValue : toValues) { Identity id = toValue.getIdentity(); if(!securityManager.isIdentityVisible(id)){ invalidTos.add(id); } } } return invalidTos; } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(source == addEmailLink) { doAddEmail(ureq); } if(source instanceof FormLink && source.getUserObject() instanceof IdentityWrapper) { if(source.getName().startsWith("rm-")) { for(Iterator<IdentityWrapper> wrapperIt=toValues.iterator(); wrapperIt.hasNext(); ) { IdentityWrapper wrapper = wrapperIt.next(); if(source.getUserObject().equals(wrapper)) { wrapperIt.remove(); } } } userListBox.setDirty(true); } super.formInnerEvent(ureq, source, event); } @Override public void event(UserRequest ureq, Controller source, Event event) { if(source == emailCalloutCtrl) { if (event instanceof SingleIdentityChosenEvent) { addIdentity((SingleIdentityChosenEvent)event); } calloutCtrl.deactivate(); } } private void addIdentity(SingleIdentityChosenEvent foundEvent) { Identity chosenIdentity = foundEvent.getChosenIdentity(); if (chosenIdentity != null) { addIdentity(chosenIdentity); } userListBox.setDirty(true); } private void addIdentity(Identity identity) { FormLink rmLink = uifactory.addFormLink("rm-" + CodeHelper.getForeverUniqueID(), " ", null, userListBox, Link.NONTRANSLATED + Link.LINK); IdentityWrapper wrapper = new IdentityWrapper(identity, rmLink); rmLink.setIconLeftCSS("o_icon o_icon_remove"); rmLink.setUserObject(wrapper); toValues.add(wrapper); userListBox.setDirty(true); } @Override protected void formOK(UserRequest ureq) { List<Identity> tos = new ArrayList<Identity>(toValues.size()); for(IdentityWrapper wrapper:toValues) { tos.add(wrapper.getIdentity()); } String subject = subjectElement.getValue(); String body = bodyElement.getValue(); sendEmail(tos, subject, body, ureq); fireEvent(ureq, FolderCommand.FOLDERCOMMAND_FINISHED); } @Override protected void formCancelled(UserRequest ureq) { fireEvent(ureq, FolderCommand.FOLDERCOMMAND_FINISHED); } protected void doAddEmail(UserRequest ureq) { String title = translate("add.email"); removeAsListenerAndDispose(emailCalloutCtrl); boolean allowExtern = FolderConfig.getSendDocumentToExtern(); emailCalloutCtrl = new EMailCalloutCtrl(ureq, getWindowControl(), allowExtern); listenTo(emailCalloutCtrl); removeAsListenerAndDispose(calloutCtrl); calloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), emailCalloutCtrl.getInitialComponent(), addEmailLink, title, true, null); listenTo(calloutCtrl); calloutCtrl.activate(); } protected void sendEmail(List<Identity> tos, String subject, String body, UserRequest ureq) { File[] attachmentArray = null; if (attachments != null && !attachments.isEmpty() && allowAttachments) { attachmentArray = attachments.toArray(new File[attachments.size()]); } MailerResult result = new MailerResult(); String metaId = UUID.randomUUID().toString().replace("-", ""); for(Identity to:tos) { MailBundle bundle = new MailBundle(); bundle.setToId(to); bundle.setMetaId(metaId); bundle.setFromId(ureq.getIdentity()); bundle.setContent(subject, body, attachmentArray); result.append(mailManager.sendMessage(bundle)); } MailHelper.printErrorsAndWarnings(result, getWindowControl(), ureq.getLocale()); } public class FileInfo { private final String filename; private final String sizeInMB; private final String cssClass; public FileInfo(String filename, String sizeInMB, String cssClass) { this.filename = filename; this.sizeInMB = sizeInMB; this.cssClass = cssClass; } public String getFilename() { return filename; } public String getSizeInMB() { return sizeInMB; } public String getCssClass() { return cssClass; } } public final class IdentityWrapper { private Identity identity; private FormLink removeLink; public IdentityWrapper(Identity identity, FormLink removeLink) { this.identity = identity; this.removeLink = removeLink; } public String getName() { if(identity instanceof EMailIdentity) { return identity.getUser().getProperty(UserConstants.EMAIL, null); } return UserManager.getInstance().getUserDisplayName(identity); } public Identity getIdentity() { return identity; } public String getRemoveLinkName() { return removeLink.getComponent().getComponentName(); } } }