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.CmsMacroResolver;
042import org.opencms.util.CmsStringUtil;
043import org.opencms.xml.content.CmsXmlContent;
044import org.opencms.xml.content.CmsXmlContentFactory;
045
046import java.util.List;
047import java.util.Locale;
048import java.util.regex.Pattern;
049
050import javax.mail.MessagingException;
051
052import org.apache.commons.logging.Log;
053import org.apache.commons.mail.EmailException;
054
055/**
056 * Abstract class to create a notfication which will be send as a html mail to
057 * a user in OpenCms.
058 *
059 * @since 6.5.3
060 */
061public abstract class A_CmsNotification extends CmsHtmlMail {
062
063    /** Path to optional config file containing header and footer. */
064    public static final String HEADER_FOOTER_CONFIG_PATH = "notification-header-footer.html";
065
066    /** Separator between header and footer in optional config file. */
067    public static final String HEADER_FOOTER_SEPARATOR = Pattern.quote("$BODY");
068
069    /** The log object for this class. */
070    private static final Log LOG = CmsLog.getLog(A_CmsNotification.class);
071
072    /** The configured header. */
073    protected String m_configuredHeader;
074
075    /** The configured footer. */
076    protected String m_configuredFooter;
077
078    /** The xml-content to read subject, header and footer of the notification. */
079    protected CmsXmlContent m_mailContent;
080
081    /** The CmsObject. */
082    protected CmsObject m_cms;
083
084    /** The locale of the receiver of the content notification. */
085    protected Locale m_locale;
086
087    /** The macro resolver used. */
088    protected CmsNotificationMacroResolver m_macroResolver;
089
090    /** The receiver of the notification. */
091    private CmsUser m_receiver;
092
093    /**
094     * Creates a new A_CmsNotification.<p>
095     *
096     * @param cms the cms object to use
097     * @param receiver the receiver of the notification
098     */
099    public A_CmsNotification(CmsObject cms, CmsUser receiver) {
100
101        m_cms = cms;
102        m_receiver = receiver;
103
104        m_macroResolver = new CmsNotificationMacroResolver(cms, receiver);
105        m_macroResolver.setCmsObject(cms);
106    }
107
108    /**
109     * Adds a new macro to the used macro resolver. Macros are used for the xml
110     * content file.
111     *
112     * @param key The key of the macro.
113     * @param value The value of the macro.
114     */
115    public void addMacro(String key, String value) {
116
117        m_macroResolver.addMacro(key, value);
118    }
119
120    /**
121     * Returns the CmsObject.<p>
122     *
123     * @return the CmsObject
124     */
125    public CmsObject getCmsObject() {
126
127        return m_cms;
128    }
129
130    /**
131     * Returns the locale.<p>
132     *
133     * @return the locale
134     */
135    public Locale getLocale() {
136
137        return m_locale;
138    }
139
140    /**
141     * Returns the receiver.<p>
142     *
143     * @return the receiver
144     */
145    public CmsUser getReceiver() {
146
147        return m_receiver;
148    }
149
150    /**
151     * @see org.apache.commons.mail.Email#send()
152     */
153    @Override
154    public String send() throws EmailException {
155
156        String messageID = null;
157        try {
158            // check if user is valid and has a mail address specified
159            if (CmsStringUtil.isEmpty(m_receiver.getEmail())) {
160                LOG.error(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_NO_ADDRESS_1, m_receiver.getName()));
161                return null;
162            }
163
164            if (LOG.isInfoEnabled()) {
165                LOG.info(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_SEND_1, m_receiver.getEmail()));
166            }
167
168            // read resource with subject, header and footer
169            m_mailContent = CmsXmlContentFactory.unmarshal(m_cms, m_cms.readFile(getNotificationContent()));
170
171            // detect locale
172            List<Locale> locales = m_mailContent.getLocales();
173            Locale userLocale = new CmsUserSettings(m_receiver).getLocale();
174            if (locales.contains(userLocale)) {
175                // mail is localized in the user locale, use that
176                m_locale = userLocale;
177            } else if (locales.contains(OpenCms.getWorkplaceManager().getDefaultLocale())) {
178                // mail is localized in the system default locale, use that
179                m_locale = OpenCms.getWorkplaceManager().getDefaultLocale();
180            } else {
181                // use any localization
182                m_locale = locales.get(0);
183            }
184
185            String mailCharset = Messages.get().getBundle(m_locale).key(Messages.GUI_MAIL_CHARSET_0);
186            if (!CmsMessages.isUnknownKey(mailCharset)) {
187                setCharset(mailCharset);
188            }
189
190            // define macro resolver
191            //TODO Remove when old notifications were adjusted
192            m_macroResolver.addMacro("firstname", m_receiver.getFirstname());
193            m_macroResolver.addMacro("lastname", m_receiver.getLastname());
194            m_macroResolver.addMacro("project", m_cms.getRequestContext().getCurrentProject().getName());
195            try {
196                CmsResource configRes = m_cms.readResource(
197                    OpenCms.getSystemInfo().getConfigFilePath(m_cms, HEADER_FOOTER_CONFIG_PATH));
198                CmsFile configFile = m_cms.readFile(configRes);
199                String configContent = new String(configFile.getContents(), "UTF-8");
200                String[] configParts = configContent.split(HEADER_FOOTER_SEPARATOR);
201                if (configParts.length == 2) {
202                    m_configuredHeader = configParts[0];
203                    m_configuredFooter = configParts[1];
204                } else {
205                    LOG.error("Invalid notification header/footer configuration: " + HEADER_FOOTER_CONFIG_PATH);
206                }
207            } catch (CmsVfsResourceNotFoundException e) {
208                LOG.debug(e.getLocalizedMessage(), e);
209            } catch (Exception e) {
210                LOG.error(e.getLocalizedMessage(), e);
211            }
212
213            StringBuffer msg = new StringBuffer();
214
215            // append html header
216            appendHtmlHeader(msg);
217            appendXMLContent(msg);
218
219            // append html footer
220            appenHtmlFooter(msg);
221
222            addTo(m_receiver.getEmail(), m_receiver.getFirstname() + ' ' + m_receiver.getLastname());
223            setSubject(
224                CmsMacroResolver.resolveMacros(
225                    m_mailContent.getStringValue(m_cms, "Subject", m_locale),
226                    m_macroResolver));
227            setHtmlMsg(msg.toString());
228
229            // send mail
230            super.send();
231
232            // get MIME message ID
233            messageID = getMimeMessage().getMessageID();
234
235        } catch (CmsException e) {
236            LOG.error(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_SEND_ERROR_0), e);
237        } catch (MessagingException e) {
238            LOG.error(Messages.get().getBundle().key(Messages.LOG_NOTIFICATION_SEND_ERROR_0), e);
239        }
240        return messageID;
241    }
242
243    /**
244     * Append the html-code to start a html mail message to the given buffer.<p>
245     *
246     * @param buffer The StringBuffer to add the html code to.
247     */
248    protected void appendHtmlHeader(StringBuffer buffer) {
249
250        if (m_configuredHeader != null) {
251            buffer.append(m_configuredHeader);
252        } else {
253            buffer.append("<html>\r\n");
254            buffer.append("  <head>\r\n");
255            buffer.append("    <style type=\"text/css\">\r\n");
256            buffer.append(
257                "      body { font-family: Verdana, Arial, Helvetica, sans-serif; background-color:white; }\r\n");
258            buffer.append("      a { color:#b31b43; text-decoration:none; font-weight: bold; }\r\n");
259            buffer.append("      a:hover { color:#b31b43; text-decoration:underline; font-weight: bold; }\r\n");
260            buffer.append("      div.publish_link { margin: 20px 0; }\r\n");
261            buffer.append("      table { white-space: nowrap; font-size: small; }\r\n");
262            buffer.append("      tr.trow1 { background-color: #cdc0b0; }\r\n");
263            buffer.append("      tr.trow2 { background-color: #eedfcc; }\r\n");
264            buffer.append("      tr.trow3 { background-color: #ffefdb; }\r\n");
265            buffer.append(
266                "      th.rescol { border-width: 1px 0 2px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n");
267            buffer.append(
268                "      th.titlecol { border-width: 1px 1px 2px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n");
269            buffer.append(
270                "      td.rescol { border-width: 0 0 1px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n");
271            buffer.append(
272                "      td.titlecol { border-width: 0 1px 1px 1px; border-style: solid; border-color: #222222; padding: 5px; }\r\n");
273            buffer.append("    </style>\r\n");
274            buffer.append("  </head>\r\n");
275            buffer.append("  <body>\r\n");
276        }
277    }
278
279    /**
280     * Append XMLContent to StringBuffer.<p>
281     *
282     * @param msg StringBuffer
283     */
284    protected void appendXMLContent(StringBuffer msg) {
285
286        // append header from xmlcontent
287        msg.append(
288            CmsMacroResolver.resolveMacros(m_mailContent.getStringValue(m_cms, "Header", m_locale), m_macroResolver));
289
290        // append body
291        msg.append("\n<br/><br/>\n");
292        msg.append(generateHtmlMsg());
293        msg.append("\n<br/><br/>\n");
294
295        // append footer from xmlcontent
296        msg.append(
297            CmsMacroResolver.resolveMacros(m_mailContent.getStringValue(m_cms, "Footer", m_locale), m_macroResolver));
298    }
299
300    /**
301     * Append the html-code to finish a html mail message to the given buffer.
302     *
303     * @param buffer The StringBuffer to add the html code to.
304     */
305    protected void appenHtmlFooter(StringBuffer buffer) {
306
307        if (m_configuredFooter != null) {
308            buffer.append(m_configuredFooter);
309        } else {
310            buffer.append("  </body>\r\n" + "</html>");
311        }
312    }
313
314    /**
315     * Overwrite the method to generate the message body of the notification. This
316     * text is placed between the header and the footer of the defined xmlcontent
317     * and the required html code is added.<p>
318     *
319     * @return The text to be inserted in the notification.
320     */
321    protected abstract String generateHtmlMsg();
322
323    /**
324     * Overwrite the method to return the path to the xmlcontent, where the subject,
325     * the header and the footer are defined.<p>
326     *
327     * @return The path to the xmlcontent file.
328     */
329    protected abstract String getNotificationContent();
330}