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.

MarkdownUtils.java 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright 2011 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.utils;
  17. import static org.pegdown.Extensions.ALL;
  18. import static org.pegdown.Extensions.ANCHORLINKS;
  19. import static org.pegdown.Extensions.HARDWRAPS;
  20. import static org.pegdown.Extensions.SMARTYPANTS;
  21. import java.io.IOException;
  22. import java.io.Reader;
  23. import java.io.StringWriter;
  24. import java.text.MessageFormat;
  25. import org.apache.commons.io.IOUtils;
  26. import org.pegdown.LinkRenderer;
  27. import org.pegdown.ParsingTimeoutException;
  28. import org.pegdown.PegDownProcessor;
  29. import org.pegdown.ast.RootNode;
  30. import com.gitblit.IStoredSettings;
  31. import com.gitblit.Keys;
  32. import com.gitblit.wicket.MarkupProcessor.WorkaroundHtmlSerializer;
  33. /**
  34. * Utility methods for transforming raw markdown text to html.
  35. *
  36. * @author James Moger
  37. *
  38. */
  39. public class MarkdownUtils {
  40. /**
  41. * Returns the html version of the plain source text.
  42. *
  43. * @param text
  44. * @return html version of plain text
  45. * @throws java.text.ParseException
  46. */
  47. public static String transformPlainText(String text) {
  48. // url auto-linking
  49. text = text.replaceAll("((http|https)://[0-9A-Za-z-_=\\?\\.\\$#&/]*)", "<a href=\"$1\">$1</a>");
  50. String html = "<pre>" + text + "</pre>";
  51. return html;
  52. }
  53. /**
  54. * Returns the html version of the markdown source text.
  55. *
  56. * @param markdown
  57. * @return html version of markdown text
  58. * @throws java.text.ParseException
  59. */
  60. public static String transformMarkdown(String markdown) {
  61. return transformMarkdown(markdown, null);
  62. }
  63. /**
  64. * Returns the html version of the markdown source text.
  65. *
  66. * @param markdown
  67. * @return html version of markdown text
  68. * @throws java.text.ParseException
  69. */
  70. public static String transformMarkdown(String markdown, LinkRenderer linkRenderer) {
  71. try {
  72. PegDownProcessor pd = new PegDownProcessor(ALL & ~SMARTYPANTS & ~ANCHORLINKS & ~HARDWRAPS);
  73. RootNode astRoot = pd.parseMarkdown(markdown.toCharArray());
  74. return new WorkaroundHtmlSerializer(linkRenderer == null ? new LinkRenderer() : linkRenderer).toHtml(astRoot);
  75. } catch (ParsingTimeoutException e) {
  76. return null;
  77. }
  78. }
  79. /**
  80. * Returns the html version of the markdown source reader. The reader is
  81. * closed regardless of success or failure.
  82. *
  83. * @param markdownReader
  84. * @return html version of the markdown text
  85. * @throws java.text.ParseException
  86. */
  87. public static String transformMarkdown(Reader markdownReader) throws IOException {
  88. // Read raw markdown content and transform it to html
  89. StringWriter writer = new StringWriter();
  90. try {
  91. IOUtils.copy(markdownReader, writer);
  92. String markdown = writer.toString();
  93. return transformMarkdown(markdown);
  94. } finally {
  95. try {
  96. writer.close();
  97. } catch (IOException e) {
  98. // IGNORE
  99. }
  100. }
  101. }
  102. /**
  103. * Transforms GFM (Github Flavored Markdown) to html.
  104. * Gitblit does not support the complete GFM specification.
  105. *
  106. * @param input
  107. * @param repositoryName
  108. * @return html
  109. */
  110. public static String transformGFM(IStoredSettings settings, String input, String repositoryName) {
  111. String text = input;
  112. // strikethrough
  113. text = text.replaceAll("~~(.*)~~", "<s>$1</s>");
  114. text = text.replaceAll("\\{(?:-){2}(.*)(?:-){2}}", "<s>$1</s>");
  115. // underline
  116. text = text.replaceAll("\\{(?:\\+){2}(.*)(?:\\+){2}}", "<u>$1</u>");
  117. // strikethrough, replacement
  118. text = text.replaceAll("\\{~~(.*)~>(.*)~~}", "<s>$1</s><u>$2</u>");
  119. // highlight
  120. text = text.replaceAll("\\{==(.*)==}", "<span class='highlight'>$1</span>");
  121. String canonicalUrl = settings.getString(Keys.web.canonicalUrl, "https://localhost:8443");
  122. // emphasize and link mentions
  123. String mentionReplacement = String.format(" **[@$1](%1s/user/$1)**", canonicalUrl);
  124. text = text.replaceAll("\\s@([A-Za-z0-9-_]+)", mentionReplacement);
  125. // link ticket refs
  126. String ticketReplacement = MessageFormat.format("$1[#$2]({0}/tickets?r={1}&h=$2)$3", canonicalUrl, repositoryName);
  127. text = text.replaceAll("([\\s,]+)#(\\d+)([\\s,:\\.\\n])", ticketReplacement);
  128. // link commit shas
  129. int shaLen = settings.getInteger(Keys.web.shortCommitIdLength, 6);
  130. String commitPattern = MessageFormat.format("\\s([A-Fa-f0-9]'{'{0}'}')([A-Fa-f0-9]'{'{1}'}')", shaLen, 40 - shaLen);
  131. String commitReplacement = String.format(" [`$1`](%1$s/commit?r=%2$s&h=$1$2)", canonicalUrl, repositoryName);
  132. text = text.replaceAll(commitPattern, commitReplacement);
  133. String html = transformMarkdown(text);
  134. return html;
  135. }
  136. }