001package org.apache.turbine.util.velocity;
002
003
004import java.net.MalformedURLException;
005
006/*
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements.  See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership.  The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License.  You may obtain a copy of the License at
014 *
015 *   http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied.  See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 */
024
025
026import java.net.URL;
027import java.util.Hashtable;
028
029import org.apache.commons.lang3.StringUtils;
030import org.apache.commons.mail.EmailException;
031import org.apache.commons.mail.HtmlEmail;
032import org.apache.logging.log4j.LogManager;
033import org.apache.logging.log4j.Logger;
034import org.apache.turbine.Turbine;
035import org.apache.turbine.TurbineConstants;
036import org.apache.turbine.services.TurbineServices;
037import org.apache.turbine.services.velocity.VelocityService;
038import org.apache.velocity.context.Context;
039
040/**
041 * This is a simple class for sending html email from within Velocity.
042 * Essentially, the bodies (text and html) of the email are a Velocity
043 * Context objects.  The beauty of this is that you can send email
044 * from within your Velocity template or from your business logic in
045 * your Java code.  The body of the email is just a Velocity template
046 * so you can use all the template functionality of Velocity within
047 * your emails!
048 *
049 * <p>This class allows you to send HTML email with embedded content
050 * and/or with attachments.  You can access the VelocityHtmlEmail
051 * instance within your templates trough the <code>$mail</code>
052 * Velocity variable.
053 * <p><code>VelocityHtmlEmail   myEmail= new VelocityHtmlEmail(data);<br>
054 *                              context.put("mail", myMail);</code>
055 * <b>or</b>
056 *    <code>VelocityHtmlEmail   myEmail= new VelocityHtmlEmail(context);<br>
057 *                              context.put("mail", myMail);</code>
058 *
059 *
060 * <p>The templates should be located under your Template turbine
061 * directory.
062 *
063 * <p>This class wraps the HtmlEmail class from commons-email.  Thus, it uses
064 * the JavaMail API and also depends on having the mail.server property
065 * set in the TurbineResources.properties file.  If you want to use
066 * this class outside of Turbine for general processing that is also
067 * possible by making sure to set the path to the
068 * TurbineResources.properties.  See the
069 * TurbineResourceService.setPropertiesFileName() method for more
070 * information.
071 *
072 * <p>This class is basically a conversion of the WebMacroHtmlEmail
073 * written by Regis Koenig
074 *
075 * <p>You can turn on debugging for the JavaMail API by calling
076 * setDebug(true).  The debugging messages will be written to System.out.
077 *
078 * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
079 * @author <a href="mailto:A.Schild@aarboard.ch">Andre Schild</a>
080 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
081 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
082 * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
083 * @version $Id$
084 */
085public class VelocityHtmlEmail extends HtmlEmail
086{
087    /** Logging */
088    private static final Logger log = LogManager.getLogger(VelocityHtmlEmail.class);
089
090    /**
091     * The html template to process, relative to VM's template
092     * directory.
093     */
094    private String htmlTemplate = null;
095
096    /**
097     * The text template to process, relative to VM's template
098     * directory.
099     */
100    private String textTemplate = null;
101
102    /** The cached context object. */
103    private Context context = null;
104
105    /** The map of embedded files. */
106    private Hashtable<String, String> embmap = null;
107
108    /** Address of outgoing mail server */
109    private String mailServer;
110
111    /**
112     * Constructor
113     */
114    public VelocityHtmlEmail()
115    {
116        super();
117    }
118
119    /**
120     * Constructor, sets the context object.
121     *
122     * @param context A Velocity context object.
123     */
124    public VelocityHtmlEmail(Context context)
125    {
126        this();
127        this.context = context;
128        embmap = new Hashtable<>();
129    }
130
131    /**
132     * Set the HTML template for the mail.  This is the Velocity
133     * template to execute for the HTML part.  Path is relative to the
134     * VM templates directory.
135     *
136     * @param template A String.
137     * @return A VelocityHtmlEmail (self).
138     */
139    public VelocityHtmlEmail setHtmlTemplate(String template)
140    {
141        this.htmlTemplate = template;
142        return this;
143    }
144
145    /**
146     * Set the text template for the mail.  This is the Velocity
147     * template to execute for the text part.  Path is relative to the
148     * VM templates directory
149     *
150     * @param template A String.
151     * @return A VelocityHtmlEmail (self).
152     */
153    public VelocityHtmlEmail setTextTemplate(String template)
154    {
155        this.textTemplate = template;
156        return this;
157    }
158
159    /**
160     * Sets the address of the outgoing mail server.  This method
161     * should be used when you need to override the value stored in
162     * TR.props.
163     *
164     * @param serverAddress host name of your outgoing mail server
165     */
166    public void setMailServer(String serverAddress)
167    {
168        this.mailServer = serverAddress;
169    }
170
171    /**
172     * Gets the host name of the outgoing mail server.  If the server
173     * name has not been set by calling setMailServer(), the value
174     * from TR.props for mail.server will be returned.  If TR.props
175     * has no value for mail.server, localhost will be returned.
176     *
177     * @return host name of the mail server.
178     */
179    public String getMailServer()
180    {
181        return StringUtils.isNotEmpty(mailServer) ? mailServer
182                : Turbine.getConfiguration().getString(
183                TurbineConstants.MAIL_SERVER_KEY,
184                TurbineConstants.MAIL_SERVER_DEFAULT);
185    }
186
187    /**
188     * Actually send the mail.
189     *
190     * @throws EmailException thrown if mail cannot be sent.
191     */
192    @Override
193    public String send() throws EmailException
194    {
195        context.put("mail", this);
196
197        try
198        {
199            VelocityService velocityService = (VelocityService)TurbineServices.getInstance().getService(VelocityService.SERVICE_NAME);
200
201            if (htmlTemplate != null)
202            {
203                setHtmlMsg(velocityService.handleRequest(context, htmlTemplate));
204            }
205            if (textTemplate != null)
206            {
207                setTextMsg(velocityService.handleRequest(context, textTemplate));
208            }
209        }
210        catch (Exception e)
211        {
212            throw new EmailException("Cannot parse velocity template", e);
213        }
214        setHostName(getMailServer());
215        return super.send();
216    }
217
218    /**
219     * Embed a file in the mail.  The file can be referenced through
220     * its Content-ID.  This function also registers the CID in an
221     * internal map, so the embedded file can be referenced more than
222     * once by using the getCid() function.  This may be useful in a
223     * template.
224     *
225     * <p>Example of template:
226     *
227     * <pre>
228     * &lt;html&gt;
229     * &lt;!-- $mail.embed("http://server/border.gif","border.gif"); --&gt;
230     * &lt;img src=$mail.getCid("border.gif")&gt;
231     * &lt;p&gt;This is your content
232     * &lt;img src=$mail.getCid("border.gif")&gt;
233     * &lt;/html&gt;
234     * </pre>
235     *
236     * @param surl A String.
237     * @param name A String.
238     * @return A String with the cid of the embedded file.
239     *
240     * @see HtmlEmail#embed(URL surl, String name) embed.
241     */
242    @Override
243    public String embed(String surl, String name)
244    {
245        String cid = "";
246        try
247        {
248            URL url = new URL(surl);
249            cid = super.embed(url, name);
250            embmap.put(name, cid);
251        }
252        catch (MalformedURLException | EmailException e)
253        {
254            log.error("cannot embed {}", surl, e);
255        }
256        return cid;
257    }
258
259    /**
260     * Get the cid of an embedded file.
261     *
262     * @param filename A String.
263     * @return A String with the cid of the embedded file.
264     * @see #embed(String surl, String name) embed.
265     */
266    public String getCid(String filename)
267    {
268        String cid = embmap.get(filename);
269        return "cid:" + cid;
270    }
271
272}