# 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
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
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;
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;
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;
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(
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)) {
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);
* 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)) {
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;
}
/**
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
\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
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
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
<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
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);
</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
};\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
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
<tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="12" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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
<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
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
}\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
return Integer.toString(index);\r
}\r
}\r
- \r
}\r
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
</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
}\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
\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