/** * <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.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.olat.core.commons.services.mark.Mark; import org.olat.core.gui.ShortName; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.table.ColumnDescriptor; import org.olat.core.gui.components.table.CustomCellRenderer; import org.olat.core.gui.components.table.CustomRenderColumnDescriptor; import org.olat.core.gui.components.table.DefaultColumnDescriptor; import org.olat.core.gui.components.table.StaticColumnDescriptor; import org.olat.core.gui.components.table.Table; import org.olat.core.gui.components.table.TableController; import org.olat.core.gui.components.table.TableEvent; import org.olat.core.gui.components.table.TableGuiConfiguration; import org.olat.core.gui.components.table.TableMultiSelectEvent; import org.olat.core.gui.components.velocity.VelocityContainer; 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.controller.BasicController; import org.olat.core.gui.control.generic.dtabs.Activateable2; import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.OLATResourceable; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; 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.mail.model.DBMail; import org.olat.core.util.mail.model.DBMailLight; import org.olat.core.util.mail.model.DBMailRecipient; import org.olat.core.util.mail.ui.MailDataModel.Columns; import org.olat.core.util.mail.ui.MailDataModel.ContextPair; import org.olat.core.util.resource.OresHelper; import org.springframework.beans.factory.annotation.Autowired; /** * * Description:<br> * Represent a list of mails. * * <P> * Initial Date: 24 mars 2011 <br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ public class MailListController extends BasicController implements Activateable2 { private static final String CMD_READ_TOGGLE = "creadt"; private static final String CMD_READ = "cread"; private static final String CMD_DELETE = "cdelselected"; private static final String CMD_MARK_TOGGLE = "cmark"; private static final String CMD_PROFILE = "cprofile"; private static final String CMD_SEND_REAL_MAIL = "cfwd"; private static final String CMD_MARK_READ = "creadselected"; private static final String CMD_MARK_UNREAD = "cunreadselected"; private static final String CMD_MARK_MARKED = "cmarkselected"; private static final String CMD_MARK_UNMARKED = "cunmarkselected"; private static final String MAIN_CMP = "mainCmp"; private Link backLink; private final VelocityContainer mainVC; private MailController mailCtr; private MailListController metaMailCtr; private final TableController tableCtr; private final VelocityContainer tableVC; private DialogBoxController deleteConfirmationBox; private final boolean outbox; private final String metaId; @Autowired private MailManager mailManager; private final MailContextResolver contextResolver; public MailListController(UserRequest ureq, WindowControl wControl, boolean outbox, MailContextResolver resolver) { this(ureq, wControl, null, outbox, resolver); } private MailListController(UserRequest ureq, WindowControl wControl, String metaId, boolean outbox, MailContextResolver resolver) { super(ureq, wControl); setBasePackage(MailModule.class); this.outbox = outbox; this.metaId = metaId; this.contextResolver = resolver; TableGuiConfiguration tableConfig = new TableGuiConfiguration(); tableConfig.setDownloadOffered(true); tableConfig.setPreferencesOffered(true, "MailBox"); tableConfig.setTableEmptyMessage(translate("mail.empty.box")); tableConfig.setMultiSelect(true); mainVC = createVelocityContainer("mails"); tableVC = createVelocityContainer("mailsTable"); String context = translate("mail.context"); tableCtr = new TableController(tableConfig, ureq, wControl, Collections.<ShortName>emptyList(), null, context , null, false, getTranslator()); //only for outbox if(outbox) { //context / recipients / subject / sendDate tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Columns.context.i18nKey(), Columns.context.ordinal(), null, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT, new MailContextCellRenderer(this, tableVC, getTranslator())) { @Override public int compareTo(int rowa, int rowb) { Object a = table.getTableDataModel().getValueAt(rowa,dataColumn); Object b = table.getTableDataModel().getValueAt(rowb,dataColumn); if(a instanceof ContextPair && b instanceof ContextPair) { ContextPair p1 = (ContextPair)a; ContextPair p2 = (ContextPair)b; return super.compareString(p1.getName(), p2.getName()); } return super.compareTo(rowa, rowb); } }); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Columns.recipients.i18nKey(), Columns.recipients.ordinal(), null, getLocale())); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Columns.subject.i18nKey(), Columns.subject.ordinal(), CMD_READ, getLocale())); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Columns.sendDate.i18nKey(), Columns.sendDate.ordinal(), null, getLocale())); } else { //read / marked / context / from / subject / receivedDate CustomCellRenderer readRenderer = new BooleanCSSCellRenderer(getTranslator(), "o_icon o_icon-lg o_icon_read", "o_icon o_icon-lg o_icon_to_read", "mail.read", "mail.unread"); tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Columns.read.i18nKey(), Columns.read.ordinal(), CMD_READ_TOGGLE, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT, readRenderer)); CustomCellRenderer markRenderer = new BooleanCSSCellRenderer(getTranslator(), Mark.MARK_CSS_LARGE, Mark.MARK_ADD_CSS_LARGE, "mail.marked", "mail.unmarked"); tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Columns.marked.i18nKey(), Columns.marked.ordinal(), CMD_MARK_TOGGLE, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT, markRenderer)); tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Columns.context.i18nKey(), Columns.context.ordinal(), null, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT, new MailContextCellRenderer(this, tableVC, getTranslator())){ @Override public int compareTo(int rowa, int rowb) { Object a = table.getTableDataModel().getValueAt(rowa,dataColumn); Object b = table.getTableDataModel().getValueAt(rowb,dataColumn); if(a instanceof ContextPair && b instanceof ContextPair) { ContextPair p1 = (ContextPair)a; ContextPair p2 = (ContextPair)b; return super.compareString(p1.getName(), p2.getName()); } return super.compareTo(rowa, rowb); } }); tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Columns.from.i18nKey(), Columns.from.ordinal(), null, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT, new MailFromCellRenderer(this, tableVC, getTranslator()))); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Columns.subject.i18nKey(), Columns.subject.ordinal(), CMD_READ, getLocale())); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Columns.receivedDate.i18nKey(), Columns.receivedDate.ordinal(), null, getLocale())); } tableCtr.addColumnDescriptor(new StaticColumnDescriptor(CMD_READ, "mail.action.open", translate("mail.action.open"))); // only for inbox if (!outbox) { tableCtr.addMultiSelectAction("mail.action.read", CMD_MARK_READ); tableCtr.addMultiSelectAction("mail.action.unread", CMD_MARK_UNREAD); tableCtr.addMultiSelectAction("mail.action.mark", CMD_MARK_MARKED); tableCtr.addMultiSelectAction("mail.action.unmark", CMD_MARK_UNMARKED); } tableCtr.addMultiSelectAction("mail.action.send.real", CMD_SEND_REAL_MAIL); tableCtr.addMultiSelectAction("delete", CMD_DELETE); reloadModel(); int dateSort = outbox ? 4 : 6; tableCtr.setSortColumn(dateSort, false); listenTo(tableCtr); tableVC.put("tableCmp", tableCtr.getInitialComponent()); if(outbox) { if(StringHelper.containsNonWhitespace(metaId)) { tableVC.contextPut("title", translate("mail.outbox.title")); tableVC.contextPut("description", translate("mail.outbox.meta")); } else { tableVC.contextPut("title", translate("mail.outbox.title")); } } else { tableVC.contextPut("title", translate("mail.inbox.title")); } mainVC.put(MAIN_CMP, tableVC); if(StringHelper.containsNonWhitespace(metaId)) { backLink = LinkFactory.createLinkBack(mainVC, this); mainVC.put("back", backLink); } putInitialPanel(mainVC); } private void replaceInModel(DBMailLight mail) { MailDataModel dataModel = (MailDataModel)tableCtr.getTableDataModel(); dataModel.replace(mail); tableCtr.modelChanged(); } private void reloadModel() { List<DBMailLight> mails; if(outbox) { if(StringHelper.containsNonWhitespace(metaId)) { mails = mailManager.getEmailsByMetaId(metaId); } else { mails = mailManager.getOutbox(getIdentity(), 0, -1, true); } //strip meta emails Set<String> metaIds = new HashSet<String>(); for(Iterator<DBMailLight> it=mails.iterator(); it.hasNext(); ) { DBMailLight mail = it.next(); if(StringHelper.containsNonWhitespace(mail.getMetaId())) { if(metaIds.contains(mail.getMetaId())) { it.remove(); } else { metaIds.add(mail.getMetaId()); } } } } else { mails = mailManager.getInbox(getIdentity(), null, Boolean.TRUE, null, 0, -1); } //extract contexts Map<String, String> bpToContexts = new HashMap<String, String>(); for(DBMailLight mail:mails) { String businessPath = mail.getContext().getBusinessPath(); if(StringHelper.containsNonWhitespace(businessPath) && !bpToContexts.containsKey(businessPath)) { String contextName = contextResolver.getName(businessPath, getLocale()); if(StringHelper.containsNonWhitespace(contextName)) { bpToContexts.put(businessPath, contextName); } } } if(!bpToContexts.isEmpty()) { List<ShortName> filters = new ArrayList<ShortName>(); Map<String, MailContextShortName> uniqueNames = new HashMap<String,MailContextShortName>(); ShortName allContextFilter = new MailContextShortName("-"); filters.add(allContextFilter); for(Map.Entry<String, String> entry:bpToContexts.entrySet()) { String businessPath = entry.getKey(); String contextName = entry.getValue(); if(!uniqueNames.containsKey(contextName)) { MailContextShortName cxt = new MailContextShortName(contextName, new HashSet<String>()); filters.add(cxt); uniqueNames.put(contextName, cxt); } uniqueNames.get(contextName).getBusinessPaths().add(businessPath); } tableCtr.setFilters(filters, allContextFilter); } Formatter formatter = Formatter.getInstance(getLocale()); MailDataModel dataModel = new MailDataModel(mails, bpToContexts, getIdentity(), getTranslator(), formatter, outbox); tableCtr.setTableDataModel(dataModel); } @Override protected void event(UserRequest ureq, Component source, Event event) { if(source == backLink) { if(mailCtr != null) { backFromMail(); } else { fireEvent(ureq, event); } } else if (source instanceof Link && source.getComponentName().startsWith("bp_")) { String businessPath = (String)((Link)source).getUserObject(); if(StringHelper.containsNonWhitespace(businessPath)) { contextResolver.open(ureq, getWindowControl(), businessPath); } } } @Override protected void event(UserRequest ureq, Controller source, Event event) { if(source == tableCtr) { if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) { TableEvent te = (TableEvent) event; String actionid = te.getActionId(); int rowid = te.getRowId(); DBMailLight mail = (DBMailLight)tableCtr.getTableDataModel().getObject(rowid); if(CMD_READ.equals(actionid)) { if(outbox && StringHelper.containsNonWhitespace(mail.getMetaId()) && !mail.getMetaId().equals(metaId)) { selectMetaMail(ureq, mail.getMetaId()); } else { selectMail(ureq, mail.getKey()); } } else if (CMD_PROFILE.equals(actionid)) { DBMailRecipient from = mail.getFrom(); if(from != null&& from.getRecipient() != null) { contextResolver.open(ureq, getWindowControl(), "[Identity:" + from.getRecipient().getKey() + "]"); } } else if (CMD_MARK_TOGGLE.equals(actionid)) { mail = mailManager.toggleMarked(mail, getIdentity()); replaceInModel(mail); } else if (CMD_READ_TOGGLE.equals(actionid)) { mail = mailManager.toggleRead(mail, getIdentity()); replaceInModel(mail); } } else if (event.getCommand().equals(Table.COMMAND_MULTISELECT)) { // Multiselect events TableMultiSelectEvent tmse = (TableMultiSelectEvent) event; BitSet selectedMails = tmse.getSelection(); if(selectedMails.isEmpty()){ tableVC.setDirty(true); showWarning("mail.action.emtpy"); return; } String actionid = tmse.getAction(); if (CMD_DELETE.equals(actionid)) { String title = translate("mail.confirm.delete.title"); int selected = selectedMails.cardinality(); String text; if (selected == 1) { text = translate("mail.confirm.delete.single.text"); } else { text = translate("mail.confirm.delete.multi.text", selected + ""); } deleteConfirmationBox = activateYesNoDialog(ureq, title, text, deleteConfirmationBox); deleteConfirmationBox.setUserObject(selectedMails); } else if (CMD_SEND_REAL_MAIL.equals(actionid)) { for (int i=selectedMails.nextSetBit(0); i >= 0; i=selectedMails.nextSetBit(i+1)) { DBMailLight mail = (DBMailLight) tableCtr.getTableDataModel().getObject(i); MailerResult result = forwardToMyRealMail(mail); if(result.getReturnCode() != MailerResult.OK) { MailHelper.printErrorsAndWarnings(result, getWindowControl(), getLocale()); } else { showInfo("mail.action.send.real.success", mail.getSubject()); } } reloadModel(); } else if (CMD_MARK_MARKED.equals(actionid) || CMD_MARK_UNMARKED.equals(actionid)) { for (int i=selectedMails.nextSetBit(0); i >= 0; i=selectedMails.nextSetBit(i+1)) { DBMailLight mail = (DBMailLight)tableCtr.getTableDataModel().getObject(i); mailManager.setMarked(mail, CMD_MARK_MARKED.equals(actionid), getIdentity()); } reloadModel(); } else if (CMD_MARK_READ.equals(actionid) || CMD_MARK_UNREAD.equals(actionid)) { for (int i=selectedMails.nextSetBit(0); i >= 0; i=selectedMails.nextSetBit(i+1)) { DBMailLight mail = (DBMailLight) tableCtr.getTableDataModel().getObject(i); mailManager.setRead(mail, CMD_MARK_READ.equals(actionid), getIdentity()); } reloadModel(); } } else if (TableController.EVENT_FILTER_SELECTED == event) { MailDataModel dataModel = (MailDataModel)tableCtr.getTableDataModel(); MailContextShortName filter = (MailContextShortName)tableCtr.getActiveFilter(); dataModel.filter(filter); tableCtr.setTableDataModel(dataModel); } else if (TableController.EVENT_NOFILTER_SELECTED == event) { MailDataModel dataModel = (MailDataModel)tableCtr.getTableDataModel(); dataModel.filter(null); tableCtr.setTableDataModel(dataModel); } } else if (source == mailCtr) { backFromMail(); } else if (source == metaMailCtr) { removeAsListenerAndDispose(metaMailCtr); metaMailCtr = null; mainVC.put(MAIN_CMP, tableVC); } else if (source == deleteConfirmationBox) { if(DialogBoxUIFactory.isYesEvent(event)) { BitSet deleteMails = (BitSet)deleteConfirmationBox.getUserObject(); for (int i=deleteMails.nextSetBit(0); i >= 0; i=deleteMails.nextSetBit(i+1)) { DBMailLight mail = (DBMailLight)tableCtr.getTableDataModel().getObject(i); //reload the message mail = mailManager.getMessageByKey(mail.getKey()); boolean deleteMetaMail = outbox && !StringHelper.containsNonWhitespace(metaId); mailManager.delete(mail, getIdentity(), deleteMetaMail); // Do not remove from model to prevent concurrent modification // exception, instead just reload model afterwards } reloadModel(); } } else { super.event(ureq, source, event); } } @Override protected void doDispose() { // } @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { if(entries == null || entries.isEmpty()) { backFromMail(); return; } ContextEntry entry = entries.get(0); String type = entry.getOLATResourceable().getResourceableTypeName(); if(DBMail.class.getSimpleName().equals(type)) { selectMail(ureq, entry.getOLATResourceable().getResourceableId()); } } private void backFromMail() { removeAsListenerAndDispose(mailCtr); mailCtr = null; mainVC.put(MAIN_CMP, tableVC); } private MailerResult forwardToMyRealMail(DBMailLight mail) { DBMail fullMail = mailManager.getMessageByKey(mail.getKey()); return mailManager.forwardToRealInbox(getIdentity(), fullMail, null); } private void selectMetaMail(UserRequest ureq, String metaID) { metaMailCtr = new MailListController(ureq, getWindowControl(), metaID, outbox, contextResolver); listenTo(metaMailCtr); mainVC.put(MAIN_CMP, metaMailCtr.getInitialComponent()); } private void selectMail(UserRequest ureq, Long mailKey) { DBMail mail = mailManager.getMessageByKey(mailKey); if(mail != null) { selectMail(ureq, mail); } } private void selectMail(UserRequest ureq, DBMail mail) { removeAsListenerAndDispose(mailCtr); boolean back = !StringHelper.containsNonWhitespace(mail.getMetaId()) || !outbox; OLATResourceable ores = OresHelper.createOLATResourceableInstance(DBMail.class, mail.getKey()); WindowControl bwControl = addToHistory(ureq, ores, null); mailCtr = new MailController(ureq, bwControl, mail, back); listenTo(mailCtr); mainVC.put(MAIN_CMP, mailCtr.getInitialComponent()); if(mailManager.setRead(mail, Boolean.TRUE, getIdentity())) { reloadModel(); } } }