Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic Table Of Contents Generator #1281

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/main/java/com/gitblit/markdown/ModifyModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.gitblit.markdown;

/**
* Created by Yuriy Aizenberg
*/
public class ModifyModel {

private static final String PATTERN = "%s- [%s](#%s)";
private static final String AFFIX = "   ";

private int currentDeepLevel = 1;
private String headerName;
private String headerLink;


public ModifyModel(int currentDeepLevel, String headerName, String headerLink) {
this.currentDeepLevel = currentDeepLevel;
this.headerName = headerName;
this.headerLink = headerLink;
}

public int getCurrentDeepLevel() {
return currentDeepLevel;
}

public void setCurrentDeepLevel(int currentDeepLevel) {
this.currentDeepLevel = currentDeepLevel;
}

public String getHeaderName() {
return headerName;
}

public void setHeaderName(String headerName) {
this.headerName = headerName;
}

public String getHeaderLink() {
return headerLink;
}

public void setHeaderLink(String headerLink) {
this.headerLink = headerLink;
}

public String create() {
String affixs = "";
if (currentDeepLevel > 1) {
for (int i = 0; i < currentDeepLevel - 1; i++) {
affixs += AFFIX;
}
}
return String.format(PATTERN, affixs, headerName, headerLink);
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/gitblit/markdown/ParameterUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.gitblit.markdown;

/**
* Created by Yuriy Aizenberg
*/
public class ParameterUtils {

public static Integer extractInteger(String key, Integer defValue) {
if (Utils.isEmpty(key)) return defValue;
try {
return Integer.valueOf(key);
} catch (NumberFormatException e) {
return defValue;
}
}

public static Integer extractInteger(String key) {
return extractInteger(key, null);
}

public static Boolean extractBoolean(String key, Boolean defValue) {
if (Utils.isEmpty(key)) return defValue;
return Boolean.valueOf(key);
}

public static Boolean extractBoolean(String key) {
return extractBoolean(key, null);
}


}
97 changes: 97 additions & 0 deletions src/main/java/com/gitblit/markdown/TableOfContentsGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.gitblit.markdown;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TableOfContentsGenerator {

public static final String DEFAULT_HEADER = "#";
public static final char DEFAULT_HEADER_CHAR = '#';
public static final String ALT_1_HEADER = "=";
public static final String ALT_2_HEADER = "-";

public static final String TOC_HEADER = "## Table of contents";

private String markdown;
private List<ModifyModel> rootModels = new ArrayList<ModifyModel>();

public TableOfContentsGenerator(String markdown) {
this.markdown = markdown;
}

public String start() {

if (!this.markdown.contains("__TOC__")) {
return this.markdown;
}

int patternLineNumber = -1;
int currentLine = 0;
String[] items = markdown.split("\n");
List<String> fileContent = new ArrayList<String>(Arrays.asList(items));

String previousLine = null;

for (String line : fileContent) {
++currentLine;
if (line.startsWith(DEFAULT_HEADER)) {
String trim = line.trim();

int count = getCount(trim);
if (count < 1 || count > 6) {
previousLine = line;
continue;
}
String headerName = line.substring(count);
rootModels.add(new ModifyModel(count, headerName, Utils.normalize(headerName)));
} else if (line.startsWith(ALT_1_HEADER) && !Utils.isEmpty(previousLine)) {
if (line.replaceAll(ALT_1_HEADER, "").isEmpty()) {
rootModels.add(new ModifyModel(1, previousLine, Utils.normalize(previousLine)));
}
} else if (line.startsWith(ALT_2_HEADER) && !Utils.isEmpty(previousLine)) {
if (line.replaceAll(ALT_2_HEADER, "").isEmpty()) {
rootModels.add(new ModifyModel(2, previousLine, Utils.normalize(previousLine)));
}
} else if (line.trim().equals("__TOC__")) {
patternLineNumber = currentLine;
}
previousLine = line;
}

return getMarkdown(fileContent, patternLineNumber).replaceAll("__TOC__", "");

}

private String getMarkdown(List<String> fileContent, int patternLineNumber) {
StringBuilder writer = new StringBuilder();
if (patternLineNumber == -1) {
System.out.println("Pattern for replace not found!");
return "";
}
fileContent.add(patternLineNumber - 1, TOC_HEADER);
for (ModifyModel modifyModel : rootModels) {
fileContent.add(patternLineNumber, modifyModel.create());
patternLineNumber++;
}

for (String line : fileContent) {
writer.append(line).append("\n");
}

return writer.toString();
}

private int getCount(String string) {
int count = 0;
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) == DEFAULT_HEADER_CHAR) {
++count;
} else {
break;
}
}
return count;
}

}
63 changes: 63 additions & 0 deletions src/main/java/com/gitblit/markdown/Utils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.gitblit.markdown;

import java.io.*;

/**
* Created by Yuriy Aizenberg
*/
public class Utils {

private static final String SPACES = " ";
private static final String CODES = "%([abcdef]|\\d){2,2}";
private static final String SPECIAL_CHARS = "[\\/?!:\\[\\]`.,()*\"';{}+=<>~\\$|#]";
private static final String DASH = "-";
private static final String EMPTY = "";


public static boolean checkSourceFile(String fileName) {
if (isEmpty(fileName)) return false;
File sourceFile = new File(fileName);
return sourceFile.exists() && sourceFile.canRead() && sourceFile.isFile();
}

public static boolean isEmpty(String string) {
return string == null || string.isEmpty();
}

public static int getFileLines(String filePath, int defFaultValue) {
LineNumberReader lineNumberReader = null;
FileReader fileReader = null;
try {
fileReader = new FileReader(filePath);
lineNumberReader = new LineNumberReader(fileReader);
lineNumberReader.skip(Long.MAX_VALUE);
return lineNumberReader.getLineNumber() + 1;
} catch (IOException ignored) {
} finally {
closeStream(lineNumberReader, fileReader);
}
return defFaultValue;
}

public static void closeStream(Closeable... closeable) {
for (Closeable c : closeable) {
if (c != null) {
try {
c.close();
} catch (IOException ignored) {
}
}
}
}

public static String normalize(final String taintedURL) {
return taintedURL
.trim()

.replaceAll(SPACES, DASH)

.replaceAll(CODES, EMPTY)

.replaceAll(SPECIAL_CHARS, EMPTY).toLowerCase();
}
}
1 change: 1 addition & 0 deletions src/main/java/com/gitblit/utils/JSoupXssFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ protected Whitelist getRelaxedWhiteList() {
.addAttributes("img", "align", "alt", "height", "src", "title", "width")
.addAttributes("ol", "start", "type")
.addAttributes("q", "cite")
.addAttributes(":all", "id")
.addAttributes("span", "class", "style")
.addAttributes("table", "class", "style", "summary", "width")
.addAttributes("td", "abbr", "axis", "class", "colspan", "rowspan", "style", "width")
Expand Down
29 changes: 28 additions & 1 deletion src/main/java/com/gitblit/wicket/MarkupProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
Expand Down Expand Up @@ -55,6 +57,8 @@

import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.markdown.TableOfContentsGenerator;
import com.gitblit.markdown.Utils;
import com.gitblit.models.PathModel;
import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.JGitUtils;
Expand Down Expand Up @@ -358,11 +362,34 @@ public Rendering render(WikiLinkNode node) {
}
};

final String content = MarkdownUtils.transformMarkdown(doc.markup, renderer);
String markdown = new TableOfContentsGenerator(doc.markup).start();
String content = MarkdownUtils.transformMarkdown(markdown, renderer);

content = generateHeaderIds(content);

final String safeContent = xssFilter.relaxed(content);

doc.html = safeContent;
}

private String generateHeaderIds(String content) {
Pattern insideHeader = Pattern.compile("<h\\d>(.*?)<\\/h\\d>");
Matcher m = insideHeader.matcher(content);

while (m.find()) {
String id = Utils.normalize(m.group().replaceAll("<h\\d>", "").replaceAll("<\\/h\\d>", ""));
Pattern iniTag = Pattern.compile("<h\\d");
Matcher lookTag = iniTag.matcher(m.group());
String tag = "";
if (lookTag.find()) {
tag = lookTag.group();
}
String finalTag = m.group().replace(tag, tag + " id=\"" + id + "\"");

content = content.replace(m.group(), finalTag);
}
return content;
}

private String getWicketUrl(Class<? extends Page> pageClass, final String repositoryName, final String commitId, final String document) {
String fsc = settings.getString(Keys.web.forwardSlashCharacter, "/");
Expand Down