Bret K. Ikehara
2013-08-04 cd9461c0c05958444d1313b794be57df39d98956
Add support for rendering Markdown commit messages (issue-203)
16 files modified
154 ■■■■ changed files
src/main/distrib/data/gitblit.properties 8 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/Constants.java 13 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/GitBlit.java 69 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/SyndicationServlet.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/models/RepositoryModel.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/CommitPage.html 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/CommitPage.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html 15 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java 17 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TagPage.html 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TagPage.java 2 ●●● patch | view | raw | blame | history
src/main/resources/gitblit.css 3 ●●●●● patch | view | raw | blame | history
src/main/distrib/data/gitblit.properties
@@ -878,6 +878,14 @@
# SINCE 0.8.0
web.repositoryListSwatches = true
# Defines the default commit message renderer.  This can be configured
# per-repository.
#
# Valid values are: plain, markdown
#
# SINCE 1.4.0
web.commitMessageRenderer = plain
# Choose the diff presentation style: gitblt, gitweb, or plain
#
# SINCE 0.5.0
src/main/java/com/gitblit/Constants.java
@@ -490,6 +490,19 @@
            return this == LOCAL;
        }
    }
    public static enum CommitMessageRenderer {
        PLAIN, MARKDOWN;
        public static CommitMessageRenderer fromName(String name) {
            for (CommitMessageRenderer renderer : values()) {
                if (renderer.name().equalsIgnoreCase(name)) {
                    return renderer;
                }
            }
            return CommitMessageRenderer.PLAIN;
        }
    }
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
src/main/java/com/gitblit/GitBlit.java
@@ -32,6 +32,7 @@
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.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.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 @@
            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 @@
                    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 @@
            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 @@
     * 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 @@
            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;
    }
    /**
src/main/java/com/gitblit/SyndicationServlet.java
@@ -244,7 +244,7 @@
                        StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());
                entry.published = commit.getCommitterIdent().getWhen();
                entry.contentType = "text/html";
                String message = GitBlit.self().processCommitMessage(model.name,
                String message = GitBlit.self().processCommitMessage(model,
                        commit.getFullMessage());
                entry.content = message;
                entry.repository = model.name;
src/main/java/com/gitblit/models/RepositoryModel.java
@@ -26,6 +26,7 @@
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.CommitMessageRenderer;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.ModelUtils;
@@ -85,6 +86,7 @@
    public int gcPeriod;
    public int maxActivityCommits;    
    public List<String> metricAuthorExclusions;
    public CommitMessageRenderer commitMessageRenderer;
    
    public transient boolean isCollectingGarbage;
    public Date lastGC;
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -501,4 +501,5 @@
gb.todaysActivityStats = today / {1} commits by {2} authors
gb.todaysActivityNone = today / none
gb.noActivityToday = there has been no activity today
gb.anonymousUser= anonymous
gb.anonymousUser= anonymous
gb.commitMessageRenderer = commit message renderer
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html
@@ -16,7 +16,7 @@
    <div wicket:id="commitHeader">[commit header]</div>
    <!-- full message -->
    <pre style="border-style:none" "class="commit_message" wicket:id="fullMessage">[commit message]</pre>
    <div wicket:id="fullMessage">[commit message]</div>
    <!-- commit legend -->
    <div class="hidden-phone" style="text-align:right;" wicket:id="commitLegend"></div>
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -79,7 +79,7 @@
        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);
src/main/java/com/gitblit/wicket/pages/CommitPage.html
@@ -49,7 +49,7 @@
    </div>
    
    <!-- full message -->
    <pre class="commit_message" wicket:id="fullMessage">[commit message]</pre>
    <div class="topborder" wicket:id="fullMessage">[commit message]</div>
    <!--  git notes -->
    <table class="gitnotes">
src/main/java/com/gitblit/wicket/pages/CommitPage.java
@@ -117,7 +117,7 @@
        };
        add(parentsView);
        addFullText("fullMessage", c.getFullMessage(), true);
        addFullText("fullMessage", c.getFullMessage());
        // git notes
        List<GitNote> notes = JGitUtils.getNotesOnCommit(r, c);
@@ -133,8 +133,8 @@
                item.add(new GravatarImage("noteAuthorAvatar", entry.notesRef.getAuthorIdent()));
                item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef
                        .getAuthorIdent().getWhen(), getTimeZone(), getTimeUtils()));
                item.add(new Label("noteContent", GitBlit.self().processCommitMessage(
                        repositoryName, entry.content)).setEscapeModelStrings(false));
                item.add(new Label("noteContent", GitBlit.self().processPlainCommitMessage(repositoryName,
                        entry.content)).setEscapeModelStrings(false));
            }
        };
        add(notesView.setVisible(notes.size() > 0));
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -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>
                <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>
                <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>
                <tr><th><wicket:message key="gb.commitMessageRenderer"></wicket:message></th><td class="edit"><select class="span2" wicket:id="commitMessageRenderer" tabindex="15" /></td></tr>
                <tr><th colspan="2"><hr/></th></tr>
                <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>
                <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>
            </tbody>
        </table>
        </div>
@@ -52,15 +53,15 @@
        <div class="tab-pane" id="permissions">
            <table class="plain">
                <tbody class="settings">
                    <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="16" /> </td></tr>
                    <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="17" /> </td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="17" /></td></tr>
                    <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="18" /></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;"><span class="authorizationControl" wicket:id="authorizationControl"></span></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <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>
                    <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>
                    <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>
                    <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>
                    <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>
                    <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>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th><wicket:message key="gb.userPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
@@ -73,7 +74,7 @@
        <div class="tab-pane" id="federation">
            <table class="plain">
                <tbody class="settings">
                    <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="21" /></td></tr>
                    <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="22" /></td></tr>
                    <tr><th><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
                </tbody>
            </table>
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -52,6 +52,7 @@
import com.gitblit.Constants;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.CommitMessageRenderer;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.GitBlit;
@@ -552,6 +553,10 @@
            }
        });
        
        List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values());
        DropDownChoice<CommitMessageRenderer> messageRendererChoice = new DropDownChoice<CommitMessageRenderer>("commitMessageRenderer", renderers);
        form.add(messageRendererChoice);
        form.add(new Button("save"));
        Button cancel = new Button("cancel") {
            private static final long serialVersionUID = 1L;
@@ -721,5 +726,4 @@
            return Integer.toString(index);
        }
    }
}
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -482,12 +482,17 @@
        add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));
    }
    protected void addFullText(String wicketId, String text, boolean substituteRegex) {
        String html = StringUtils.escapeForHtml(text, false);
        if (substituteRegex) {
            html = GitBlit.self().processCommitMessage(repositoryName, html);
        } else {
            html = StringUtils.breakLinesForHtml(html);
    protected void addFullText(String wicketId, String text) {
        RepositoryModel model = getRepositoryModel();
        String content = GitBlit.self().processCommitMessage(model, text);
        String html;
        switch (model.commitMessageRenderer) {
        case MARKDOWN:
            html = MessageFormat.format("<div class='commit_message'>{0}</div>", content);
            break;
        default:
            html = MessageFormat.format("<pre class='commit_message'>{0}</pre>", content);
            break;
        }
        add(new Label(wicketId, html).setEscapeModelStrings(false));
    }
src/main/java/com/gitblit/wicket/pages/TagPage.html
@@ -22,7 +22,7 @@
    </div>
    
    <!--  full message -->
    <pre class="commit_message" wicket:id="fullMessage">[commit message]</pre>
    <div class="topborder" wicket:id="fullMessage">[commit message]</div>
    
    <wicket:fragment wicket:id="fullPersonIdent">
        <span wicket:id="personName"></span><span wicket:id="personAddress"></span>
src/main/java/com/gitblit/wicket/pages/TagPage.java
@@ -92,7 +92,7 @@
        }
        add(WicketUtils.createTimestampLabel("tagDate", when, getTimeZone(), getTimeUtils()));
        addFullText("fullMessage", tagRef.getFullMessage(), true);
        addFullText("fullMessage", tagRef.getFullMessage());
    }
    @Override
src/main/resources/gitblit.css
@@ -891,6 +891,9 @@
.commit_message {
    padding: 8px;
}
.topborder {
    border: solid #ddd;
    border-width: 1px 0px 0px;
    border-radius: 0px;