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}