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