Merge remote-tracking branch 'upstream/master' into anon-access

Conflicts:
	src/main/scala/app/SystemSettingsController.scala
	src/main/scala/service/SystemSettingsService.scala
	src/test/scala/view/AvatarImageProviderSpec.scala
This commit is contained in:
Mark LaCroix
2015-01-21 15:49:42 -05:00
27 changed files with 170 additions and 155 deletions

View File

@@ -8,6 +8,6 @@ Common scripts are in this directory.
This version of scripts has so far only been tested on Ubuntu and Mac. Someone else will have to test on RedHat.
To run:
1. Edit `gitbucket.conf` to suit.
2. Type: `install`
1. Edit `gitbucket.conf` to suit.
2. Type: `install`

View File

@@ -0,0 +1 @@
ALTER TABLE COMMIT_COMMENT ALTER COLUMN FILE_NAME NVARCHAR(260);

View File

@@ -291,7 +291,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
* Show the new repository form.
*/
get("/new")(usersOnly {
account.html.newrepo(getGroupsByUserName(context.loginAccount.get.userName))
account.html.newrepo(getGroupsByUserName(context.loginAccount.get.userName), context.settings.isCreateRepoOptionPublic)
})
/**

View File

@@ -248,10 +248,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
val id = params("id")
createCommitComment(repository.owner, repository.name, id, context.loginAccount.get.userName, form.content,
form.fileName, form.oldLineNumber, form.newLineNumber, form.issueId.isDefined)
if (form.issueId.isDefined)
recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, form.issueId.get, form.content)
else
recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
form.issueId match {
case Some(issueId) => recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, issueId, form.content)
case None => recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
}
redirect(s"/${repository.owner}/${repository.name}/commit/${id}")
})
@@ -273,10 +273,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
val id = params("id")
val commentId = createCommitComment(repository.owner, repository.name, id, context.loginAccount.get.userName,
form.content, form.fileName, form.oldLineNumber, form.newLineNumber, form.issueId.isDefined)
if (form.issueId.isDefined)
recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, form.issueId.get, form.content)
else
recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
form.issueId match {
case Some(issueId) => recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, issueId, form.content)
case None => recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
}
helper.html.commitcomment(getCommitComment(repository.owner, repository.name, commentId.toString).get,
hasWritePermission(repository.owner, repository.name, context.loginAccount), repository)
})
@@ -532,7 +532,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
}
}
private def archiveRepository(name: String, suffix: String, repository: RepositoryService.RepositoryInfo): File = {
private def archiveRepository(name: String, suffix: String, repository: RepositoryService.RepositoryInfo): Unit = {
val revision = name.stripSuffix(suffix)
val workDir = getDownloadWorkDir(repository.owner, repository.name, session.getId)
if(workDir.exists) {
@@ -540,21 +540,23 @@ trait RepositoryViewerControllerBase extends ControllerBase {
}
workDir.mkdirs
val file = new File(workDir, repository.name + "-" +
(if(revision.length == 40) revision.substring(0, 10) else revision).replace('/', '_') + suffix)
val filename = repository.name + "-" +
(if(revision.length == 40) revision.substring(0, 10) else revision).replace('/', '_') + suffix
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(revision))
using(new java.io.FileOutputStream(file)) { out =>
git.archive
.setFormat(suffix.tail)
.setTree(revCommit.getTree)
.setOutputStream(out)
.call()
}
contentType = "application/octet-stream"
response.setHeader("Content-Disposition", s"attachment; filename=${file.getName}")
file
response.setHeader("Content-Disposition", s"attachment; filename=${filename}")
response.setBufferSize(1024 * 1024);
git.archive
.setFormat(suffix.tail)
.setTree(revCommit.getTree)
.setOutputStream(response.getOutputStream)
.call()
Unit
}
}

View File

@@ -17,6 +17,7 @@ trait SystemSettingsControllerBase extends ControllerBase {
"information" -> trim(label("Information", optional(text()))),
"allowAccountRegistration" -> trim(label("Account registration", boolean())),
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
"isCreateRepoOptionPublic" -> trim(label("Default option to create a new repository", boolean())),
"gravatar" -> trim(label("Gravatar", boolean())),
"notification" -> trim(label("Notification", boolean())),
"ssh" -> trim(label("SSH access", boolean())),
@@ -42,6 +43,7 @@ trait SystemSettingsControllerBase extends ControllerBase {
"fullNameAttribute" -> trim(label("Full name attribute", optional(text()))),
"mailAttribute" -> trim(label("Mail address attribute", optional(text()))),
"tls" -> trim(label("Enable TLS", optional(boolean()))),
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
"keystore" -> trim(label("Keystore", optional(text())))
)(Ldap.apply))
)(SystemSettings.apply).verifying { settings =>

View File

@@ -189,7 +189,7 @@ trait RepositoryService { self: AccountService =>
new RepositoryInfo(
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl),
repository,
issues.size,
issues.count(_ == false),
issues.count(_ == true),
getForkedCount(
repository.originUserName.getOrElse(repository.userName),

View File

@@ -15,6 +15,7 @@ trait SystemSettingsService {
settings.information.foreach(x => props.setProperty(Information, x))
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString)
props.setProperty(IsCreateRepoOptionPublic, settings.isCreateRepoOptionPublic.toString)
props.setProperty(Gravatar, settings.gravatar.toString)
props.setProperty(Notification, settings.notification.toString)
props.setProperty(Ssh, settings.ssh.toString)
@@ -43,6 +44,7 @@ trait SystemSettingsService {
ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x))
ldap.mailAttribute.foreach(x => props.setProperty(LdapMailAddressAttribute, x))
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
ldap.ssl.foreach(x => props.setProperty(LdapSsl, x.toString))
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
}
}
@@ -65,6 +67,7 @@ trait SystemSettingsService {
getOptionValue[String](props, Information, None),
getValue(props, AllowAccountRegistration, false),
getValue(props, AllowAnonymousAccess, true),
getValue(props, IsCreateRepoOptionPublic, true),
getValue(props, Gravatar, true),
getValue(props, Notification, false),
getValue(props, Ssh, false),
@@ -94,6 +97,7 @@ trait SystemSettingsService {
getOptionValue(props, LdapFullNameAttribute, None),
getOptionValue(props, LdapMailAddressAttribute, None),
getOptionValue[Boolean](props, LdapTls, None),
getOptionValue[Boolean](props, LdapSsl, None),
getOptionValue(props, LdapKeystore, None)))
} else {
None
@@ -112,6 +116,7 @@ object SystemSettingsService {
information: Option[String],
allowAccountRegistration: Boolean,
allowAnonymousAccess: Boolean,
isCreateRepoOptionPublic: Boolean,
gravatar: Boolean,
notification: Boolean,
ssh: Boolean,
@@ -137,6 +142,7 @@ object SystemSettingsService {
fullNameAttribute: Option[String],
mailAttribute: Option[String],
tls: Option[Boolean],
ssl: Option[Boolean],
keystore: Option[String])
case class Smtp(
@@ -156,6 +162,7 @@ object SystemSettingsService {
private val Information = "information"
private val AllowAccountRegistration = "allow_account_registration"
private val AllowAnonymousAccess = "allow_anonymous_access"
private val IsCreateRepoOptionPublic = "is_create_repository_option_public"
private val Gravatar = "gravatar"
private val Notification = "notification"
private val Ssh = "ssh"
@@ -178,6 +185,7 @@ object SystemSettingsService {
private val LdapFullNameAttribute = "ldap.fullname_attribute"
private val LdapMailAddressAttribute = "ldap.mail_attribute"
private val LdapTls = "ldap.tls"
private val LdapSsl = "ldap.ssl"
private val LdapKeystore = "ldap.keystore"
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A =

View File

@@ -52,6 +52,7 @@ object AutoUpdate {
* The history of versions. A head of this sequence is the current BitBucket version.
*/
val versions = Seq(
new Version(2, 8),
new Version(2, 7) {
override def update(conn: Connection): Unit = {
super.update(conn)

View File

@@ -48,6 +48,7 @@ object LDAPUtil {
dn = ldapSettings.bindDN.getOrElse(""),
password = ldapSettings.bindPassword.getOrElse(""),
tls = ldapSettings.tls.getOrElse(false),
ssl = ldapSettings.ssl.getOrElse(false),
keystore = ldapSettings.keystore.getOrElse(""),
error = "System LDAP authentication failed."
){ conn =>
@@ -65,6 +66,7 @@ object LDAPUtil {
dn = userDN,
password = password,
tls = ldapSettings.tls.getOrElse(false),
ssl = ldapSettings.ssl.getOrElse(false),
keystore = ldapSettings.keystore.getOrElse(""),
error = "User LDAP Authentication Failed."
){ conn =>
@@ -96,7 +98,7 @@ object LDAPUtil {
}).replaceAll("[^a-zA-Z0-9\\-_.]", "").replaceAll("^[_\\-]", "")
}
private def bind[A](host: String, port: Int, dn: String, password: String, tls: Boolean, keystore: String, error: String)
private def bind[A](host: String, port: Int, dn: String, password: String, tls: Boolean, ssl: Boolean, keystore: String, error: String)
(f: LDAPConnection => Either[String, A]): Either[String, A] = {
if (tls) {
// Dynamically set Sun as the security provider
@@ -109,7 +111,13 @@ object LDAPUtil {
}
}
val conn: LDAPConnection = new LDAPConnection(new LDAPJSSEStartTLSFactory())
val conn: LDAPConnection =
if(ssl) {
new LDAPConnection(new LDAPJSSESecureSocketFactory())
}else {
new LDAPConnection(new LDAPJSSEStartTLSFactory())
}
try {
// Connect to the server
conn.connect(host, port)

View File

@@ -18,9 +18,14 @@ object Markdown {
/**
* Converts Markdown of Wiki pages to HTML.
*/
def toHtml(markdown: String, repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean, enableRefsLink: Boolean,
enableTaskList: Boolean = false, hasWritePermission: Boolean = false)(implicit context: app.Context): String = {
def toHtml(markdown: String,
repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableTaskList: Boolean = false,
hasWritePermission: Boolean = false,
pages: List[String] = Nil)(implicit context: app.Context): String = {
// escape issue id
val s = if(enableRefsLink){
markdown.replaceAll("(?<=(\\W|^))#(\\d+)(?=(\\W|$))", "issue:$2")
@@ -35,12 +40,16 @@ object Markdown {
Extensions.AUTOLINKS | Extensions.WIKILINKS | Extensions.FENCED_CODE_BLOCKS | Extensions.TABLES | Extensions.HARDWRAPS | Extensions.SUPPRESS_ALL_HTML
).parseMarkdown(source.toCharArray)
new GitBucketHtmlSerializer(markdown, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission).toHtml(rootNode)
new GitBucketHtmlSerializer(markdown, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission, pages).toHtml(rootNode)
}
}
class GitBucketLinkRender(context: app.Context, repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean) extends LinkRenderer with WikiService {
class GitBucketLinkRender(
context: app.Context,
repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean,
pages: List[String]) extends LinkRenderer with WikiService {
override def render(node: WikiLinkNode): Rendering = {
if(enableWikiLink){
try {
@@ -54,7 +63,7 @@ class GitBucketLinkRender(context: app.Context, repository: service.RepositorySe
val url = repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/" + StringUtil.urlEncode(page)
if(getWikiPage(repository.owner, repository.name, page).isDefined){
if(pages.contains(page)){
new Rendering(url, label)
} else {
new Rendering(url, label).withAttribute("class", "absent")
@@ -91,9 +100,10 @@ class GitBucketHtmlSerializer(
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableTaskList: Boolean,
hasWritePermission: Boolean
hasWritePermission: Boolean,
pages: List[String]
)(implicit val context: app.Context) extends ToHtmlSerializer(
new GitBucketLinkRender(context, repository, enableWikiLink),
new GitBucketLinkRender(context, repository, enableWikiLink, pages),
Map[String, VerbatimSerializer](VerbatimSerializer.DEFAULT -> new GitBucketVerbatimSerializer).asJava
) with LinkConverter with RequestCache {

View File

@@ -86,9 +86,14 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
/**
* Converts Markdown of Wiki pages to HTML.
*/
def markdown(value: String, repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean, enableRefsLink: Boolean, enableTaskList: Boolean = false, hasWritePermission: Boolean = false)(implicit context: app.Context): Html =
Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission))
def markdown(value: String,
repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableTaskList: Boolean = false,
hasWritePermission: Boolean = false,
pages: List[String] = Nil)(implicit context: app.Context): Html =
Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission, pages))
def renderMarkup(filePath: List[String], fileContent: String, branch: String,
repository: service.RepositoryService.RepositoryInfo,

View File

@@ -1,4 +1,5 @@
@(groupNames: List[String])(implicit context: app.Context)
@(groupNames: List[String],
isCreateRepoOptionPublic: Boolean)(implicit context: app.Context)
@import context._
@import view.helpers._
@html.main("Create a New Repository"){
@@ -29,7 +30,7 @@
</fieldset>
<fieldset class="margin">
<label class="radio">
<input type="radio" name="isPrivate" value="false" checked>
<input type="radio" name="isPrivate" value="false" @if(isCreateRepoOptionPublic){checked}>
<span class="strong"><img src="@assets/common/images/repo_public.png"/>&nbsp;</i>&nbsp;Public</span><br>
<div>
<span>All users and guests can read this repository.</span>
@@ -38,7 +39,7 @@
</fieldset>
<fieldset>
<label class="radio">
<input type="radio" name="isPrivate" value="true">
<input type="radio" name="isPrivate" value="true" @if(!isCreateRepoOptionPublic){checked}>
<span class="strong"><img src="@assets/common/images/repo_private.png"/>&nbsp;</i>&nbsp;Private</span><br>
<div>
<span>Only collaborators can read this repository.</span>

View File

@@ -53,6 +53,18 @@
<span class="strong">Deny</span> - Only administrators can create accounts.
</label>
</fieldset>
<hr>
<label class="strong">Default option to create a new repository</label>
<fieldset>
<label class="radio">
<input type="radio" name="isCreateRepoOptionPublic" value="true"@if(settings.isCreateRepoOptionPublic){ checked}>
<span class="strong">Public</span> - All users and guests can read that repository.
</label>
<label class="radio">
<input type="radio" name="isCreateRepoOptionPublic" value="false"@if(!settings.isCreateRepoOptionPublic){ checked}>
<span class="strong">Private</span> - Only collaborators can read that repository.
</label>
</fieldset>
<!--====================================================================-->
<!-- Anonymous access -->
<!--====================================================================-->
@@ -184,6 +196,13 @@
</label>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" name="ldap.ssl"@if(settings.ldap.flatMap(_.ssl).getOrElse(false)){ checked}/> Enable SSL
</label>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapBindDN">Keystore</label>
<div class="controls">

View File

@@ -8,13 +8,6 @@
@import context._
@import view.helpers._
@import service.IssuesService.IssueInfo
@*
<ul class="nav nav-pills-group pull-left fill-width">
<li class="@if(filter == "created_by"){active} first"><a href="@path/dashboard/issues/created_by@condition.toURL">Created</a></li>
<li class="@if(filter == "assigned"){active}"><a href="@path/dashboard/issues/assigned@condition.toURL">Assigned</a></li>
<li class="@if(filter == "mentioned"){active} last"><a href="@path/dashboard/issues/mentioned@condition.toURL">Mentioned</a></li>
</ul>
*@
<table class="table table-bordered table-hover table-issues">
<tr>
<th style="background-color: #eee;">
@@ -29,7 +22,7 @@
} else {
<img src="@assets/common/images/issue-@(if(issue.closed) "closed" else "open").png"/>
}
<a href="@path/@issue.userName/@issue.repositoryName">@issue.repositoryName</a>&nbsp;&#xFF65;
<a href="@path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a>&nbsp;&#xFF65;
@if(issue.isPullRequest){
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
} else {

View File

@@ -11,6 +11,6 @@
@dashboard.html.tab("pulls")
<div class="container">
@issuesnavi(filter, "pulls", condition)
@pullslist(issues, page, openCount, closedCount, condition, filter, groups)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
</div>
}

View File

@@ -1,67 +0,0 @@
@(issues: List[service.IssuesService.IssueInfo],
page: Int,
openCount: Int,
closedCount: Int,
condition: service.IssuesService.IssueSearchCondition,
filter: String,
groups: List[String])(implicit context: app.Context)
@import context._
@import view.helpers._
@import service.IssuesService.IssueInfo
@*
<ul class="nav nav-pills-group pull-left fill-width">
<li class="@if(filter == "created_by"){active} first"><a href="@path/dashboard/pulls/created_by@condition.toURL">Created</a></li>
<li class="@if(filter == "assigned"){active}"><a href="@path/dashboard/pulls/assigned@condition.toURL">Assigned</a></li>
<li class="@if(filter == "mentioned"){active} last"><a href="@path/dashboard/pulls/mentioned@condition.toURL">Mentioned</a></li>
<li class="pull-right">
<div class="input-prepend" style="margin-bottom: 0px;">
<div class="btn-group">
<button type="button" class="btn dropdown-toggle" data-toggle="dropdown" style="height: 34px;">
Filter
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="?q=is:open">Open issues and pull requests</a></li>
<li><a href="?q=is:open+is:issue+author:@urlEncode(loginAccount.get.userName)">Your issues</a></li>
<li><a href="?q=is:open+is:pr+author:@urlEncode(loginAccount.get.userName)">Your pull requests</a></li>
<li><a href="?q=is:open+assignee:@urlEncode(loginAccount.get.userName)">Everything assigned to you</a></li>
<li><a href="?q=is:open+mentions:@urlEncode(loginAccount.get.userName)">Everything mentioning you</a></li>
</ul>
</div>
<input type="text" id="search-filter-box" class="input-xlarge" name="q" style="height: 24px;" value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
</div>
</li>
</ul>
*@
<table class="table table-bordered table-hover table-issues">
<tr>
<th style="background-color: #eee;">
@dashboard.html.header(openCount, closedCount, condition, groups)
</th>
</tr>
@issues.map { case IssueInfo(issue, labels, milestone, commentCount) =>
<tr>
<td>
<img src="@assets/common/images/pullreq-@(if(issue.closed) "closed" else "open").png"/>
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
<span class="pull-right muted">#@issue.issueId</span>
<div style="margin-left: 20px;">
@issue.content.map { content =>
@cut(content, 90)
}.getOrElse {
<span class="muted">No description available</span>
}
</div>
<div class="small muted" style="margin-left: 20px;">
@avatarLink(issue.openedUserName, 20) by @user(issue.openedUserName, styleClass="username") @datetime(issue.registeredDate)&nbsp;
@if(commentCount > 0){
<i class="icon-comment"></i><a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">@commentCount @plural(commentCount, "comment")</a>
}
</div>
</td>
</tr>
}
</table>
<div class="pull-right">
@helper.html.paginator(page, (if(condition.state == "open") openCount else closedCount), service.PullRequestService.PullRequestLimit, 10, condition.toURL)
</div>

View File

@@ -120,6 +120,13 @@ $(function(){
renderDiffs(0);
});
$('.toggle-notes').change(function() {
if (!$(this).prop('checked')) {
$(this).closest('table').find('.not-diff.inline-comment-form').remove();
}
$(this).closest('table').find('.not-diff').toggle();
});
function renderDiffs(viewType){
window.viewType = viewType;
@diffs.zipWithIndex.map { case (diff, i) =>
@@ -146,33 +153,31 @@ $(function(){
if (typeof $('#show-notes')[0] !== 'undefined' && !$('#show-notes')[0].checked) {
$(this).hide();
}
var tmp;
var diff;
if (typeof oldline !== 'undefined') {
var tmp;
if (typeof newline !== 'undefined') {
tmp = getInlineContainer();
} else {
tmp = getInlineContainer('old');
}
tmp.children('td:first').html($(this).clone().show());
$('table[filename="' + filename + '"]').find('table.diff').find('.oldline').filter(function() {
return new RegExp('^' + oldline + '[\\+]*$').test($(this).text());
}).parent().nextAll(':not(.not-diff):first').before(tmp);
diff = $('table[filename="' + filename + '"]');
diff.find('table.diff').find('.oldline[line-number=' + oldline + ']')
.parent().nextAll(':not(.not-diff):first').before(tmp);
} else {
var tmp = getInlineContainer('new');
tmp = getInlineContainer('new');
tmp.children('td:last').html($(this).clone().show());
$('table[filename="' + filename + '"]').find('table.diff').find('.newline').filter(function() {
return new RegExp('^' + newline + '\\+$').test($(this).text());
}).parent().nextAll(':not(.not-diff):first').before(tmp);
diff = $('table[filename="' + filename + '"]');
diff.find('table.diff').find('.newline[line-number=' + newline + ']')
.parent().nextAll(':not(.not-diff):first').before(tmp);
}
});
$('.toggle-notes').change(function() {
if (!$(this).prop('checked')) {
$(this).closest('table').find('.not-diff.inline-comment-form').remove();
if (!diff.find('.toggle-notes').prop('checked')) {
tmp.hide();
}
$(this).closest('table').find('.not-diff').toggle();
});
@if(hasWritePermission) {
$('table.diff th').hover(
$('table.diff td').hover(
function() {
$(this).find('b').css('display', 'inline-block');
},
@@ -180,12 +185,12 @@ $(function(){
$(this).find('b').css('display', 'none');
}
);
$('table.diff td').hover(
$('table.diff th').hover(
function() {
$(this).prev('th').find('b').css('display', 'inline-block');
$(this).nextAll().find('b').first().css('display', 'inline-block');
},
function() {
$(this).prev('th').find('b').css('display', 'none');
$(this).nextAll().find('b').first().css('display', 'none');
}
);
$('.add-comment').click(function() {
@@ -198,12 +203,14 @@ $(function(){
if (!$tr.nextAll(':not(.not-diff):first').prev().hasClass('inline-comment-form')) {
var commitId = $this.closest('.table-bordered').attr('commitId'),
fileName = $this.closest('.table-bordered').attr('fileName'),
oldLineNumber, newLineNumber = $this.closest('.newline').clone().children().remove().end().text(),
url = '@url(repository)/commit/' + commitId + '/comment/_form?fileName=' + fileName @if(issueId.isDefined){+ '&issueId=@issueId.get'};
oldLineNumber, newLineNumber,
url = '@url(repository)/commit/' + commitId + '/comment/_form?fileName=' + fileName@issueId.map { id => + '&issueId=@id' };
if (viewType == 0) {
oldLineNumber = $this.closest('.oldline').clone().children().remove().end().text();
oldLineNumber = $this.parent().prev('.oldline').attr('line-number');
newLineNumber = $this.parent().prev('.newline').attr('line-number');
} else {
oldLineNumber = $this.closest('tr').find('.oldline').text();
oldLineNumber = $this.parent().prevAll('.oldline').attr('line-number');
newLineNumber = $this.parent().prevAll('.newline').attr('line-number');
}
if (!isNaN(oldLineNumber) && oldLineNumber) {
url += ('&oldLineNumber=' + oldLineNumber)

View File

@@ -43,7 +43,7 @@
<span class="icon-bar"></span>
</button>
<a class="brand" href="@path/">
<img src="@assets/common/images/gitbucket.png"/>GitBucket
<img src="@assets/common/images/gitbucket.png" style="width: 24px; height: 24px;"/>GitBucket
@defining(servlet.AutoUpdate.getCurrentVersion){ version =>
<span class="header-version">@version.majorVersion.@version.minorVersion</span>
}

View File

@@ -62,7 +62,7 @@
<p>
<span class="strong">Step 3:</span> Merge the changes and update the server
</p>
@defining(s"git checkout master\ngit merge ${pullreq.requestUserName}-${pullreq.requestBranch}\ngit push origin ${pullreq.branch}"){ command =>
@defining(s"git checkout ${pullreq.branch}\ngit merge ${pullreq.requestUserName}-${pullreq.requestBranch}\ngit push origin ${pullreq.branch}"){ command =>
@helper.html.copy("merge-command-copy-3", command){
<pre style="width: 500px; float: left;">@command</pre>
}

View File

@@ -29,10 +29,10 @@
<input type="submit" class="btn btn-success" formaction="@url(repository)/commit/@commitId/comment/new" value="Comment on this commit"/>
</div>
}
@if(issueId.isDefined){<input type="hidden" name="issueId" value="@issueId.get">}
@if(fileName.isDefined){<input type="hidden" name="fileName" value="@fileName.get">}
@if(oldLineNumber.isDefined){<input type="hidden" name="oldLineNumber" value="@oldLineNumber.get">}
@if(newLineNumber.isDefined){<input type="hidden" name="newLineNumber" value="@newLineNumber.get">}
@issueId.map { issueId => <input type="hidden" name="issueId" value="@issueId"> }
@fileName.map { fileName => <input type="hidden" name="fileName" value="@fileName"> }
@oldLineNumber.map { oldLineNumber => <input type="hidden" name="oldLineNumber" value="@oldLineNumber"> }
@newLineNumber.map { newLineNumber => <input type="hidden" name="newLineNumber" value="@newLineNumber"> }
</form>
<script>
$('.btn-inline-comment').click(function(e) {

View File

@@ -2,6 +2,12 @@
@import context._
@main("Sign in"){
<div class="signin-form">
@settings.information.map { information =>
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
<button type="button" class="close" data-dismiss="alert">&times;</button>
@Html(information)
</div>
}
@signinform(settings)
</div>
}

View File

@@ -53,7 +53,7 @@
</div>
<div style="width: 650px;" class="pull-left">
<div class="markdown-body">
@markdown(page.content, repository, true, false)
@markdown(page.content, repository, true, false, false, false, pages)
</div>
</div>
}

View File

@@ -975,15 +975,16 @@ table.diff .add-comment {
position: absolute;
background: blue;
top: 0;
left: -7px;
color: white;
padding: 2px;
padding: 2px 4px;
border: solid 1px blue;
border-radius: 3px;
z-index: 99;
}
table.diff .add-comment:hover {
padding: 4px;
padding: 4px 6px;
top: -1px;
}
@@ -1009,6 +1010,14 @@ table.diff tbody tr.not-diff:hover td{
padding: 10px;
}
.diff .oldline:before, .diff .newline:before {
content: attr(line-number);
}
.diff .skipline:before {
content: "..."
}
/****************************************************************************/
/* Repository Settings */
/****************************************************************************/
@@ -1088,6 +1097,8 @@ div.markdown-body pre {
div.markdown-body code {
font-size: 12px;
padding: 0 5px;
background-color: rgba(0,0,0,0.04);
rgb(51, 51, 51);
}
div.markdown-body table {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 B

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -84,7 +84,7 @@ diffview = {
b.appendChild(document.createTextNode("+"));
b.style.display = "none";
b.className = "add-comment";
e.appendChild(b);
e.insertBefore(b, e.firstChild);
e.style.position = "relative";
return e;
}
@@ -117,9 +117,12 @@ diffview = {
* to the given row.
*/
function addCells (row, tidx, tend, textLines, change, thclass) {
var tmp;
if (tidx < tend) {
row.appendChild(addButton(ctelt("th", thclass, (tidx + 1).toString())));
row.appendChild(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
tmp = ctelt("th", thclass, "");
tmp.setAttribute("line-number", (tidx + 1).toString());
row.appendChild(tmp);
row.appendChild(addButton(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0"))));
return tidx + 1;
} else {
row.appendChild(document.createElement("th"));
@@ -129,9 +132,13 @@ diffview = {
}
function addCellsInline (row, tidx, tidx2, textLines, change) {
row.appendChild(ctelt("th", "oldline", tidx == null ? "" : (tidx + 1).toString()));
row.appendChild(addButton(ctelt("th", "newline", tidx2 == null ? "" : (tidx2 + 1).toString())));
row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
var tmp = ctelt("th", "oldline", "");
tmp.setAttribute("line-number", tidx == null ? "" : (tidx + 1).toString());
row.appendChild(tmp);
tmp = ctelt("th", "newline", "");
tmp.setAttribute("line-number", tidx2 == null ? "" : (tidx2 + 1).toString());
row.appendChild(tmp);
row.appendChild(addButton(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0"))));
}
for (var idx = 0; idx < opcodes.length; idx++) {
@@ -154,9 +161,9 @@ diffview = {
b += jump;
n += jump;
i += jump - 1;
node.appendChild(telt("th", "..."));
node.appendChild(ctelt("th", "skipline", ""));
if (!inline) node.appendChild(ctelt("td", "skip", ""));
node.appendChild(telt("th", "..."));
node.appendChild(ctelt("th", "skipline", ""));
node.appendChild(ctelt("td", "skip", ""));
// skip last lines if they're all equal

View File

@@ -96,6 +96,7 @@ class AvatarImageProviderSpec extends Specification with Mockito {
information = None,
allowAccountRegistration = false,
allowAnonymousAccess = true,
isCreateRepoOptionPublic = true,
gravatar = useGravatar,
notification = false,
ssh = false,