Skip to content

Commit

Permalink
Merge pull request #1392 from jediwhale/master
Browse files Browse the repository at this point in the history
Publish FitNesse site to static html files
  • Loading branch information
Mike Stockdale authored Sep 26, 2022
2 parents 729dfd5 + 02a0f6c commit 6ae38b8
Show file tree
Hide file tree
Showing 18 changed files with 421 additions and 52 deletions.
3 changes: 2 additions & 1 deletion src/fitnesse/fixtures/FileSection.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.fixtures;

import fitnesse.wiki.PathParser;
import util.FileUtil;

import java.io.File;
Expand All @@ -13,7 +14,7 @@ public FileSection(String type) throws Exception {
if ("setup".equalsIgnoreCase(type)) {
File dir = new File(FitnesseFixtureContext.context.getRootPagePath());
dir.mkdir();
fileSection = new File(dir, "files");
fileSection = new File(dir, PathParser.FILES);
fileSection.mkdir();
} else {
FileUtil.deleteFileSystemDirectory(FitnesseFixtureContext.context.getRootPagePath());
Expand Down
2 changes: 1 addition & 1 deletion src/fitnesse/fixtures/PageDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public boolean pageIsASymbolicLink(String pageName) {
WikiPage root = FitnesseFixtureContext.context.getRootPage();
WikiPagePath pagePath = PathParser.parse(pageName);
WikiPage thePage = root.getPageCrawler().getPage(pagePath);
return thePage instanceof SymbolicPage;
return thePage.isSymbolicPage();
}

public boolean pageExists(String pageName) {
Expand Down
2 changes: 2 additions & 0 deletions src/fitnesse/responders/ResponderFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import fitnesse.responders.files.DeleteConfirmationResponder;
import fitnesse.responders.files.DeleteFileResponder;
import fitnesse.responders.files.FileResponder;
import fitnesse.responders.files.PublishResponder;
import fitnesse.responders.files.RenameFileConfirmationResponder;
import fitnesse.responders.files.RenameFileResponder;
import fitnesse.responders.files.UploadResponder;
Expand Down Expand Up @@ -115,6 +116,7 @@ public ResponderFactory(String rootPath) {
addResponder("overview", SuiteOverviewResponder.class);
addResponder("compareVersions", VersionComparerResponder.class);
addResponder("instruction", InstructionResponder.class);
addResponder("publish", PublishResponder.class);
filterMap = new HashMap<>();
}

Expand Down
20 changes: 5 additions & 15 deletions src/fitnesse/responders/SerializedPageResponder.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,13 @@
import fitnesse.util.XmlUtil;
import fitnesse.wiki.fs.PageXmlizer;
import fitnesse.wiki.PathParser;
import fitnesse.wiki.SymbolicPage;
import fitnesse.wiki.WikiPage;
import fitnesse.wiki.WikiPagePath;
import fitnesse.wiki.XmlizePageCondition;
import org.w3c.dom.Document;

public class SerializedPageResponder implements SecureResponder {
private XmlizePageCondition xmlizePageCondition = new XmlizePageCondition() {
@Override
public boolean canBeXmlized(WikiPage page) {
return !(page instanceof SymbolicPage);
}
};
private final XmlizePageCondition xmlizePageCondition = page -> !page.isSymbolicPage();

@Override
public Response makeResponse(FitNesseContext context, Request request) throws Exception {
Expand All @@ -40,12 +34,10 @@ public Response makeResponse(FitNesseContext context, Request request) throws Ex
PageXmlizer pageXmlizer = new PageXmlizer();
pageXmlizer.addPageCondition(xmlizePageCondition);
Document doc = pageXmlizer.xmlize(page);
SimpleResponse response = makeResponseWithxml(doc);
return response;
return makeResponseWithxml(doc);
} else if ("data".equals(request.getInput("type"))) {
Document doc = new PageXmlizer().xmlize(page.getData());
SimpleResponse response = makeResponseWithxml(doc);
return response;
return makeResponseWithxml(doc);
} else {
Object object = getObjectToSerialize(request, page);
byte[] bytes = serializeToBytes(object);
Expand Down Expand Up @@ -79,8 +71,7 @@ private Object getObjectToSerialize(Request request, WikiPage page) {
private WikiPage getRequestedPage(Request request, FitNesseContext context) {
String resource = request.getResource();
WikiPagePath path = PathParser.parse(resource);
WikiPage page = context.getRootPage().getPageCrawler().getPage(path);
return page;
return context.getRootPage().getPageCrawler().getPage(path);
}

private SimpleResponse responseWith(byte[] bytes) {
Expand All @@ -95,8 +86,7 @@ private byte[] serializeToBytes(Object object) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(byteStream);
os.writeObject(object);
os.close();
byte[] bytes = byteStream.toByteArray();
return bytes;
return byteStream.toByteArray();
}

@Override
Expand Down
18 changes: 8 additions & 10 deletions src/fitnesse/responders/files/FileResponder.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import fitnesse.authentication.SecureOperation;
import fitnesse.authentication.SecureResponder;
import fitnesse.util.Clock;
import fitnesse.wiki.PathParser;
import util.FileUtil;
import util.StreamReader;
import fitnesse.FitNesseContext;
Expand Down Expand Up @@ -170,12 +171,12 @@ public static String getContentType(String filename) {
}

public static boolean isInFilesDirectory(File rootPath, File file) throws IOException {
return isInSubDirectory(new File(rootPath, "files").getCanonicalFile(),
return isInSubDirectory(new File(rootPath, PathParser.FILES).getCanonicalFile(),
file.getCanonicalFile());
}

public static boolean isInFilesFitNesseDirectory(File rootPath, File file) throws IOException {
return isInSubDirectory(new File(new File(rootPath, "files"), "fitnesse").getCanonicalFile(),
return isInSubDirectory(new File(new File(rootPath, PathParser.FILES), "fitnesse").getCanonicalFile(),
file.getCanonicalFile());
}

Expand All @@ -185,14 +186,11 @@ private static boolean isInSubDirectory(File dir, File file) {

@Override
public SecureOperation getSecureOperation() {
return new SecureOperation() {
@Override
public boolean shouldAuthenticate(FitNesseContext context, Request request) {
try {
return new File(context.getRootPagePath(), URLDecoder.decode(request.getResource(), FileUtil.CHARENCODING)).isDirectory();
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Invalid URL encoding", e);
}
return (context, request) -> {
try {
return new File(context.getRootPagePath(), URLDecoder.decode(request.getResource(), FileUtil.CHARENCODING)).isDirectory();
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Invalid URL encoding", e);
}
};
}
Expand Down
63 changes: 63 additions & 0 deletions src/fitnesse/responders/files/PublishResponder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package fitnesse.responders.files;

import fitnesse.FitNesseContext;
import fitnesse.Responder;
import fitnesse.http.Request;
import fitnesse.http.Response;
import fitnesse.http.SimpleResponse;
import fitnesse.updates.ReplacingFileUpdate;
import fitnesse.updates.Update;
import fitnesse.wiki.PathParser;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class PublishResponder implements Responder {
@Override
public Response makeResponse(FitNesseContext context, Request request) throws Exception {
report = new StringBuilder();
destination = request.getInput("destination");

Path resourcePath = Paths.get(context.getRootPagePath(), PathParser.FILES, "fitnesse", "publishResources.txt");
Files.readAllLines(resourcePath).stream().map(this::makeUpdate).forEach(this::doUpdate);

Path templatePath = Paths.get(context.getRootPagePath(), PathParser.FILES, "fitnesse", "publishTemplate.html");
String template = String.join(System.lineSeparator(), Files.readAllLines(templatePath));
Publisher publisher = new Publisher(template, destination, context.getRootPage().getPageCrawler(), this::writePage);
report.append(publisher.traverse(context.getRootPage()));

SimpleResponse response = new SimpleResponse();
response.setContent(report.toString());
return response;
}

private void doUpdate(Update update) {
try {
update.doUpdate();
} catch (IOException e) {
report.append(e).append("<br>");
}
}

private Update makeUpdate(String resource) {
report.append(resource).append("<br>");
return new ReplacingFileUpdate("fitnesse/resources/" + resource, Paths.get(destination, PathParser.FILES, "fitnesse", resource).toFile().getParentFile());
}

private void writePage(String content, String path) {
Path directory = Paths.get(path).getParent();
try {
if (!Files.exists(directory))
Files.createDirectories(directory);
Files.write(Paths.get(path), content.getBytes());
}
catch (IOException e) {
throw new RuntimeException(e);
}
}

private String destination;
private StringBuilder report;
}
131 changes: 131 additions & 0 deletions src/fitnesse/responders/files/Publisher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package fitnesse.responders.files;

import fitnesse.html.template.PageTitle;
import fitnesse.util.StringTransform;
import fitnesse.wiki.PageCrawler;
import fitnesse.wiki.PathParser;
import fitnesse.wiki.WikiPage;
import fitnesse.wiki.WikiPagePath;
import fitnesse.wiki.WikiPageUtil;
import fitnesse.wikitext.parser.TextMaker;

import java.io.File;
import java.util.function.BiConsumer;

public class Publisher {
//todo: downloads?

public Publisher(String template, String destination, PageCrawler crawler, BiConsumer<String, String> writer) {
this.template = template;
this.destination = destination;
this.crawler = crawler;
this.writer = writer;
}

public String traverse(WikiPage page) {
StringBuilder result = new StringBuilder();
String path = destinationPath(page);
result.append(path).append("<br>");
try {
writer.accept(pageContent(page), path);
}
catch (Exception e) {
result.append(e).append("<br>");
}
for (WikiPage child: page.getChildren()) {
if (child.getName().equals(PathParser.FILES)) continue;
if (child.getName().equals(WikiPageUtil.PAGE_HEADER)) continue;
if (child.getName().equals(WikiPageUtil.PAGE_FOOTER)) continue;
//todo: skip others?
if (child.isSymbolicPage()) continue;
result.append(traverse(child));
}
return result.toString();
}

private String pageContent(WikiPage page) {
return fixSources(page, fixLinks(page, replaceKeywords(page, template)));
}

private String replaceKeywords(WikiPage page, String input) {
StringTransform transform = new StringTransform(input);
String[][] keywords = new String[][] {
{ "title", PathParser.render(page.getFullPath()) },
{ "breadcrumbs", makeBreadCrumbs(page) },
{ "body", WikiPageUtil.makePageHtml(page) },
{ "footer", WikiPageUtil.getFooterPageHtml(page) },
{ "", ""}
};
while (transform.find("$")) {
for (String[] keyword: keywords) {
if (keyword[0].length() == 0) transform.copy();
else if (transform.startsWith(keyword[0] + "$")) {
transform.insert(keyword[1]);
transform.skip(keyword[0].length() + 1);
break;
}
}
}
return transform.getOutput();
}

private String makeBreadCrumbs(WikiPage page) {
PageTitle pt = new PageTitle(page.getFullPath());
StringBuilder breadcrumbs = new StringBuilder();
for (PageTitle.BreadCrumb breadcrumb : pt.getBreadCrumbs()) {
breadcrumbs.append("<li><a href=\"").append(breadcrumb.getLink()).append("\">").append(breadcrumb.getName()).append("</a></li>\n");
}
breadcrumbs.append("<li>").append(page.getName()).append("</li>\n");
return breadcrumbs.toString();
}

private String fixLinks(WikiPage page, String input) {
StringTransform transform = new StringTransform(input);
long depth = page.getFullPath().toString().chars().filter(c -> c == '.').count();
while (transform.find(" href=\"")) {
transform.copy();
fixWikiWord(transform, depth);
fixFiles(transform, depth);
}
return transform.getOutput();
}

private String fixSources(WikiPage page, String input) {
StringTransform transform = new StringTransform(input);
long depth = page.getFullPath().toString().chars().filter(c -> c == '.').count();
while (transform.find(" src=\"")) {
transform.copy();
fixFiles(transform, depth);
}
return transform.getOutput();
}

private void fixWikiWord(StringTransform transform, long depth) {
int wikiWordStart = transform.getCurrent();
if (transform.startsWith("/") || transform.startsWith(".")) {
wikiWordStart++;
}
int wikiWordLength = TextMaker.findWikiWordLength(transform.from(wikiWordStart));
if (wikiWordLength == 0) return;
WikiPagePath path = PathParser.parse(transform.from(wikiWordStart).substring(0, wikiWordLength));
WikiPage targetPage = crawler.getPage(path).getRealPage();
for (int i = 0; i < depth; i++) transform.insert("../");
transform.insert(targetPage.getFullPath().toString().replace('.', '/') + ".html");
transform.skipTo(wikiWordStart + wikiWordLength);
}

private void fixFiles(StringTransform transform, long depth) {
if (!transform.startsWith("files/")) return;
for (int i = 0; i < depth; i++) transform.insert("../");
}

private String destinationPath(WikiPage page) {
String pagePath = page.getFullPath().toString().replace(".", File.separator);
return destination + File.separator + (pagePath.length() > 0 ? pagePath : "root") + ".html";
}

private final String template;
private final String destination;
private final PageCrawler crawler;
private final BiConsumer<String, String> writer;
}
12 changes: 4 additions & 8 deletions src/fitnesse/responders/refactoring/PageMovementResponder.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public Response makeResponse(FitNesseContext context, Request request) throws Ex
}

if (!getAndValidateNewParentPage(context, request)) {
return makeErrorMessageResponder(newParentPath == null ? "null" : newParentPath.toString() + " does not exist.").makeResponse(context, request);
return makeErrorMessageResponder(newParentPath == null ? "null" : newParentPath + " does not exist.").makeResponse(context, request);
}

if (!getAndValidateRefactoringParameters(request)) {
Expand Down Expand Up @@ -96,11 +96,11 @@ protected String createRedirectionUrl(WikiPage newParent, String newName) {

protected void movePage(WikiPage movedPage, WikiPage newParentPage, String pageName) throws RefactorException {

if (isSymlinkedPage(movedPage)) {
if (isSymlinkedPage(movedPage.getParent())) {
if (movedPage.isSymbolicPage()) {
if (movedPage.getParent().isSymbolicPage()) {
throw new RefactorException("Can not move symlink page when parent page is also a symlink");
}
WikiPage referencedPage = ((SymbolicPage) movedPage).getRealPage();
WikiPage referencedPage = movedPage.getRealPage();
removeSymlink(movedPage);
createSymlink(referencedPage, newParentPage, pageName);
} else {
Expand All @@ -122,10 +122,6 @@ protected void moveChildren(WikiPage movedPage, WikiPage newParentPage) throws R
}
}

private boolean isSymlinkedPage(WikiPage page) {
return page instanceof SymbolicPage;
}

private void removeSymlink(WikiPage movedPage) {
WikiPage parent = movedPage.getParent();
PageData data = parent.getData();
Expand Down
3 changes: 2 additions & 1 deletion src/fitnesse/testrunner/run/FileBasedTestRunFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import fitnesse.FitNesseContext;
import fitnesse.util.partitioner.ListPartitioner;
import fitnesse.wiki.PathParser;
import fitnesse.wiki.WikiPage;

import java.io.BufferedReader;
Expand Down Expand Up @@ -52,7 +53,7 @@ protected ListPartitioner<WikiPage> getPartitionFunction(List<WikiPage> pages) {
}

protected File getPartitionFile(String paramValue) {
return new File(new File(context.getRootPagePath(), "files"), paramValue);
return new File(new File(context.getRootPagePath(), PathParser.FILES), paramValue);
}

protected String getFilename(List<WikiPage> pages) {
Expand Down
Loading

0 comments on commit 6ae38b8

Please sign in to comment.