001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.notification; 029 030import org.opencms.db.CmsUserSettings; 031import org.opencms.file.CmsFile; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsResource; 034import org.opencms.file.CmsUser; 035import org.opencms.file.CmsVfsResourceNotFoundException; 036import org.opencms.i18n.CmsMessages; 037import org.opencms.mail.CmsHtmlMail; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.OpenCms; 041import org.opencms.util.CmsHtml2TextConverter; 042import org.opencms.util.CmsMacroResolver; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.xml.content.CmsXmlContent; 045import org.opencms.xml.content.CmsXmlContentFactory; 046 047import java.util.List; 048import java.util.Locale; 049import java.util.regex.Pattern; 050 051import javax.mail.MessagingException; 052 053import org.apache.commons.logging.Log; 054import org.apache.commons.mail.EmailException; 055 056import org.htmlparser.util.ParserException; 057 058/** 059 * Abstract class to create a notfication which will be send as a html mail to 060 * a user in OpenCms. 061 * 062 * @since 6.5.3 063 */ 064public abstract class A_CmsNotification extends CmsHtmlMail { 065 066 /** ID of the system mail host. */ 067 public static final String SYSTEM_MAIL_HOST = "system"; 068 069 /** Path to optional config file containing header and footer. */ 070 public static final String HEADER_FOOTER_CONFIG_PATH = "notification-header-footer.html"; 071 072 /** Separator between header and footer in optional config file. */ 073 public static final String HEADER_FOOTER_SEPARATOR = Pattern.quote("$BODY"); 074 075 /** The log object for this class. */ 076 private static final Log LOG = CmsLog.getLog(A_CmsNotification.class); 077 078 /** The configured header. */ 079 protected String m_configuredHeader; 080 081 /** The configured footer. */ 082 protected String m_configuredFooter; 083 084 /** The xml-content to read subject, header and footer of the notification. */ 085 protected CmsXmlContent m_mailContent; 086 087 /** The CmsObject. */ 088 protected CmsObject m_cms; 089 090 /** The locale of the receiver of the content notification. */ 091 protected Locale m_locale; 092 093 /** The macro resolver used. */ 094 protected CmsNotificationMacroResolver m_macroResolver; 095 096 /** The receiver of the notification. */ 097 private CmsUser m_receiver; 098 099 /** 100 * Creates a new A_CmsNotification.<p> 101 * 102 * @param cms the cms object to use 103 * @param receiver the receiver of the notification 104 */ 105 public A_CmsNotification(CmsObject cms, CmsUser receiver) { 106 107 super(SYSTEM_MAIL_HOST); 108 m_cms = cms; 109 m_receiver = receiver; 110 m_macroResolver = new CmsNotificationMacroResolver(cms, receiver); 111 m_macroResolver.setCmsObject(cms); 112 } 113 114 /** 115 * Adds a new macro to the used macro resolver. Macros are used for the xml 116 * content file. 117 * 118 * @param key The key of the macro. 119 * @param value The value of the macro. 120 */ 121 public void addMacro(String key, String value) { 122 123 m_macroResolver.addMacro(key, value); 124 } 125 126 /** 127 * Returns the CmsObject.<p> 128 * 129 * @return the CmsObject 130 */ 131 public CmsObject getCmsObject() { 132 133 return m_cms; 134 } 135 136 /** 137 * Returns the locale.<p> 138 * 139 * @return the locale 140 */ 141 public Locale getLocale() { 142 143 return m_locale; 144 } 145 146 /** 147 * Returns the receiver.<p> 148 * 149 * @return the receiver 150 */ 151 public CmsUser getReceiver() { 152 153 return m_receiver; 154 } 155 156 /** 157 * @see org.apache.commons.mail.Email#send() 158 */ 159 @Override 160 public String send() throws EmailException { 161 162 String messageID = null; 163 try { 164 // check if user is valid and has a mail address specified 165 if (CmsStringUtil.isEmpty(m_receiver.getEmail())) { 166 LOG.error(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_NO_ADDRESS_1, m_receiver.getName())); 167 return null; 168 } 169 170 if (LOG.isInfoEnabled()) { 171 LOG.info(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_SEND_1, m_receiver.getEmail())); 172 } 173 174 // read resource with subject, header and footer 175 m_mailContent = CmsXmlContentFactory.unmarshal(m_cms, m_cms.readFile(getNotificationContent())); 176 177 // detect locale 178 List<Locale> locales = m_mailContent.getLocales(); 179 Locale userLocale = new CmsUserSettings(m_receiver).getLocale(); 180 if (locales.contains(userLocale)) { 181 // mail is localized in the user locale, use that 182 m_locale = userLocale; 183 } else if (locales.contains(OpenCms.getWorkplaceManager().getDefaultLocale())) { 184 // mail is localized in the system default locale, use that 185 m_locale = OpenCms.getWorkplaceManager().getDefaultLocale(); 186 } else { 187 // use any localization 188 m_locale = locales.get(0); 189 } 190 191 String mailCharset = Messages.get().getBundle(m_locale).key(Messages.GUI_MAIL_CHARSET_0); 192 if (!CmsMessages.isUnknownKey(mailCharset)) { 193 setCharset(mailCharset); 194 } 195 196 // define macro resolver 197 //TODO Remove when old notifications were adjusted 198 m_macroResolver.addMacro("firstname", m_receiver.getFirstname()); 199 m_macroResolver.addMacro("lastname", m_receiver.getLastname()); 200 m_macroResolver.addMacro("project", m_cms.getRequestContext().getCurrentProject().getName()); 201 try { 202 CmsResource configRes = m_cms.readResource( 203 OpenCms.getSystemInfo().getConfigFilePath(m_cms, HEADER_FOOTER_CONFIG_PATH)); 204 CmsFile configFile = m_cms.readFile(configRes); 205 String configContent = new String(configFile.getContents(), "UTF-8"); 206 String[] configParts = configContent.split(HEADER_FOOTER_SEPARATOR); 207 if (configParts.length == 2) { 208 m_configuredHeader = configParts[0]; 209 m_configuredFooter = configParts[1]; 210 } else { 211 LOG.error("Invalid notification header/footer configuration: " + HEADER_FOOTER_CONFIG_PATH); 212 } 213 } catch (CmsVfsResourceNotFoundException e) { 214 LOG.debug(e.getLocalizedMessage(), e); 215 } catch (Exception e) { 216 LOG.error(e.getLocalizedMessage(), e); 217 } 218 219 StringBuffer msg = new StringBuffer(); 220 221 // append html header 222 appendHtmlHeader(msg); 223 appendXMLContent(msg); 224 225 // append html footer 226 appenHtmlFooter(msg); 227 228 addTo(m_receiver.getEmail(), m_receiver.getFirstname() + ' ' + m_receiver.getLastname()); 229 setSubject( 230 CmsMacroResolver.resolveMacros( 231 m_mailContent.getStringValue(m_cms, "Subject", m_locale), 232 m_macroResolver)); 233 setHtmlMsg(msg.toString()); 234 try { 235 String textMsg = CmsHtml2TextConverter.html2text(msg.toString(), mailCharset); 236 setTextMsg(textMsg); 237 } catch (ParserException e) { 238 LOG.error("Failed to process the text version of a HTML message.", e); 239 } catch (Exception e) { 240 LOG.error(e.getLocalizedMessage(), e); 241 } 242 243 // send mail 244 super.send(); 245 246 // get MIME message ID 247 messageID = getMimeMessage().getMessageID(); 248 249 } catch (CmsException e) { 250 LOG.error(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_SEND_ERROR_0), e); 251 } catch (MessagingException e) { 252 LOG.error(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_SEND_ERROR_0), e); 253 } 254 return messageID; 255 } 256 257 /** 258 * Append the html-code to start a html mail message to the given buffer.<p> 259 * 260 * @param buffer The StringBuffer to add the html code to. 261 */ 262 protected void appendHtmlHeader(StringBuffer buffer) { 263 264 if (m_configuredHeader != null) { 265 buffer.append(m_configuredHeader); 266 } else { 267 buffer.append("<html>\r\n"); 268 buffer.append(" <head>\r\n"); 269 buffer.append(" <style type=\"text/css\">\r\n"); 270 buffer.append( 271 " body { font-family: Verdana, Arial, Helvetica, sans-serif; background-color:white; }\r\n"); 272 buffer.append(" a { color:#b31b43; text-decoration:none; font-weight: bold; }\r\n"); 273 buffer.append(" a:hover { color:#b31b43; text-decoration:underline; font-weight: bold; }\r\n"); 274 buffer.append(" div.publish_link { margin: 20px 0; }\r\n"); 275 buffer.append(" table { white-space: nowrap; font-size: small; }\r\n"); 276 buffer.append(" tr.trow1 { background-color: #cdc0b0; }\r\n"); 277 buffer.append(" tr.trow2 { background-color: #eedfcc; }\r\n"); 278 buffer.append(" tr.trow3 { background-color: #ffefdb; }\r\n"); 279 buffer.append( 280 " th.rescol { border-width: 1px 0 2px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n"); 281 buffer.append( 282 " th.titlecol { border-width: 1px 1px 2px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n"); 283 buffer.append( 284 " td.rescol { border-width: 0 0 1px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n"); 285 buffer.append( 286 " td.titlecol { border-width: 0 1px 1px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n"); 287 buffer.append(" </style>\r\n"); 288 buffer.append(" </head>\r\n"); 289 buffer.append(" <body>\r\n"); 290 } 291 } 292 293 /** 294 * Append XMLContent to StringBuffer.<p> 295 * 296 * @param msg StringBuffer 297 */ 298 protected void appendXMLContent(StringBuffer msg) { 299 300 // append header from xmlcontent 301 msg.append( 302 CmsMacroResolver.resolveMacros(m_mailContent.getStringValue(m_cms, "Header", m_locale), m_macroResolver)); 303 304 // append body 305 msg.append("\n<br/><br/>\n"); 306 msg.append(generateHtmlMsg()); 307 msg.append("\n<br/><br/>\n"); 308 309 // append footer from xmlcontent 310 msg.append( 311 CmsMacroResolver.resolveMacros(m_mailContent.getStringValue(m_cms, "Footer", m_locale), m_macroResolver)); 312 } 313 314 /** 315 * Append the html-code to finish a html mail message to the given buffer. 316 * 317 * @param buffer The StringBuffer to add the html code to. 318 */ 319 protected void appenHtmlFooter(StringBuffer buffer) { 320 321 if (m_configuredFooter != null) { 322 buffer.append(m_configuredFooter); 323 } else { 324 buffer.append(" </body>\r\n" + "</html>"); 325 } 326 } 327 328 /** 329 * Overwrite the method to generate the message body of the notification. This 330 * text is placed between the header and the footer of the defined xmlcontent 331 * and the required html code is added.<p> 332 * 333 * @return The text to be inserted in the notification. 334 */ 335 protected abstract String generateHtmlMsg(); 336 337 /** 338 * Overwrite the method to return the path to the xmlcontent, where the subject, 339 * the header and the footer are defined.<p> 340 * 341 * @return The path to the xmlcontent file. 342 */ 343 protected abstract String getNotificationContent(); 344}