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.tools;
019
020import io.stallion.boot.AppContextLoader;
021import io.stallion.boot.StallionRunAction;
022import io.stallion.boot.ServeCommandOptions;
023import io.stallion.requests.RequestHandler;
024import io.stallion.sitemaps.SiteMapController;
025import io.stallion.sitemaps.SiteMapItem;
026import io.stallion.services.Log;
027import io.stallion.settings.Settings;
028import io.stallion.testing.MockRequest;
029import io.stallion.testing.MockResponse;
030import io.stallion.utils.DateUtils;
031import jodd.jerry.Jerry;
032import org.apache.commons.io.FileUtils;
033import org.apache.commons.lang3.StringUtils;
034
035import java.io.File;
036import java.net.URL;
037import java.util.HashSet;
038import java.util.Set;
039
040import static io.stallion.utils.Literals.*;
041
042
043public class ExportToHtml implements StallionRunAction<ServeCommandOptions> {
044
045    @Override
046    public String getActionName() {
047        return "export-to-html";
048    }
049
050    @Override
051    public String getHelp() {
052        return "Exports the entire web site to plain HTML and static files.";
053    }
054
055
056    @Override
057    public ServeCommandOptions newCommandOptions() {
058        return new ServeCommandOptions();
059    }
060
061    @Override
062    public void loadApp(ServeCommandOptions options) {
063        options.setDevMode(false);
064        options.setLocalMode("false");
065        AppContextLoader.loadCompletely(options);
066    }
067
068    @Override
069    public void execute(ServeCommandOptions options) throws Exception {
070        Log.info("EXECUTE EXPORT ACTION!!");
071        String exportFolder = Settings.instance().getTargetFolder() + "/export-" + DateUtils.formatNow("yyyy-MM-dd-HH-mm-ss");
072        File export = new File(exportFolder);
073        if (!export.exists()) {
074            export.mkdirs();
075        }
076        FileUtils.copyDirectory(new File(Settings.instance().getTargetFolder() + "/assets"), new File(exportFolder + "/st-assets"));
077
078
079
080        Set<String> assets = new HashSet<>();
081
082        Set<String> allUrlPaths = new HashSet<>();
083
084        for(SiteMapItem item: SiteMapController.instance().getAllItems()) {
085            String uri = item.getPermalink();
086            Log.info("URI {0}", uri);
087            if (!uri.contains("://")) {
088                uri = "http://localhost" + uri;
089            }
090            URL url = new URL(uri);
091            allUrlPaths.add(url.getPath());
092        }
093
094        allUrlPaths.addAll(ExporterRegistry.instance().exportAll());
095
096
097        for(String path: allUrlPaths) {
098            Log.info("Export page {0}", path);
099            MockRequest request = new MockRequest(path, "GET");
100            MockResponse response = new MockResponse();
101            RequestHandler.instance().handleStallionRequest(request, response);
102            response.getContent();
103
104            if (!path.contains(".")) {
105                if (!path.endsWith("/")) {
106                    path += "/";
107                }
108                path += "index.html";
109            }
110            File file = new File(exportFolder + path);
111            File folder = new File(file.getParent());
112            if (!folder.isDirectory()) {
113                folder.mkdirs();
114            }
115            String html = response.getContent();
116            html = html.replace(Settings.instance().getSiteUrl(), "");
117            FileUtils.write(file, html, UTF8);
118            assets.addAll(findAssetsInHtml(response.getContent()));
119        }
120
121        for (String src: assets) {
122            Log.info("Asset src: {0}", src);
123
124            MockRequest request = new MockRequest(src, "GET");
125            MockResponse response = new MockResponse();
126            RequestHandler.instance().handleStallionRequest(request, response);
127            int min = 300;
128            if (response.getContent().length() < 300) {
129                min = response.getContent().length();
130            }
131            URL url = new URL("http://localhost" + src);
132            File file = new File(exportFolder + url.getPath());
133            File folder = new File(file.getParent());
134            if (!folder.isDirectory()) {
135                folder.mkdirs();
136            }
137            FileUtils.write(file, response.getContent(), UTF8);
138        }
139
140    }
141
142    public Set<String> findAssetsInHtml(String html) {
143        HashSet<String> assets = new HashSet<>();
144        Jerry jerry = Jerry.jerry(html);
145        for(Jerry j :jerry.find("script")) {
146            String src = j.attr("src");
147            Log.info("SCRIPT TAG HTML {0} {1}", j.htmlAll(true), src);
148            if (empty(src)) {
149                continue;
150            }
151            Log.info("Add asset {0}", src);
152            assets.add(src);
153        }
154        for(Jerry j :jerry.find("link")) {
155            Log.info("LINK TAG HTML {0}", j.htmlAll(true));
156            if (!"stylesheet".equals(j.attr("rel"))) {
157                continue;
158            }
159            String src = j.attr("href");
160            if (empty(src)) {
161                continue;
162            }
163            assets.add(src);
164
165        }
166        for(Jerry j :jerry.find("img")) {
167            String src = j.attr("src");
168            if (empty(src)) {
169                continue;
170            }
171            assets.add(src);
172        }
173        HashSet<String> filteredAssets = new HashSet<>();
174        Log.info("CDN URL {0}", Settings.instance().getCdnUrl());
175        Log.info("Site URL {0}", Settings.instance().getSiteUrl());
176        for (String src: assets) {
177            if (src.startsWith(Settings.instance().getCdnUrl())) {
178                src = StringUtils.replace(src, Settings.instance().getCdnUrl(), "");
179                if (!src.startsWith("/")) {
180                    src = "/" + src;
181                }
182            }
183            if (src.startsWith(Settings.instance().getSiteUrl())) {
184                src = StringUtils.replace(src, Settings.instance().getSiteUrl(), "");
185            }
186            if (src.startsWith("/")) {
187                filteredAssets.add(src);
188            }
189        }
190        Log.info("Asset count {0}", filteredAssets.size());
191        return filteredAssets;
192    }
193}
194