Package hudson.plugins.emailext.plugins

Source Code of hudson.plugins.emailext.plugins.CssInliner

package hudson.plugins.emailext.plugins;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Entities;
import org.jsoup.select.Elements;

import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.StringTokenizer;

/**
* <p>Inlines CSS to avoid the dreaded GMail Grimace.</p>
*
* <p>The magic keyword is <code><em>data-inline="true"</em></code>.</p>
*
* <ul>
* <li>
* When used in conjunction with the <code>style</code> tag, it inlines the stylesheet defined there into all
* html elements matching the rules.
* </li>
* <li>
* When used with the <code>img</code> tag, it base64 encodes the image it found to make it visible in the
* email.
* </li>
* </ul>
*
* @author <a href="https://github.com/rahulsom">Rahul Somasunderam</a>
*/
public class CssInliner {
  public static final String CSS_STYLE = "cssstyle";
  public static final String STYLE_ATTR = "style";
  public static final String STYLE_TAG = "style";
  public static final String IMG_TAG = "img";
  public static final String IMG_SRC_ATTR = "src";

  private static String concatenateProperties(String oldProp, String newProp) {
    if (!oldProp.endsWith(";"))
      oldProp += ";";
    return oldProp.trim() + " " + newProp.trim() + ";";
  }

  /**
   * Generates a stylesheet from an html document
   *
   * @param doc the html document
   * @return a string representing the stylesheet.
   */
  private String fetchStyles(Document doc) {
    Elements els = doc.select(STYLE_TAG);
    StringBuilder styles = new StringBuilder();
    for (Element e : els) {
      if (e.attr("data-inline").equals("true")) {
        styles.append(e.data());
        e.remove();
      }
    }
    return styles.toString();
  }

  /**
   * Takes an input string representing an html document and processes it with the Css Inliner.
   * @param input the html document
   * @return the processed html document
   */
  public String process(String input) {

    Document doc = Jsoup.parse(input);
   
    extractStyles(doc);
    applyStyles(doc);
    inlineImages(doc);

    doc.outputSettings(doc.outputSettings().prettyPrint(false).escapeMode(Entities.EscapeMode.xhtml));
    String output = doc.outerHtml();
    return output;
  }

  /**
   * Inlines images marked with <code>data-inline="true"</code>
   *
   * @param doc the html document
   */
  private void inlineImages(Document doc) {
    Elements allImages = doc.getElementsByTag(IMG_TAG);
    for (Element img : allImages) {
      if (img.attr("data-inline").equals("true")) {
        String src = img.attr(IMG_SRC_ATTR);
        try {
          URL url = new URL(src);
          URLConnection urlConnection = url.openConnection();
          urlConnection.connect();
          String contentType = urlConnection.getContentType();

          urlConnection.getContent();
          byte[] srcContent = IOUtils.toByteArray(url.openStream());
          String base64 = new Base64().encodeToString(srcContent);

          img.attr(IMG_SRC_ATTR, MessageFormat.format("data:{0};base64,{1}", contentType, base64));
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }

  /**
   * Transfers styles from the <code>cssstyle</code> attribute to the <code>style</code> attribute.
   *
   * @param doc the html document
   */
  private void applyStyles(Document doc) {
    Elements allStyledElements = doc.getElementsByAttribute(CSS_STYLE);

    for (Element e : allStyledElements) {
      String newStyle = e.attr(CSS_STYLE);
      String oldStyle = e.attr(STYLE_ATTR);
      e.attr(STYLE_ATTR, (newStyle + "; " + oldStyle).replace(";;", ";"));
      e.removeAttr(CSS_STYLE);
    }
  }

  /**
   * Extracts styles from the stylesheet and applies them to a <code>cssstyle</code> attribute. This is because the
   * styles need to be applied sequentially, but before the <code>style</code> defined for the element inline.
   *
   * @param doc the html document
   */
  private void extractStyles(Document doc) {
    String stylesheet = fetchStyles(doc);

    String trimmedStylesheet = stylesheet.replaceAll("\n", "").replaceAll("/\\*.*?\\*/", "").replaceAll(" +", " ");
    String styleRules = trimmedStylesheet.trim(), delims = "{}";
    StringTokenizer st = new StringTokenizer(styleRules, delims);
    while (st.countTokens() > 1) {
      String selector = st.nextToken(), properties = st.nextToken();
      Elements selectedElements = doc.select(selector);
      for (Element selElem : selectedElements) {
        String oldProperties = selElem.attr(CSS_STYLE);
        selElem.attr(CSS_STYLE,
            oldProperties.length() > 0 ? concatenateProperties(
                oldProperties, properties) : properties);
      }
    }
  }
}
TOP

Related Classes of hudson.plugins.emailext.plugins.CssInliner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.