001/*
002 * Stallion Core: A Modern Web Framework
003 *
004 * Copyright (C) 2015 - 2016 Stallion Software LLC.
005 *
006 * This program is free software: you can redistribute it and/or modify it under the terms of the
007 * GNU General Public License as published by the Free Software Foundation, either version 2 of
008 * the License, or (at your option) any later version. This program is distributed in the hope that
009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
011 * License for more details. You should have received a copy of the GNU General Public License
012 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
013 *
014 *
015 *
016 */
017
018package io.stallion.assets;
019
020import io.stallion.Context;
021import io.stallion.assetBundling.AssetBundle;
022import io.stallion.assetBundling.AssetFile;
023import io.stallion.exceptions.NotFoundException;
024import io.stallion.exceptions.UsageException;
025import io.stallion.plugins.PluginRegistry;
026import io.stallion.settings.Settings;
027import org.apache.commons.codec.digest.DigestUtils;
028import org.apache.commons.io.IOUtils;
029
030import java.io.File;
031import java.io.IOException;
032import java.io.UnsupportedEncodingException;
033import java.net.URISyntaxException;
034import java.net.URL;
035import java.net.URLEncoder;
036import java.text.MessageFormat;
037import java.util.List;
038import java.util.Map;
039
040import static io.stallion.utils.Literals.empty;
041import static io.stallion.utils.Literals.or;
042
043
044public class FileSystemAssetBundleRenderer {
045    private String fileSystemPath;
046    private String path;
047    private String extension;
048    private File bundleFile;
049    private String bundleRelativePath;
050
051    public FileSystemAssetBundleRenderer(String path) {
052        path = AssetsController.ensureSafeAssetsPath(path);
053        int i = path.indexOf(".bundle.") + 7;
054        if (i <= 6) {
055            throw new UsageException("The path - "  + path + " - is not a valid bundle name. Expected to end with .bundle.js, .bundle.head.js, or .bundle.css");
056        }
057        this.extension = path.substring(i);
058        path = path.substring(0, i);
059        if (path.startsWith("/")) {
060            path = path.substring(1);
061        }
062        if (path.contains("..")) {
063            throw new UsageException("Illegal assets path " + path);
064        }
065        if (!path.startsWith("assets/")) {
066            this.bundleRelativePath = path;
067            path = "assets/" + path;
068        } else {
069            this.bundleRelativePath = path.substring(7);
070        }
071        this.path = path;
072        fileSystemPath = Settings.instance().getTargetFolder() + "/" + path;
073        bundleFile = new File(fileSystemPath);
074        if (!bundleFile.exists()) {
075            throw new NotFoundException("The bundle for path " + path + " was not found in the site /assets folder.");
076        }
077
078    }
079
080    public String renderFile(String filePath) {
081        AssetBundle bundle = io.stallion.assetBundling.BundleRegistry.instance().getByPath(bundleFile.getAbsolutePath());
082        return bundle.renderPath(filePath, true);
083    }
084
085    public String renderDebugHtml() {
086
087        AssetBundle bundle = io.stallion.assetBundling.BundleRegistry.instance().getByPath(bundleFile.getAbsolutePath());
088        bundle.hydrateFilesIfNeeded(true);
089        StringBuilder builder = new StringBuilder();
090        for (AssetFile af: bundle.getFiles()) {
091            af.hydrateIfNeeded(true);
092            String tag = "<script src=\"{0}\" type=\"text/javascript\"></script>";
093            if (".head.js".equals(extension)) {
094                if (empty(af.getHeadJavaScript())) {
095                    continue;
096                }
097            } else if (".css".equals(extension)) {
098                if (empty(af.getCss())) {
099                    continue;
100                }
101                tag = "<link rel=\"stylesheet\" href=\"{0}\">";
102            } else if (".js".equals(extension)) {
103                if (empty(af.getJavaScript())) {
104                    continue;
105                }
106            }
107            String url;
108            String relativePath = af.getRelativePath();
109            if (!relativePath.startsWith("/")) {
110                relativePath = "/" + relativePath;
111            }
112            try {
113                url = Settings.instance().getCdnUrl() + "/st-assets/" + bundleRelativePath + this.extension
114                        + "?isBundleFile=true&bundleFilePath=" + URLEncoder.encode(relativePath + extension, "utf-8")
115                        + "&ts=" + af.getHydratedAt();
116                //url = Settings.instance().getCdnUrl() + "/st-file-bundle-assets" + relativePath + extension + "?bundlePath=" + URLEncoder.encode(this.path, "utf-8") + extension + "&ts=" + af.getHydratedAt();
117            } catch (UnsupportedEncodingException e) {
118                throw new RuntimeException(e);
119            }
120            builder.append("    " + MessageFormat.format(tag, url) + "\n");
121        }
122        return builder.toString();
123
124    }
125
126    public String renderProductionHtml() {
127        String content = renderProductionContent();
128        String hash = DigestUtils.md5Hex(content);
129        String tag = "<script src=\"{0}\" type=\"text/javascript\"></script>";
130        String url = Settings.instance().getCdnUrl() + "/st-assets/" + bundleRelativePath + extension + "?isConcatenatedFileBundle=true&hash=" + hash;
131        if (".css".equals(extension)) {
132            tag = "<link rel=\"stylesheet\" href=\"{0}\">";
133        }
134        return MessageFormat.format(tag, url);
135    }
136
137
138
139    public String renderProductionContent() {
140        AssetBundle bundle = io.stallion.assetBundling.BundleRegistry.instance().getByPath(bundleFile.getAbsolutePath());
141        bundle.hydrateAllFilesIfNeeded(true);
142        if (extension.equals(".head.js")) {
143            return bundle.getConcatenatedHeadJavaScript();
144        } else if (extension.equals(".js")) {
145            return bundle.getConcatenatedJavaScript();
146        } else if (extension.equals(".css")) {
147            return bundle.getConcatenatedCss();
148        }
149        throw new NotFoundException("Could not find a bundle handler for extension " + extension);
150
151    }
152}