]> source.dussan.org Git - gitblit.git/commitdiff
Add support for rendering Markdown commit messages (issue-203)
authorBret K. Ikehara <bret.k.ikehara@gmail.com>
Sun, 4 Aug 2013 23:43:25 +0000 (13:43 -1000)
committerJames Moger <james.moger@gitblit.com>
Wed, 18 Sep 2013 15:29:45 +0000 (11:29 -0400)
16 files changed:
src/main/distrib/data/gitblit.properties
src/main/java/com/gitblit/Constants.java
src/main/java/com/gitblit/GitBlit.java
src/main/java/com/gitblit/SyndicationServlet.java
src/main/java/com/gitblit/models/RepositoryModel.java
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
src/main/java/com/gitblit/wicket/pages/CommitPage.html
src/main/java/com/gitblit/wicket/pages/CommitPage.java
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
src/main/java/com/gitblit/wicket/pages/TagPage.html
src/main/java/com/gitblit/wicket/pages/TagPage.java
src/main/resources/gitblit.css

index 3c0f1d1573e5d22386dcb4aba74613730f259f44..311152ad18a80c55e66c5443bb0c8e78bc20c468 100644 (file)
@@ -878,6 +878,14 @@ web.repositoryRootGroupName = main
 # SINCE 0.8.0\r
 web.repositoryListSwatches = true\r
 \r
+# Defines the default commit message renderer.  This can be configured\r
+# per-repository.\r
+#\r
+# Valid values are: plain, markdown\r
+#\r
+# SINCE 1.4.0\r
+web.commitMessageRenderer = plain\r
+\r
 # Choose the diff presentation style: gitblt, gitweb, or plain\r
 #\r
 # SINCE 0.5.0\r
index 88a1022383cca88aeaa9533ad9622be427748b46..bd04128eddc623982992562ad7fd7f412387750a 100644 (file)
@@ -490,6 +490,19 @@ public class Constants {
                        return this == LOCAL;\r
                }\r
        }\r
+       \r
+       public static enum CommitMessageRenderer {\r
+               PLAIN, MARKDOWN;\r
+               \r
+               public static CommitMessageRenderer fromName(String name) {\r
+                       for (CommitMessageRenderer renderer : values()) {\r
+                               if (renderer.name().equalsIgnoreCase(name)) {\r
+                                       return renderer;\r
+                               }\r
+                       }\r
+                       return CommitMessageRenderer.PLAIN;\r
+               }\r
+       }\r
 \r
        @Documented\r
        @Retention(RetentionPolicy.RUNTIME)\r
index 8c0d62dafd29b4227fd65be5bbf8f2948aeba94a..95da669e119e67fa6549404bad9934b2f63bc4a6 100644 (file)
@@ -32,6 +32,7 @@ import java.net.URISyntaxException;
 import java.nio.charset.Charset;
 import java.security.Principal;
 import java.text.MessageFormat;
+import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -87,6 +88,7 @@ import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AccountType;
 import com.gitblit.Constants.AuthenticationType;
 import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.Constants.CommitMessageRenderer;
 import com.gitblit.Constants.FederationRequest;
 import com.gitblit.Constants.FederationStrategy;
 import com.gitblit.Constants.FederationToken;
@@ -124,6 +126,7 @@ import com.gitblit.utils.HttpUtils;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.JGitUtils.LastChange;
 import com.gitblit.utils.JsonUtils;
+import com.gitblit.utils.MarkdownUtils;
 import com.gitblit.utils.MetricUtils;
 import com.gitblit.utils.ModelUtils;
 import com.gitblit.utils.ObjectCache;
@@ -2014,6 +2017,8 @@ public class GitBlit implements ServletContextListener {
                        model.showReadme = getConfig(config, "showReadme", false);
                        model.skipSizeCalculation = getConfig(config, "skipSizeCalculation", false);
                        model.skipSummaryMetrics = getConfig(config, "skipSummaryMetrics", false);
+                       model.commitMessageRenderer = CommitMessageRenderer.fromName(getConfig(config, "commitMessageRenderer",
+                                       settings.getString(Keys.web.commitMessageRenderer, null)));
                        model.federationStrategy = FederationStrategy.fromName(getConfig(config,
                                        "federationStrategy", null));
                        model.federationSets = new ArrayList<String>(Arrays.asList(config.getStringList(
@@ -2041,7 +2046,7 @@ public class GitBlit implements ServletContextListener {
                                        Constants.CONFIG_GITBLIT, null, "indexBranch")));
                        model.metricAuthorExclusions = new ArrayList<String>(Arrays.asList(config.getStringList(
                                        Constants.CONFIG_GITBLIT, null, "metricAuthorExclusions")));
-                       
+                                       
                        // Custom defined properties
                        model.customFields = new LinkedHashMap<String, String>();
                        for (String aProperty : config.getNames(Constants.CONFIG_GITBLIT, Constants.CONFIG_CUSTOM_FIELDS)) {
@@ -2596,6 +2601,16 @@ public class GitBlit implements ServletContextListener {
                        config.setInt(Constants.CONFIG_GITBLIT, null, "maxActivityCommits", repository.maxActivityCommits);
                }
 
+               CommitMessageRenderer defaultRenderer = CommitMessageRenderer.fromName(settings.getString(Keys.web.commitMessageRenderer, null));
+               if (repository.commitMessageRenderer == null || repository.commitMessageRenderer == defaultRenderer) {
+                       // use default from config
+                       config.unset(Constants.CONFIG_GITBLIT, null, "commitMessageRenderer");
+               } else {
+                       // repository overrides default
+                       config.setString(Constants.CONFIG_GITBLIT, null, "commitMessageRenderer",
+                                       repository.commitMessageRenderer.name());
+               }
+               
                updateList(config, "federationSets", repository.federationSets);
                updateList(config, "preReceiveScript", repository.preReceiveScripts);
                updateList(config, "postReceiveScript", repository.postReceiveScripts);
@@ -2685,12 +2700,56 @@ public class GitBlit implements ServletContextListener {
         * Returns an html version of the commit message with any global or
         * repository-specific regular expression substitution applied.
         * 
+        * This method uses the preferred renderer to transform the commit message.
+        * 
+        * @param repository
+        * @param text
+        * @return html version of the commit message
+        */
+       public String processCommitMessage(RepositoryModel repository, String text) {
+               switch (repository.commitMessageRenderer) {
+               case MARKDOWN:
+                       try {
+                               String prepared = processCommitMessageRegex(repository.name, text);
+                               return MarkdownUtils.transformMarkdown(prepared);
+                       } catch (ParseException e) {
+                               logger.error("Failed to render commit message as markdown", e);
+                       }
+                       break;
+               default:
+                       // noop
+                       break;
+               }
+               
+               return processPlainCommitMessage(repository.name, text);
+       }
+       
+       /**
+        * Returns an html version of the commit message with any global or
+        * repository-specific regular expression substitution applied.
+        * 
+        * This method assumes the commit message is plain text.
+        * 
         * @param repositoryName
         * @param text
         * @return html version of the commit message
         */
-       public String processCommitMessage(String repositoryName, String text) {
-               String html = StringUtils.breakLinesForHtml(text);
+       public String processPlainCommitMessage(String repositoryName, String text) {
+               String html = StringUtils.escapeForHtml(text, false);
+               html = processCommitMessageRegex(repositoryName, html);
+               return StringUtils.breakLinesForHtml(html);
+               
+       }
+       
+       /**
+        * Apply globally or per-repository specified regex substitutions to the
+        * commit message.
+        * 
+        * @param repositoryName
+        * @param text
+        * @return the processed commit message
+        */
+       protected String processCommitMessageRegex(String repositoryName, String text) {
                Map<String, String> map = new HashMap<String, String>();
                // global regex keys
                if (settings.getBoolean(Keys.regex.global, false)) {
@@ -2714,14 +2773,14 @@ public class GitBlit implements ServletContextListener {
                        String definition = entry.getValue().trim();
                        String[] chunks = definition.split("!!!");
                        if (chunks.length == 2) {
-                               html = html.replaceAll(chunks[0], chunks[1]);
+                               text = text.replaceAll(chunks[0], chunks[1]);
                        } else {
                                logger.warn(entry.getKey()
                                                + " improperly formatted.  Use !!! to separate match from replacement: "
                                                + definition);
                        }
                }
-               return html;
+               return text;
        }
 
        /**
index baaf7eb7c52d1154ab48732deddd523ee233b6d6..bdb3b57186c86d69b05d898592f8c5cda3a18f5f 100644 (file)
@@ -244,7 +244,7 @@ public class SyndicationServlet extends HttpServlet {
                                                StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());\r
                                entry.published = commit.getCommitterIdent().getWhen();\r
                                entry.contentType = "text/html";\r
-                               String message = GitBlit.self().processCommitMessage(model.name,\r
+                               String message = GitBlit.self().processCommitMessage(model,\r
                                                commit.getFullMessage());\r
                                entry.content = message;\r
                                entry.repository = model.name;\r
index e28c9df3833e37d267578fa4809ec5f08bafa338..f0354b9af93aed7cba718f16ef505c0f9a42be14 100644 (file)
@@ -26,6 +26,7 @@ import java.util.TreeSet;
 \r
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.Constants.AuthorizationControl;\r
+import com.gitblit.Constants.CommitMessageRenderer;\r
 import com.gitblit.Constants.FederationStrategy;\r
 import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.ModelUtils;\r
@@ -85,6 +86,7 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
        public int gcPeriod;\r
        public int maxActivityCommits;  \r
        public List<String> metricAuthorExclusions;\r
+       public CommitMessageRenderer commitMessageRenderer;\r
        \r
        public transient boolean isCollectingGarbage;\r
        public Date lastGC;\r
index 55d898754e4dc5d805369fe29e124d4afc321634..1f1a58402de91434b58542a0dd9d0869cd4240f3 100644 (file)
@@ -501,4 +501,5 @@ gb.reviewPatchset = review {0} patchset {1}
 gb.todaysActivityStats = today / {1} commits by {2} authors
 gb.todaysActivityNone = today / none
 gb.noActivityToday = there has been no activity today
-gb.anonymousUser= anonymous
\ No newline at end of file
+gb.anonymousUser= anonymous
+gb.commitMessageRenderer = commit message renderer
\ No newline at end of file
index 11a0ce3e1afa76486827fc30d43b4d62fb69b26b..7437d2ed0d72a4f251f8ee06419a493c675cc2f9 100644 (file)
@@ -16,7 +16,7 @@
        <div wicket:id="commitHeader">[commit header]</div>\r
 \r
        <!-- full message -->\r
-       <pre style="border-style:none" "class="commit_message" wicket:id="fullMessage">[commit message]</pre>\r
+       <div wicket:id="fullMessage">[commit message]</div>\r
 \r
        <!-- commit legend -->\r
        <div class="hidden-phone" style="text-align:right;" wicket:id="commitLegend"></div>\r
index 6f1b459801777bd5996969c89467a7e2bc13cf72..0a41b67b9b4b5766f2ffb79275bfc256d86d2a40 100644 (file)
@@ -79,7 +79,7 @@ public class CommitDiffPage extends RepositoryPage {
 
                add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
 
-               addFullText("fullMessage", commit.getFullMessage(), true);
+               addFullText("fullMessage", commit.getFullMessage());
 
                // changed paths list
                List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, commit);
index ab5ddcaa94a4614241a10079697e54b139bcd9b0..1ec541c6f02ff155a5378ce07f720badc00db58c 100644 (file)
@@ -49,7 +49,7 @@
        </div>\r
        \r
        <!-- full message -->\r
-       <pre class="commit_message" wicket:id="fullMessage">[commit message]</pre>\r
+       <div class="topborder" wicket:id="fullMessage">[commit message]</div>\r
 \r
        <!--  git notes -->\r
        <table class="gitnotes">\r
index 1d11d4433b05b455edfe884c3c1f340ccdcc5e48..1a1d5987ddd9d32e966c3fbf866501d4a9608c5f 100644 (file)
@@ -117,7 +117,7 @@ public class CommitPage extends RepositoryPage {
                };\r
                add(parentsView);\r
 \r
-               addFullText("fullMessage", c.getFullMessage(), true);\r
+               addFullText("fullMessage", c.getFullMessage());\r
 \r
                // git notes\r
                List<GitNote> notes = JGitUtils.getNotesOnCommit(r, c);\r
@@ -133,8 +133,8 @@ public class CommitPage extends RepositoryPage {
                                item.add(new GravatarImage("noteAuthorAvatar", entry.notesRef.getAuthorIdent()));\r
                                item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef\r
                                                .getAuthorIdent().getWhen(), getTimeZone(), getTimeUtils()));\r
-                               item.add(new Label("noteContent", GitBlit.self().processCommitMessage(\r
-                                               repositoryName, entry.content)).setEscapeModelStrings(false));\r
+                               item.add(new Label("noteContent", GitBlit.self().processPlainCommitMessage(repositoryName,\r
+                                               entry.content)).setEscapeModelStrings(false));\r
                        }\r
                };\r
                add(notesView.setVisible(notes.size() > 0));\r
index 38070273afc1c85a01e94c6eed7005cbfe3e1a53..f7d1f7331e3e0142aff66bff777d173f38e5afa8 100644 (file)
@@ -42,8 +42,9 @@
                                <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="12" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></label></td></tr>\r
                                <tr><th><wicket:message key="gb.maxActivityCommits"></wicket:message></th><td class="edit"><select class="span2" wicket:id="maxActivityCommits" tabindex="13" /> &nbsp;<span class="help-inline"><wicket:message key="gb.maxActivityCommitsDescription"></wicket:message></span></td></tr>\r
                                <tr><th><wicket:message key="gb.metricAuthorExclusions"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="metricAuthorExclusions" size="40" tabindex="14" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.commitMessageRenderer"></wicket:message></th><td class="edit"><select class="span2" wicket:id="commitMessageRenderer" tabindex="15" /></td></tr>\r
                                <tr><th colspan="2"><hr/></th></tr>\r
-                               <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="15" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="16" /></td></tr>\r
                        </tbody>\r
                </table>\r
                </div>\r
                <div class="tab-pane" id="permissions">\r
                        <table class="plain">\r
                                <tbody class="settings">\r
-                                       <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="16" /> </td></tr>\r
+                                       <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="17" /> </td></tr>\r
                                        <tr><th colspan="2"><hr/></th></tr>\r
-                                       <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="17" /></td></tr>\r
+                                       <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="18" /></td></tr>\r
                                        <tr><th colspan="2"><hr/></th></tr>\r
                                        <tr><th><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;"><span class="authorizationControl" wicket:id="authorizationControl"></span></td></tr>\r
                                        <tr><th colspan="2"><hr/></th></tr>\r
-                                       <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="18" /> &nbsp;<span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr>\r
-                                       <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="19" /> &nbsp;<span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr>\r
-                                       <tr><th><wicket:message key="gb.verifyCommitter"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="verifyCommitter" tabindex="20" /> &nbsp;<span class="help-inline"><wicket:message key="gb.verifyCommitterDescription"></wicket:message></span><br/><span class="help-inline" style="padding-left:10px;"><wicket:message key="gb.verifyCommitterNote"></wicket:message></span></label></td></tr>\r
+                                       <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="19" /> &nbsp;<span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr>\r
+                                       <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="20" /> &nbsp;<span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr>\r
+                                       <tr><th><wicket:message key="gb.verifyCommitter"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="verifyCommitter" tabindex="21" /> &nbsp;<span class="help-inline"><wicket:message key="gb.verifyCommitterDescription"></wicket:message></span><br/><span class="help-inline" style="padding-left:10px;"><wicket:message key="gb.verifyCommitterNote"></wicket:message></span></label></td></tr>\r
                                        <tr><th colspan="2"><hr/></th></tr>\r
                                        <tr><th><wicket:message key="gb.userPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>\r
                                        <tr><th colspan="2"><hr/></th></tr>\r
@@ -73,7 +74,7 @@
                <div class="tab-pane" id="federation">\r
                        <table class="plain">\r
                                <tbody class="settings">\r
-                                       <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="21" /></td></tr>\r
+                                       <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="22" /></td></tr>\r
                                        <tr><th><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>\r
                                </tbody>\r
                        </table>\r
index 4c471a1e79265f4e55b2ff892f9730fa9d9dbfcf..a25797ff592f58623716a9fc47990b2bbc23dce0 100644 (file)
@@ -52,6 +52,7 @@ import org.apache.wicket.model.util.ListModel;
 import com.gitblit.Constants;\r
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.Constants.AuthorizationControl;\r
+import com.gitblit.Constants.CommitMessageRenderer;\r
 import com.gitblit.Constants.FederationStrategy;\r
 import com.gitblit.Constants.RegistrantType;\r
 import com.gitblit.GitBlit;\r
@@ -552,6 +553,10 @@ public class EditRepositoryPage extends RootSubPage {
                        }\r
                });\r
                \r
+               List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values());\r
+               DropDownChoice<CommitMessageRenderer> messageRendererChoice = new DropDownChoice<CommitMessageRenderer>("commitMessageRenderer", renderers);\r
+               form.add(messageRendererChoice);\r
+               \r
                form.add(new Button("save"));\r
                Button cancel = new Button("cancel") {\r
                        private static final long serialVersionUID = 1L;\r
@@ -721,5 +726,4 @@ public class EditRepositoryPage extends RootSubPage {
                        return Integer.toString(index);\r
                }\r
        }\r
-       \r
 }\r
index afbed86fd68780425ef5f60d02d324602ec09f4f..e5ce22a1e3063b383b71fa48a45b110a155d6bc3 100644 (file)
@@ -482,12 +482,17 @@ public abstract class RepositoryPage extends RootPage {
                add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));\r
        }\r
 \r
-       protected void addFullText(String wicketId, String text, boolean substituteRegex) {\r
-               String html = StringUtils.escapeForHtml(text, false);\r
-               if (substituteRegex) {\r
-                       html = GitBlit.self().processCommitMessage(repositoryName, html);\r
-               } else {\r
-                       html = StringUtils.breakLinesForHtml(html);\r
+       protected void addFullText(String wicketId, String text) {\r
+               RepositoryModel model = getRepositoryModel();\r
+               String content = GitBlit.self().processCommitMessage(model, text);\r
+               String html;\r
+               switch (model.commitMessageRenderer) {\r
+               case MARKDOWN:\r
+                       html = MessageFormat.format("<div class='commit_message'>{0}</div>", content);\r
+                       break;\r
+               default:\r
+                       html = MessageFormat.format("<pre class='commit_message'>{0}</pre>", content);\r
+                       break;\r
                }\r
                add(new Label(wicketId, html).setEscapeModelStrings(false));\r
        }\r
index e719a392163e50c2671a04cd4b97f1b68246570e..492ece3a9aa3303b57b59c2f83ecefb2209401f6 100644 (file)
@@ -22,7 +22,7 @@
        </div>\r
        \r
        <!--  full message -->\r
-       <pre class="commit_message" wicket:id="fullMessage">[commit message]</pre>\r
+       <div class="topborder" wicket:id="fullMessage">[commit message]</div>\r
        \r
        <wicket:fragment wicket:id="fullPersonIdent">\r
                <span wicket:id="personName"></span><span wicket:id="personAddress"></span>\r
index 13c10111c13fd41a3c511817922032dae8395278..b8b6eef0740241d78a98dd52b5d5725ff0664313 100644 (file)
@@ -92,7 +92,7 @@ public class TagPage extends RepositoryPage {
                }\r
                add(WicketUtils.createTimestampLabel("tagDate", when, getTimeZone(), getTimeUtils()));\r
 \r
-               addFullText("fullMessage", tagRef.getFullMessage(), true);\r
+               addFullText("fullMessage", tagRef.getFullMessage());\r
        }\r
 \r
        @Override\r
index d8745bf4d2ad03aed28888316d9d30b772fcff70..eef0d0b519bedf9e51405d66672ecec8f1e1fa00 100644 (file)
@@ -891,6 +891,9 @@ span.login input:focus {
 \r
 .commit_message {\r
        padding: 8px;\r
+}\r
+\r
+.topborder {\r
        border: solid #ddd;\r
        border-width: 1px 0px 0px;\r
        border-radius: 0px;\r