You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MarkupProcessor.java 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * Copyright 2013 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.wicket;
  17. import java.io.Serializable;
  18. import java.io.UnsupportedEncodingException;
  19. import java.net.URLEncoder;
  20. import java.text.MessageFormat;
  21. import java.util.ArrayList;
  22. import java.util.List;
  23. import org.apache.wicket.Page;
  24. import org.apache.wicket.RequestCycle;
  25. import org.eclipse.jgit.lib.Repository;
  26. import org.eclipse.jgit.revwalk.RevCommit;
  27. import org.pegdown.LinkRenderer;
  28. import org.pegdown.ast.WikiLinkNode;
  29. import org.slf4j.Logger;
  30. import org.slf4j.LoggerFactory;
  31. import com.gitblit.IStoredSettings;
  32. import com.gitblit.Keys;
  33. import com.gitblit.models.PathModel;
  34. import com.gitblit.utils.JGitUtils;
  35. import com.gitblit.utils.MarkdownUtils;
  36. import com.gitblit.utils.StringUtils;
  37. import com.gitblit.wicket.pages.DocPage;
  38. /**
  39. * Processes markup content and generates html with repository-relative page and
  40. * image linking.
  41. *
  42. * @author James Moger
  43. *
  44. */
  45. public class MarkupProcessor {
  46. public enum MarkupSyntax {
  47. PLAIN, MARKDOWN
  48. }
  49. private Logger logger = LoggerFactory.getLogger(getClass());
  50. private final IStoredSettings settings;
  51. public MarkupProcessor(IStoredSettings settings) {
  52. this.settings = settings;
  53. }
  54. public List<String> getMarkupExtensions() {
  55. List<String> list = new ArrayList<String>();
  56. list.addAll(settings.getStrings(Keys.web.markdownExtensions));
  57. return list;
  58. }
  59. private MarkupSyntax determineSyntax(String documentPath) {
  60. String ext = StringUtils.getFileExtension(documentPath).toLowerCase();
  61. if (StringUtils.isEmpty(ext)) {
  62. return MarkupSyntax.PLAIN;
  63. }
  64. if (settings.getStrings(Keys.web.markdownExtensions).contains(ext)) {
  65. return MarkupSyntax.MARKDOWN;
  66. }
  67. return MarkupSyntax.PLAIN;
  68. }
  69. public MarkupDocument parseReadme(Repository r, String repositoryName, String commitId) {
  70. String readme = null;
  71. RevCommit commit = JGitUtils.getCommit(r, commitId);
  72. List<PathModel> paths = JGitUtils.getFilesInPath(r, null, commit);
  73. for (PathModel path : paths) {
  74. if (!path.isTree()) {
  75. String name = path.name.toLowerCase();
  76. if (name.equals("readme") || name.equals("readme.txt")) {
  77. readme = path.name;
  78. break;
  79. } else if (name.startsWith("readme.")) {
  80. String ext = StringUtils.getFileExtension(name).toLowerCase();
  81. if (getMarkupExtensions().contains(ext)) {
  82. readme = path.name;
  83. break;
  84. }
  85. }
  86. }
  87. }
  88. if (!StringUtils.isEmpty(readme)) {
  89. String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);
  90. String markup = JGitUtils.getStringContent(r, commit.getTree(), readme, encodings);
  91. return parse(repositoryName, commitId, readme, markup);
  92. }
  93. return null;
  94. }
  95. public MarkupDocument parse(String repositoryName, String commitId, String documentPath, String markupText) {
  96. final MarkupSyntax syntax = determineSyntax(documentPath);
  97. final MarkupDocument doc = new MarkupDocument(documentPath, markupText, syntax);
  98. if (markupText != null) {
  99. try {
  100. switch (syntax){
  101. case MARKDOWN:
  102. parse(doc, repositoryName, commitId);
  103. break;
  104. default:
  105. doc.html = MarkdownUtils.transformPlainText(markupText);
  106. break;
  107. }
  108. } catch (Exception e) {
  109. logger.error("failed to transform " + syntax, e);
  110. }
  111. }
  112. if (doc.html == null) {
  113. // failed to transform markup
  114. if (markupText == null) {
  115. markupText = String.format("Document <b>%1$s</b> not found in <em>%2$s</em>", documentPath, repositoryName);
  116. }
  117. markupText = MessageFormat.format("<div class=\"alert alert-error\"><strong>{0}:</strong> {1}</div>{2}", "Error", "failed to parse markup", markupText);
  118. doc.html = StringUtils.breakLinesForHtml(markupText);
  119. }
  120. return doc;
  121. }
  122. /**
  123. * Parses the document as Markdown using Pegdown.
  124. *
  125. * @param doc
  126. * @param repositoryName
  127. * @param commitId
  128. */
  129. private void parse(final MarkupDocument doc, final String repositoryName, final String commitId) {
  130. LinkRenderer renderer = new LinkRenderer() {
  131. @Override
  132. public Rendering render(WikiLinkNode node) {
  133. String path = doc.getRelativePath(node.getText());
  134. String name = getDocumentName(path);
  135. String url = getWicketUrl(DocPage.class, repositoryName, commitId, path);
  136. return new Rendering(url, name);
  137. }
  138. };
  139. doc.html = MarkdownUtils.transformMarkdown(doc.markup, renderer);
  140. }
  141. private String getWicketUrl(Class<? extends Page> pageClass, final String repositoryName, final String commitId, final String document) {
  142. String fsc = settings.getString(Keys.web.forwardSlashCharacter, "/");
  143. String encodedPath = document.replace(' ', '-');
  144. try {
  145. encodedPath = URLEncoder.encode(encodedPath, "UTF-8");
  146. } catch (UnsupportedEncodingException e) {
  147. logger.error(null, e);
  148. }
  149. encodedPath = encodedPath.replace("/", fsc).replace("%2F", fsc);
  150. String url = RequestCycle.get().urlFor(pageClass, WicketUtils.newPathParameter(repositoryName, commitId, encodedPath)).toString();
  151. return url;
  152. }
  153. private String getDocumentName(final String document) {
  154. // extract document name
  155. String name = StringUtils.stripFileExtension(document);
  156. name = name.replace('_', ' ');
  157. if (name.indexOf('/') > -1) {
  158. name = name.substring(name.lastIndexOf('/') + 1);
  159. }
  160. return name;
  161. }
  162. public static class MarkupDocument implements Serializable {
  163. private static final long serialVersionUID = 1L;
  164. public final String documentPath;
  165. public final String markup;
  166. public final MarkupSyntax syntax;
  167. public String html;
  168. MarkupDocument(String documentPath, String markup, MarkupSyntax syntax) {
  169. this.documentPath = documentPath;
  170. this.markup = markup;
  171. this.syntax = syntax;
  172. }
  173. String getCurrentPath() {
  174. String basePath = "";
  175. if (documentPath.indexOf('/') > -1) {
  176. basePath = documentPath.substring(0, documentPath.lastIndexOf('/') + 1);
  177. if (basePath.charAt(0) == '/') {
  178. return basePath.substring(1);
  179. }
  180. }
  181. return basePath;
  182. }
  183. String getRelativePath(String ref) {
  184. return ref.charAt(0) == '/' ? ref.substring(1) : (getCurrentPath() + ref);
  185. }
  186. }
  187. }