mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-06 13:35:50 +01:00
Merge pull request #1338 from gitbucket/new-permission-system
New permission system
This commit is contained in:
2
src/main/resources/update/gitbucket-core_4.7.sql
Normal file
2
src/main/resources/update/gitbucket-core_4.7.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- DELETE COLLABORATORS IN GROUP REPOSITORIES
|
||||||
|
DELETE FROM COLLABORATOR WHERE USER_NAME IN (SELECT USER_NAME FROM ACCOUNT WHERE GROUP_ACCOUNT = TRUE)
|
||||||
33
src/main/resources/update/gitbucket-core_4.7.xml
Normal file
33
src/main/resources/update/gitbucket-core_4.7.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<changeSet>
|
||||||
|
<addColumn tableName="COLLABORATOR">
|
||||||
|
<column name="PERMISSION" type="varchar(10)" nullable="false" defaultValue="ADMIN"/>
|
||||||
|
</addColumn>
|
||||||
|
<addColumn tableName="REPOSITORY">
|
||||||
|
<column name="WIKI_OPTION" type="varchar(10)" nullable="false" defaultValue="DISABLE"/>
|
||||||
|
<column name="ISSUES_OPTION" type="varchar(10)" nullable="false" defaultValue="DISABLE"/>
|
||||||
|
</addColumn>
|
||||||
|
<update tableName="REPOSITORY">
|
||||||
|
<column name="WIKI_OPTION" value="DISABLE"/>
|
||||||
|
<where>ENABLE_WIKI = FALSE</where>
|
||||||
|
</update>
|
||||||
|
<update tableName="REPOSITORY">
|
||||||
|
<column name="WIKI_OPTION" value="PRIVATE"/>
|
||||||
|
<where>ENABLE_WIKI = TRUE AND ALLOW_WIKI_EDITING = FALSE</where>
|
||||||
|
</update>
|
||||||
|
<update tableName="REPOSITORY">
|
||||||
|
<column name="WIKI_OPTION" value="PUBLIC"/>
|
||||||
|
<where>ENABLE_WIKI = TRUE AND ALLOW_WIKI_EDITING = TRUE</where>
|
||||||
|
</update>
|
||||||
|
<update tableName="REPOSITORY">
|
||||||
|
<column name="ISSUES_OPTION" value="DISABLE"/>
|
||||||
|
<where>ENABLE_ISSUES = FALSE</where>
|
||||||
|
</update>
|
||||||
|
<update tableName="REPOSITORY">
|
||||||
|
<column name="ISSUES_OPTION" value="PUBLIC"/>
|
||||||
|
<where>ENABLE_ISSUES = TRUE</where>
|
||||||
|
</update>
|
||||||
|
<dropColumn tableName="REPOSITORY" columnName="ENABLE_WIKI"/>
|
||||||
|
<dropColumn tableName="REPOSITORY" columnName="ALLOW_WIKI_EDITING"/>
|
||||||
|
<dropColumn tableName="REPOSITORY" columnName="ENABLE_ISSUES"/>
|
||||||
|
</changeSet>
|
||||||
@@ -18,5 +18,9 @@ object GitBucketCoreModule extends Module("gitbucket-core",
|
|||||||
new Version("4.5.0"),
|
new Version("4.5.0"),
|
||||||
new Version("4.6.0",
|
new Version("4.6.0",
|
||||||
new LiquibaseMigration("update/gitbucket-core_4.6.xml")
|
new LiquibaseMigration("update/gitbucket-core_4.6.xml")
|
||||||
|
),
|
||||||
|
new Version("4.7.0",
|
||||||
|
new LiquibaseMigration("update/gitbucket-core_4.7.xml"),
|
||||||
|
new SqlMigration("update/gitbucket-core_4.7.sql")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ object ApiUser{
|
|||||||
def apply(user: Account): ApiUser = ApiUser(
|
def apply(user: Account): ApiUser = ApiUser(
|
||||||
login = user.userName,
|
login = user.userName,
|
||||||
email = user.mailAddress,
|
email = user.mailAddress,
|
||||||
`type` = if(user.isGroupAccount){ "Organization" }else{ "User" },
|
`type` = if(user.isGroupAccount){ "Organization" } else { "User" },
|
||||||
site_admin = user.isAdmin,
|
site_admin = user.isAdmin,
|
||||||
created_at = user.registeredDate
|
created_at = user.registeredDate
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -319,13 +319,13 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
// Update GROUP_MEMBER
|
// Update GROUP_MEMBER
|
||||||
updateGroupMembers(form.groupName, members)
|
updateGroupMembers(form.groupName, members)
|
||||||
// Update COLLABORATOR for group repositories
|
// // Update COLLABORATOR for group repositories
|
||||||
getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
// getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||||
removeCollaborators(form.groupName, repositoryName)
|
// removeCollaborators(form.groupName, repositoryName)
|
||||||
members.foreach { case (userName, isManager) =>
|
// members.foreach { case (userName, isManager) =>
|
||||||
addCollaborator(form.groupName, repositoryName, userName)
|
// addCollaborator(form.groupName, repositoryName, userName)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||||
redirect(s"/${form.groupName}")
|
redirect(s"/${form.groupName}")
|
||||||
@@ -402,13 +402,13 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
parentUserName = Some(repository.owner)
|
parentUserName = Some(repository.owner)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add collaborators for group repository
|
// // Add collaborators for group repository
|
||||||
val ownerAccount = getAccountByUserName(accountName).get
|
// val ownerAccount = getAccountByUserName(accountName).get
|
||||||
if(ownerAccount.isGroupAccount){
|
// if(ownerAccount.isGroupAccount){
|
||||||
getGroupMembers(accountName).foreach { member =>
|
// getGroupMembers(accountName).foreach { member =>
|
||||||
addCollaborator(accountName, repository.name, member.userName)
|
// addCollaborator(accountName, repository.name, member.userName)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Insert default labels
|
// Insert default labels
|
||||||
insertDefaultLabels(accountName, repository.name)
|
insertDefaultLabels(accountName, repository.name)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ApiController extends ApiControllerBase
|
|||||||
with GroupManagerAuthenticator
|
with GroupManagerAuthenticator
|
||||||
with ReferrerAuthenticator
|
with ReferrerAuthenticator
|
||||||
with ReadableUsersAuthenticator
|
with ReadableUsersAuthenticator
|
||||||
with CollaboratorsAuthenticator
|
with WritableUsersAuthenticator
|
||||||
|
|
||||||
trait ApiControllerBase extends ControllerBase {
|
trait ApiControllerBase extends ControllerBase {
|
||||||
self: RepositoryService
|
self: RepositoryService
|
||||||
@@ -52,7 +52,7 @@ trait ApiControllerBase extends ControllerBase {
|
|||||||
with GroupManagerAuthenticator
|
with GroupManagerAuthenticator
|
||||||
with ReferrerAuthenticator
|
with ReferrerAuthenticator
|
||||||
with ReadableUsersAuthenticator
|
with ReadableUsersAuthenticator
|
||||||
with CollaboratorsAuthenticator =>
|
with WritableUsersAuthenticator =>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://developer.github.com/v3/#root-endpoint
|
* https://developer.github.com/v3/#root-endpoint
|
||||||
@@ -177,7 +177,8 @@ trait ApiControllerBase extends ControllerBase {
|
|||||||
* https://developer.github.com/v3/repos/collaborators/#list-collaborators
|
* https://developer.github.com/v3/repos/collaborators/#list-collaborators
|
||||||
*/
|
*/
|
||||||
get("/api/v3/repos/:owner/:repo/collaborators") (referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repo/collaborators") (referrersOnly { repository =>
|
||||||
JsonFormat(getCollaborators(params("owner"), params("repo")).map(u => ApiUser(getAccountByUserName(u).get)))
|
// TODO Should ApiUser take permission? getCollaboratorUserNames does not return owner group members.
|
||||||
|
JsonFormat(getCollaboratorUserNames(params("owner"), params("repo")).map(u => ApiUser(getAccountByUserName(u).get)))
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -327,7 +328,7 @@ trait ApiControllerBase extends ControllerBase {
|
|||||||
* Create a label
|
* Create a label
|
||||||
* https://developer.github.com/v3/issues/labels/#create-a-label
|
* https://developer.github.com/v3/issues/labels/#create-a-label
|
||||||
*/
|
*/
|
||||||
post("/api/v3/repos/:owner/:repository/labels")(collaboratorsOnly { repository =>
|
post("/api/v3/repos/:owner/:repository/labels")(writableUsersOnly { repository =>
|
||||||
(for{
|
(for{
|
||||||
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
||||||
} yield {
|
} yield {
|
||||||
@@ -352,7 +353,7 @@ trait ApiControllerBase extends ControllerBase {
|
|||||||
* Update a label
|
* Update a label
|
||||||
* https://developer.github.com/v3/issues/labels/#update-a-label
|
* https://developer.github.com/v3/issues/labels/#update-a-label
|
||||||
*/
|
*/
|
||||||
patch("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository =>
|
patch("/api/v3/repos/:owner/:repository/labels/:labelName")(writableUsersOnly { repository =>
|
||||||
(for{
|
(for{
|
||||||
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
||||||
} yield {
|
} yield {
|
||||||
@@ -378,7 +379,7 @@ trait ApiControllerBase extends ControllerBase {
|
|||||||
* Delete a label
|
* Delete a label
|
||||||
* https://developer.github.com/v3/issues/labels/#delete-a-label
|
* https://developer.github.com/v3/issues/labels/#delete-a-label
|
||||||
*/
|
*/
|
||||||
delete("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository =>
|
delete("/api/v3/repos/:owner/:repository/labels/:labelName")(writableUsersOnly { repository =>
|
||||||
LockUtil.lock(RepositoryName(repository).fullName) {
|
LockUtil.lock(RepositoryName(repository).fullName) {
|
||||||
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
||||||
deleteLabel(repository.owner, repository.name, label.labelId)
|
deleteLabel(repository.owner, repository.name, label.labelId)
|
||||||
@@ -466,7 +467,7 @@ trait ApiControllerBase extends ControllerBase {
|
|||||||
/**
|
/**
|
||||||
* https://developer.github.com/v3/repos/statuses/#create-a-status
|
* https://developer.github.com/v3/repos/statuses/#create-a-status
|
||||||
*/
|
*/
|
||||||
post("/api/v3/repos/:owner/:repo/statuses/:sha")(collaboratorsOnly { repository =>
|
post("/api/v3/repos/:owner/:repo/statuses/:sha")(writableUsersOnly { repository =>
|
||||||
(for{
|
(for{
|
||||||
ref <- params.get("sha")
|
ref <- params.get("sha")
|
||||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||||
|
|||||||
@@ -108,18 +108,29 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/_user/proposals")(usersOnly {
|
get("/_user/proposals")(usersOnly {
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
|
val user = params("user").toBoolean
|
||||||
|
val group = params("group").toBoolean
|
||||||
org.json4s.jackson.Serialization.write(
|
org.json4s.jackson.Serialization.write(
|
||||||
Map("options" -> getAllUsers(false).filter(!_.isGroupAccount).map(_.userName).toArray)
|
Map("options" -> (
|
||||||
|
getAllUsers(false)
|
||||||
|
.withFilter { t => (user, group) match {
|
||||||
|
case (true, true) => true
|
||||||
|
case (true, false) => !t.isGroupAccount
|
||||||
|
case (false, true) => t.isGroupAccount
|
||||||
|
case (false, false) => false
|
||||||
|
}}.map { t => t.userName }
|
||||||
|
))
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON API for checking user existence.
|
* JSON API for checking user or group existence.
|
||||||
|
* Returns a single string which is any of "group", "user" or "".
|
||||||
*/
|
*/
|
||||||
post("/_user/existence")(usersOnly {
|
post("/_user/existence")(usersOnly {
|
||||||
getAccountByUserName(params("userName")).map { account =>
|
getAccountByUserName(params("userName")).map { account =>
|
||||||
if(params.get("userOnly").isDefined) !account.isGroupAccount else true
|
if(account.isGroupAccount) "group" else "user"
|
||||||
} getOrElse false
|
} getOrElse ""
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO Move to RepositoryViwerController?
|
// TODO Move to RepositoryViwerController?
|
||||||
|
|||||||
@@ -2,24 +2,24 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import gitbucket.core.issues.html
|
import gitbucket.core.issues.html
|
||||||
import gitbucket.core.service.IssuesService._
|
import gitbucket.core.service.IssuesService._
|
||||||
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.service._
|
import gitbucket.core.service._
|
||||||
import gitbucket.core.util.ControlUtil._
|
import gitbucket.core.util.ControlUtil._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.view
|
import gitbucket.core.view
|
||||||
import gitbucket.core.view.Markdown
|
import gitbucket.core.view.Markdown
|
||||||
|
|
||||||
import io.github.gitbucket.scalatra.forms._
|
import io.github.gitbucket.scalatra.forms._
|
||||||
import org.scalatra.Ok
|
import org.scalatra.Ok
|
||||||
|
|
||||||
|
|
||||||
class IssuesController extends IssuesControllerBase
|
class IssuesController extends IssuesControllerBase
|
||||||
with IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService
|
with IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService
|
||||||
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with WebHookIssueCommentService
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with PullRequestService with WebHookIssueCommentService
|
||||||
|
|
||||||
trait IssuesControllerBase extends ControllerBase {
|
trait IssuesControllerBase extends ControllerBase {
|
||||||
self: IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService
|
self: IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService
|
||||||
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with WebHookIssueCommentService =>
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with PullRequestService with WebHookIssueCommentService =>
|
||||||
|
|
||||||
case class IssueCreateForm(title: String, content: Option[String],
|
case class IssueCreateForm(title: String, content: Option[String],
|
||||||
assignedUserName: Option[String], milestoneId: Option[Int], labelNames: Option[String])
|
assignedUserName: Option[String], milestoneId: Option[Int], labelNames: Option[String])
|
||||||
@@ -67,72 +67,77 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
_,
|
_,
|
||||||
getComments(owner, name, issueId.toInt),
|
getComments(owner, name, issueId.toInt),
|
||||||
getIssueLabels(owner, name, issueId.toInt),
|
getIssueLabels(owner, name, issueId.toInt),
|
||||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).exists(_.isGroupAccount)) Nil else List(owner))).sorted,
|
getAssignableUserNames(owner, name),
|
||||||
getMilestonesWithIssueCount(owner, name),
|
getMilestonesWithIssueCount(owner, name),
|
||||||
getLabels(owner, name),
|
getLabels(owner, name),
|
||||||
hasWritePermission(owner, name, context.loginAccount),
|
isEditable(repository),
|
||||||
|
isManageable(repository),
|
||||||
repository)
|
repository)
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
|
get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
if(isEditable(repository)){ // TODO Should this check is provided by authenticator?
|
||||||
html.create(
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).exists(_.isGroupAccount)) Nil else List(owner))).sorted,
|
html.create(
|
||||||
|
getAssignableUserNames(owner, name),
|
||||||
getMilestones(owner, name),
|
getMilestones(owner, name),
|
||||||
getLabels(owner, name),
|
getLabels(owner, name),
|
||||||
hasWritePermission(owner, name, context.loginAccount),
|
hasWritePermission(owner, name, context.loginAccount),
|
||||||
repository)
|
repository)
|
||||||
}
|
}
|
||||||
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
if(isEditable(repository)){ // TODO Should this check is provided by authenticator?
|
||||||
val writable = hasWritePermission(owner, name, context.loginAccount)
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
val userName = context.loginAccount.get.userName
|
val manageable = isManageable(repository)
|
||||||
|
val userName = context.loginAccount.get.userName
|
||||||
|
|
||||||
// insert issue
|
// insert issue
|
||||||
val issueId = createIssue(owner, name, userName, form.title, form.content,
|
val issueId = createIssue(owner, name, userName, form.title, form.content,
|
||||||
if(writable) form.assignedUserName else None,
|
if (manageable) form.assignedUserName else None,
|
||||||
if(writable) form.milestoneId else None)
|
if (manageable) form.milestoneId else None)
|
||||||
|
|
||||||
// insert labels
|
// insert labels
|
||||||
if(writable){
|
if (manageable) {
|
||||||
form.labelNames.map { value =>
|
form.labelNames.map { value =>
|
||||||
val labels = getLabels(owner, name)
|
val labels = getLabels(owner, name)
|
||||||
value.split(",").foreach { labelName =>
|
value.split(",").foreach { labelName =>
|
||||||
labels.find(_.labelName == labelName).map { label =>
|
labels.find(_.labelName == labelName).map { label =>
|
||||||
registerIssueLabel(owner, name, issueId, label.labelId)
|
registerIssueLabel(owner, name, issueId, label.labelId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// record activity
|
// record activity
|
||||||
recordCreateIssueActivity(owner, name, userName, issueId, form.title)
|
recordCreateIssueActivity(owner, name, userName, issueId, form.title)
|
||||||
|
|
||||||
getIssue(owner, name, issueId.toString).foreach { issue =>
|
getIssue(owner, name, issueId.toString).foreach { issue =>
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""), context.loginAccount.get)
|
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""), context.loginAccount.get)
|
||||||
|
|
||||||
// call web hooks
|
// call web hooks
|
||||||
callIssuesWebHook("opened", repository, issue, context.baseUrl, context.loginAccount.get)
|
callIssuesWebHook("opened", repository, issue, context.baseUrl, context.loginAccount.get)
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
Notifier().toNotify(repository, issue, form.content.getOrElse("")){
|
Notifier().toNotify(repository, issue, form.content.getOrElse("")) {
|
||||||
Notifier.msgIssue(s"${context.baseUrl}/${owner}/${name}/issues/${issueId}")
|
Notifier.msgIssue(s"${context.baseUrl}/${owner}/${name}/issues/${issueId}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
redirect(s"/${owner}/${name}/issues/${issueId}")
|
redirect(s"/${owner}/${name}/issues/${issueId}")
|
||||||
}
|
}
|
||||||
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/edit_title/:id", issueTitleEditForm)(readableUsersOnly { (title, repository) =>
|
ajaxPost("/:owner/:repository/issues/edit_title/:id", issueTitleEditForm)(readableUsersOnly { (title, repository) =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
getIssue(owner, name, params("id")).map { issue =>
|
getIssue(owner, name, params("id")).map { issue =>
|
||||||
if(isEditable(owner, name, issue.openedUserName)){
|
if(isEditableContent(owner, name, issue.openedUserName)){
|
||||||
// update issue
|
// update issue
|
||||||
updateIssue(owner, name, issue.issueId, title, issue.content)
|
updateIssue(owner, name, issue.issueId, title, issue.content)
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
@@ -147,7 +152,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (content, repository) =>
|
ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (content, repository) =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
getIssue(owner, name, params("id")).map { issue =>
|
getIssue(owner, name, params("id")).map { issue =>
|
||||||
if(isEditable(owner, name, issue.openedUserName)){
|
if(isEditableContent(owner, name, issue.openedUserName)){
|
||||||
// update issue
|
// update issue
|
||||||
updateIssue(owner, name, issue.issueId, issue.title, content)
|
updateIssue(owner, name, issue.issueId, issue.title, content)
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
@@ -161,7 +166,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||||
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
||||||
val actionOpt = params.get("action").filter(_ => isEditable(issue.userName, issue.repositoryName, issue.openedUserName))
|
val actionOpt = params.get("action").filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||||
handleComment(issue, Some(form.content), repository, actionOpt) map { case (issue, id) =>
|
handleComment(issue, Some(form.content), repository, actionOpt) map { case (issue, id) =>
|
||||||
redirect(s"/${repository.owner}/${repository.name}/${
|
redirect(s"/${repository.owner}/${repository.name}/${
|
||||||
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
||||||
@@ -171,7 +176,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) =>
|
||||||
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
||||||
val actionOpt = params.get("action").filter(_ => isEditable(issue.userName, issue.repositoryName, issue.openedUserName))
|
val actionOpt = params.get("action").filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||||
handleComment(issue, form.content, repository, actionOpt) map { case (issue, id) =>
|
handleComment(issue, form.content, repository, actionOpt) map { case (issue, id) =>
|
||||||
redirect(s"/${repository.owner}/${repository.name}/${
|
redirect(s"/${repository.owner}/${repository.name}/${
|
||||||
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
||||||
@@ -182,7 +187,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
ajaxPost("/:owner/:repository/issue_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) =>
|
ajaxPost("/:owner/:repository/issue_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
getComment(owner, name, params("id")).map { comment =>
|
getComment(owner, name, params("id")).map { comment =>
|
||||||
if(isEditable(owner, name, comment.commentedUserName)){
|
if(isEditableContent(owner, name, comment.commentedUserName)){
|
||||||
updateComment(comment.commentId, form.content)
|
updateComment(comment.commentId, form.content)
|
||||||
redirect(s"/${owner}/${name}/issue_comments/_data/${comment.commentId}")
|
redirect(s"/${owner}/${name}/issue_comments/_data/${comment.commentId}")
|
||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
@@ -193,7 +198,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
ajaxPost("/:owner/:repository/issue_comments/delete/:id")(readableUsersOnly { repository =>
|
ajaxPost("/:owner/:repository/issue_comments/delete/:id")(readableUsersOnly { repository =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
getComment(owner, name, params("id")).map { comment =>
|
getComment(owner, name, params("id")).map { comment =>
|
||||||
if(isEditable(owner, name, comment.commentedUserName)){
|
if(isEditableContent(owner, name, comment.commentedUserName)){
|
||||||
Ok(deleteComment(comment.commentId))
|
Ok(deleteComment(comment.commentId))
|
||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -202,7 +207,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
ajaxGet("/:owner/:repository/issues/_data/:id")(readableUsersOnly { repository =>
|
ajaxGet("/:owner/:repository/issues/_data/:id")(readableUsersOnly { repository =>
|
||||||
getIssue(repository.owner, repository.name, params("id")) map { x =>
|
getIssue(repository.owner, repository.name, params("id")) map { x =>
|
||||||
if(isEditable(x.userName, x.repositoryName, x.openedUserName)){
|
if(isEditableContent(x.userName, x.repositoryName, x.openedUserName)){
|
||||||
params.get("dataType") collect {
|
params.get("dataType") collect {
|
||||||
case t if t == "html" => html.editissue(x.content, x.issueId, repository)
|
case t if t == "html" => html.editissue(x.content, x.issueId, repository)
|
||||||
} getOrElse {
|
} getOrElse {
|
||||||
@@ -218,7 +223,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
enableAnchor = true,
|
enableAnchor = true,
|
||||||
enableLineBreaks = true,
|
enableLineBreaks = true,
|
||||||
enableTaskList = true,
|
enableTaskList = true,
|
||||||
hasWritePermission = isEditable(x.userName, x.repositoryName, x.openedUserName)
|
hasWritePermission = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -229,7 +234,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
ajaxGet("/:owner/:repository/issue_comments/_data/:id")(readableUsersOnly { repository =>
|
ajaxGet("/:owner/:repository/issue_comments/_data/:id")(readableUsersOnly { repository =>
|
||||||
getComment(repository.owner, repository.name, params("id")) map { x =>
|
getComment(repository.owner, repository.name, params("id")) map { x =>
|
||||||
if(isEditable(x.userName, x.repositoryName, x.commentedUserName)){
|
if(isEditableContent(x.userName, x.repositoryName, x.commentedUserName)){
|
||||||
params.get("dataType") collect {
|
params.get("dataType") collect {
|
||||||
case t if t == "html" => html.editcomment(x.content, x.commentId, repository)
|
case t if t == "html" => html.editcomment(x.content, x.commentId, repository)
|
||||||
} getOrElse {
|
} getOrElse {
|
||||||
@@ -244,7 +249,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
enableAnchor = true,
|
enableAnchor = true,
|
||||||
enableLineBreaks = true,
|
enableLineBreaks = true,
|
||||||
enableTaskList = true,
|
enableTaskList = true,
|
||||||
hasWritePermission = isEditable(x.userName, x.repositoryName, x.commentedUserName)
|
hasWritePermission = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -253,32 +258,32 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/new/label")(collaboratorsOnly { repository =>
|
ajaxPost("/:owner/:repository/issues/new/label")(writableUsersOnly { repository =>
|
||||||
val labelNames = params("labelNames").split(",")
|
val labelNames = params("labelNames").split(",")
|
||||||
val labels = getLabels(repository.owner, repository.name).filter(x => labelNames.contains(x.labelName))
|
val labels = getLabels(repository.owner, repository.name).filter(x => labelNames.contains(x.labelName))
|
||||||
html.labellist(labels)
|
html.labellist(labels)
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/:id/label/new")(collaboratorsOnly { repository =>
|
ajaxPost("/:owner/:repository/issues/:id/label/new")(writableUsersOnly { repository =>
|
||||||
defining(params("id").toInt){ issueId =>
|
defining(params("id").toInt){ issueId =>
|
||||||
registerIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt)
|
registerIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt)
|
||||||
html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
|
html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/:id/label/delete")(collaboratorsOnly { repository =>
|
ajaxPost("/:owner/:repository/issues/:id/label/delete")(writableUsersOnly { repository =>
|
||||||
defining(params("id").toInt){ issueId =>
|
defining(params("id").toInt){ issueId =>
|
||||||
deleteIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt)
|
deleteIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt)
|
||||||
html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
|
html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/:id/assign")(collaboratorsOnly { repository =>
|
ajaxPost("/:owner/:repository/issues/:id/assign")(writableUsersOnly { repository =>
|
||||||
updateAssignedUserName(repository.owner, repository.name, params("id").toInt, assignedUserName("assignedUserName"))
|
updateAssignedUserName(repository.owner, repository.name, params("id").toInt, assignedUserName("assignedUserName"))
|
||||||
Ok("updated")
|
Ok("updated")
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/:id/milestone")(collaboratorsOnly { repository =>
|
ajaxPost("/:owner/:repository/issues/:id/milestone")(writableUsersOnly { repository =>
|
||||||
updateMilestoneId(repository.owner, repository.name, params("id").toInt, milestoneId("milestoneId"))
|
updateMilestoneId(repository.owner, repository.name, params("id").toInt, milestoneId("milestoneId"))
|
||||||
milestoneId("milestoneId").map { milestoneId =>
|
milestoneId("milestoneId").map { milestoneId =>
|
||||||
getMilestonesWithIssueCount(repository.owner, repository.name)
|
getMilestonesWithIssueCount(repository.owner, repository.name)
|
||||||
@@ -288,7 +293,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
} getOrElse Ok()
|
} getOrElse Ok()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issues/batchedit/state")(collaboratorsOnly { repository =>
|
post("/:owner/:repository/issues/batchedit/state")(writableUsersOnly { repository =>
|
||||||
defining(params.get("value")){ action =>
|
defining(params.get("value")){ action =>
|
||||||
action match {
|
action match {
|
||||||
case Some("open") => executeBatch(repository) { issueId =>
|
case Some("open") => executeBatch(repository) { issueId =>
|
||||||
@@ -306,7 +311,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issues/batchedit/label")(collaboratorsOnly { repository =>
|
post("/:owner/:repository/issues/batchedit/label")(writableUsersOnly { repository =>
|
||||||
params("value").toIntOpt.map{ labelId =>
|
params("value").toIntOpt.map{ labelId =>
|
||||||
executeBatch(repository) { issueId =>
|
executeBatch(repository) { issueId =>
|
||||||
getIssueLabel(repository.owner, repository.name, issueId, labelId) getOrElse {
|
getIssueLabel(repository.owner, repository.name, issueId, labelId) getOrElse {
|
||||||
@@ -316,7 +321,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issues/batchedit/assign")(collaboratorsOnly { repository =>
|
post("/:owner/:repository/issues/batchedit/assign")(writableUsersOnly { repository =>
|
||||||
defining(assignedUserName("value")){ value =>
|
defining(assignedUserName("value")){ value =>
|
||||||
executeBatch(repository) {
|
executeBatch(repository) {
|
||||||
updateAssignedUserName(repository.owner, repository.name, _, value)
|
updateAssignedUserName(repository.owner, repository.name, _, value)
|
||||||
@@ -324,7 +329,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issues/batchedit/milestone")(collaboratorsOnly { repository =>
|
post("/:owner/:repository/issues/batchedit/milestone")(writableUsersOnly { repository =>
|
||||||
defining(milestoneId("value")){ value =>
|
defining(milestoneId("value")){ value =>
|
||||||
executeBatch(repository) {
|
executeBatch(repository) {
|
||||||
updateMilestoneId(repository.owner, repository.name, _, value)
|
updateMilestoneId(repository.owner, repository.name, _, value)
|
||||||
@@ -346,9 +351,6 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
val assignedUserName = (key: String) => params.get(key) filter (_.trim != "")
|
val assignedUserName = (key: String) => params.get(key) filter (_.trim != "")
|
||||||
val milestoneId: String => Option[Int] = (key: String) => params.get(key).flatMap(_.toIntOpt)
|
val milestoneId: String => Option[Int] = (key: String) => params.get(key).flatMap(_.toIntOpt)
|
||||||
|
|
||||||
private def isEditable(owner: String, repository: String, author: String)(implicit context: Context): Boolean =
|
|
||||||
hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
|
|
||||||
|
|
||||||
private def executeBatch(repository: RepositoryService.RepositoryInfo)(execute: Int => Unit) = {
|
private def executeBatch(repository: RepositoryService.RepositoryInfo)(execute: Int => Unit) = {
|
||||||
params("checked").split(',') map(_.toInt) foreach execute
|
params("checked").split(',') map(_.toInt) foreach execute
|
||||||
params("from") match {
|
params("from") match {
|
||||||
@@ -359,8 +361,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
private def searchIssues(repository: RepositoryService.RepositoryInfo) = {
|
private def searchIssues(repository: RepositoryService.RepositoryInfo) = {
|
||||||
defining(repository.owner, repository.name){ case (owner, repoName) =>
|
defining(repository.owner, repository.name){ case (owner, repoName) =>
|
||||||
val page = IssueSearchCondition.page(request)
|
val page = IssueSearchCondition.page(request)
|
||||||
val sessionKey = Keys.Session.Issues(owner, repoName)
|
|
||||||
|
|
||||||
// retrieve search condition
|
// retrieve search condition
|
||||||
val condition = IssueSearchCondition(request)
|
val condition = IssueSearchCondition(request)
|
||||||
@@ -369,18 +370,41 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
"issues",
|
"issues",
|
||||||
searchIssue(condition, false, (page - 1) * IssueLimit, IssueLimit, owner -> repoName),
|
searchIssue(condition, false, (page - 1) * IssueLimit, IssueLimit, owner -> repoName),
|
||||||
page,
|
page,
|
||||||
if(!getAccountByUserName(owner).exists(_.isGroupAccount)){
|
getAssignableUserNames(owner, repoName),
|
||||||
(getCollaborators(owner, repoName) :+ owner).sorted
|
|
||||||
} else {
|
|
||||||
getCollaborators(owner, repoName)
|
|
||||||
},
|
|
||||||
getMilestones(owner, repoName),
|
getMilestones(owner, repoName),
|
||||||
getLabels(owner, repoName),
|
getLabels(owner, repoName),
|
||||||
countIssue(condition.copy(state = "open" ), false, owner -> repoName),
|
countIssue(condition.copy(state = "open" ), false, owner -> repoName),
|
||||||
countIssue(condition.copy(state = "closed"), false, owner -> repoName),
|
countIssue(condition.copy(state = "closed"), false, owner -> repoName),
|
||||||
condition,
|
condition,
|
||||||
repository,
|
repository,
|
||||||
hasWritePermission(owner, repoName, context.loginAccount))
|
isEditable(repository),
|
||||||
|
isManageable(repository))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether an logged-in user can manage issues.
|
||||||
|
*/
|
||||||
|
private def isManageable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
|
||||||
|
hasWritePermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether an logged-in user can post issues.
|
||||||
|
*/
|
||||||
|
private def isEditable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
|
||||||
|
repository.repository.options.issuesOption match {
|
||||||
|
case "PUBLIC" => hasReadPermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
case "PRIVATE" => hasWritePermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
case "DISABLE" => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether an issue or a comment is editable by a logged-in user.
|
||||||
|
*/
|
||||||
|
private def isEditableContent(owner: String, repository: String, author: String)(implicit context: Context): Boolean = {
|
||||||
|
hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import gitbucket.core.issues.labels.html
|
import gitbucket.core.issues.labels.html
|
||||||
import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, LabelsService}
|
import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, LabelsService}
|
||||||
import gitbucket.core.util.{ReferrerAuthenticator, CollaboratorsAuthenticator}
|
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import io.github.gitbucket.scalatra.forms._
|
import io.github.gitbucket.scalatra.forms._
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
@@ -10,11 +10,11 @@ import org.scalatra.Ok
|
|||||||
|
|
||||||
class LabelsController extends LabelsControllerBase
|
class LabelsController extends LabelsControllerBase
|
||||||
with LabelsService with IssuesService with RepositoryService with AccountService
|
with LabelsService with IssuesService with RepositoryService with AccountService
|
||||||
with ReferrerAuthenticator with CollaboratorsAuthenticator
|
with ReferrerAuthenticator with WritableUsersAuthenticator
|
||||||
|
|
||||||
trait LabelsControllerBase extends ControllerBase {
|
trait LabelsControllerBase extends ControllerBase {
|
||||||
self: LabelsService with IssuesService with RepositoryService
|
self: LabelsService with IssuesService with RepositoryService
|
||||||
with ReferrerAuthenticator with CollaboratorsAuthenticator =>
|
with ReferrerAuthenticator with WritableUsersAuthenticator =>
|
||||||
|
|
||||||
case class LabelForm(labelName: String, color: String)
|
case class LabelForm(labelName: String, color: String)
|
||||||
|
|
||||||
@@ -32,11 +32,11 @@ trait LabelsControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxGet("/:owner/:repository/issues/labels/new")(collaboratorsOnly { repository =>
|
ajaxGet("/:owner/:repository/issues/labels/new")(writableUsersOnly { repository =>
|
||||||
html.edit(None, repository)
|
html.edit(None, repository)
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/labels/new", labelForm)(collaboratorsOnly { (form, repository) =>
|
ajaxPost("/:owner/:repository/issues/labels/new", labelForm)(writableUsersOnly { (form, repository) =>
|
||||||
val labelId = createLabel(repository.owner, repository.name, form.labelName, form.color.substring(1))
|
val labelId = createLabel(repository.owner, repository.name, form.labelName, form.color.substring(1))
|
||||||
html.label(
|
html.label(
|
||||||
getLabel(repository.owner, repository.name, labelId).get,
|
getLabel(repository.owner, repository.name, labelId).get,
|
||||||
@@ -46,13 +46,13 @@ trait LabelsControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxGet("/:owner/:repository/issues/labels/:labelId/edit")(collaboratorsOnly { repository =>
|
ajaxGet("/:owner/:repository/issues/labels/:labelId/edit")(writableUsersOnly { repository =>
|
||||||
getLabel(repository.owner, repository.name, params("labelId").toInt).map { label =>
|
getLabel(repository.owner, repository.name, params("labelId").toInt).map { label =>
|
||||||
html.edit(Some(label), repository)
|
html.edit(Some(label), repository)
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/labels/:labelId/edit", labelForm)(collaboratorsOnly { (form, repository) =>
|
ajaxPost("/:owner/:repository/issues/labels/:labelId/edit", labelForm)(writableUsersOnly { (form, repository) =>
|
||||||
updateLabel(repository.owner, repository.name, params("labelId").toInt, form.labelName, form.color.substring(1))
|
updateLabel(repository.owner, repository.name, params("labelId").toInt, form.labelName, form.color.substring(1))
|
||||||
html.label(
|
html.label(
|
||||||
getLabel(repository.owner, repository.name, params("labelId").toInt).get,
|
getLabel(repository.owner, repository.name, params("labelId").toInt).get,
|
||||||
@@ -62,7 +62,7 @@ trait LabelsControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/labels/:labelId/delete")(collaboratorsOnly { repository =>
|
ajaxPost("/:owner/:repository/issues/labels/:labelId/delete")(writableUsersOnly { repository =>
|
||||||
deleteLabel(repository.owner, repository.name, params("labelId").toInt)
|
deleteLabel(repository.owner, repository.name, params("labelId").toInt)
|
||||||
Ok()
|
Ok()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import gitbucket.core.issues.milestones.html
|
import gitbucket.core.issues.milestones.html
|
||||||
import gitbucket.core.service.{RepositoryService, MilestonesService, AccountService}
|
import gitbucket.core.service.{RepositoryService, MilestonesService, AccountService}
|
||||||
import gitbucket.core.util.{ReferrerAuthenticator, CollaboratorsAuthenticator}
|
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import io.github.gitbucket.scalatra.forms._
|
import io.github.gitbucket.scalatra.forms._
|
||||||
|
|
||||||
class MilestonesController extends MilestonesControllerBase
|
class MilestonesController extends MilestonesControllerBase
|
||||||
with MilestonesService with RepositoryService with AccountService
|
with MilestonesService with RepositoryService with AccountService
|
||||||
with ReferrerAuthenticator with CollaboratorsAuthenticator
|
with ReferrerAuthenticator with WritableUsersAuthenticator
|
||||||
|
|
||||||
trait MilestonesControllerBase extends ControllerBase {
|
trait MilestonesControllerBase extends ControllerBase {
|
||||||
self: MilestonesService with RepositoryService
|
self: MilestonesService with RepositoryService
|
||||||
with ReferrerAuthenticator with CollaboratorsAuthenticator =>
|
with ReferrerAuthenticator with WritableUsersAuthenticator =>
|
||||||
|
|
||||||
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
||||||
|
|
||||||
@@ -30,22 +30,22 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/issues/milestones/new")(collaboratorsOnly {
|
get("/:owner/:repository/issues/milestones/new")(writableUsersOnly {
|
||||||
html.edit(None, _)
|
html.edit(None, _)
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issues/milestones/new", milestoneForm)(collaboratorsOnly { (form, repository) =>
|
post("/:owner/:repository/issues/milestones/new", milestoneForm)(writableUsersOnly { (form, repository) =>
|
||||||
createMilestone(repository.owner, repository.name, form.title, form.description, form.dueDate)
|
createMilestone(repository.owner, repository.name, form.title, form.description, form.dueDate)
|
||||||
redirect(s"/${repository.owner}/${repository.name}/issues/milestones")
|
redirect(s"/${repository.owner}/${repository.name}/issues/milestones")
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/issues/milestones/:milestoneId/edit")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/issues/milestones/:milestoneId/edit")(writableUsersOnly { repository =>
|
||||||
params("milestoneId").toIntOpt.map{ milestoneId =>
|
params("milestoneId").toIntOpt.map{ milestoneId =>
|
||||||
html.edit(getMilestone(repository.owner, repository.name, milestoneId), repository)
|
html.edit(getMilestone(repository.owner, repository.name, milestoneId), repository)
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issues/milestones/:milestoneId/edit", milestoneForm)(collaboratorsOnly { (form, repository) =>
|
post("/:owner/:repository/issues/milestones/:milestoneId/edit", milestoneForm)(writableUsersOnly { (form, repository) =>
|
||||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||||
updateMilestone(milestone.copy(title = form.title, description = form.description, dueDate = form.dueDate))
|
updateMilestone(milestone.copy(title = form.title, description = form.description, dueDate = form.dueDate))
|
||||||
@@ -54,7 +54,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/issues/milestones/:milestoneId/close")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/issues/milestones/:milestoneId/close")(writableUsersOnly { repository =>
|
||||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||||
closeMilestone(milestone)
|
closeMilestone(milestone)
|
||||||
@@ -63,7 +63,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/issues/milestones/:milestoneId/open")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/issues/milestones/:milestoneId/open")(writableUsersOnly { repository =>
|
||||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||||
openMilestone(milestone)
|
openMilestone(milestone)
|
||||||
@@ -72,7 +72,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/issues/milestones/:milestoneId/delete")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/issues/milestones/:milestoneId/delete")(writableUsersOnly { repository =>
|
||||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||||
deleteMilestone(repository.owner, repository.name, milestone.milestoneId)
|
deleteMilestone(repository.owner, repository.name, milestone.milestoneId)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import gitbucket.core.service.CommitStatusService
|
|||||||
import gitbucket.core.service.MergeService
|
import gitbucket.core.service.MergeService
|
||||||
import gitbucket.core.service.IssuesService._
|
import gitbucket.core.service.IssuesService._
|
||||||
import gitbucket.core.service.PullRequestService._
|
import gitbucket.core.service.PullRequestService._
|
||||||
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.service._
|
import gitbucket.core.service._
|
||||||
import gitbucket.core.util.ControlUtil._
|
import gitbucket.core.util.ControlUtil._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
@@ -14,28 +15,26 @@ import gitbucket.core.util.JGitUtil._
|
|||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.view
|
import gitbucket.core.view
|
||||||
import gitbucket.core.view.helpers
|
import gitbucket.core.view.helpers
|
||||||
|
|
||||||
import io.github.gitbucket.scalatra.forms._
|
import io.github.gitbucket.scalatra.forms._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.PersonIdent
|
import org.eclipse.jgit.lib.PersonIdent
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
|
||||||
|
|
||||||
class PullRequestsController extends PullRequestsControllerBase
|
class PullRequestsController extends PullRequestsControllerBase
|
||||||
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService
|
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService
|
||||||
with CommitsService with ActivityService with WebHookPullRequestService with ReferrerAuthenticator with CollaboratorsAuthenticator
|
with CommitsService with ActivityService with WebHookPullRequestService
|
||||||
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator
|
||||||
with CommitStatusService with MergeService with ProtectedBranchService
|
with CommitStatusService with MergeService with ProtectedBranchService
|
||||||
|
|
||||||
|
|
||||||
trait PullRequestsControllerBase extends ControllerBase {
|
trait PullRequestsControllerBase extends ControllerBase {
|
||||||
self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService
|
self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService
|
||||||
with CommitsService with ActivityService with PullRequestService with WebHookPullRequestService with ReferrerAuthenticator with CollaboratorsAuthenticator
|
with CommitsService with ActivityService with PullRequestService with WebHookPullRequestService
|
||||||
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator
|
||||||
with CommitStatusService with MergeService with ProtectedBranchService =>
|
with CommitStatusService with MergeService with ProtectedBranchService =>
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[PullRequestsControllerBase])
|
|
||||||
|
|
||||||
val pullRequestForm = mapping(
|
val pullRequestForm = mapping(
|
||||||
"title" -> trim(label("Title" , text(required, maxlength(100)))),
|
"title" -> trim(label("Title" , text(required, maxlength(100)))),
|
||||||
"content" -> trim(label("Content", optional(text()))),
|
"content" -> trim(label("Content", optional(text()))),
|
||||||
@@ -94,12 +93,13 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
(commits.flatten.map(commit => getCommitComments(owner, name, commit.id, true)).flatten.toList ::: getComments(owner, name, issueId))
|
(commits.flatten.map(commit => getCommitComments(owner, name, commit.id, true)).flatten.toList ::: getComments(owner, name, issueId))
|
||||||
.sortWith((a, b) => a.registeredDate before b.registeredDate),
|
.sortWith((a, b) => a.registeredDate before b.registeredDate),
|
||||||
getIssueLabels(owner, name, issueId),
|
getIssueLabels(owner, name, issueId),
|
||||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
getAssignableUserNames(owner, name),
|
||||||
getMilestonesWithIssueCount(owner, name),
|
getMilestonesWithIssueCount(owner, name),
|
||||||
getLabels(owner, name),
|
getLabels(owner, name),
|
||||||
commits,
|
commits,
|
||||||
diffs,
|
diffs,
|
||||||
hasWritePermission(owner, name, context.loginAccount),
|
isEditable(repository),
|
||||||
|
isManageable(repository),
|
||||||
repository,
|
repository,
|
||||||
flash.toMap.map(f => f._1 -> f._2.toString))
|
flash.toMap.map(f => f._1 -> f._2.toString))
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/pull/:id/delete/*")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/pull/:id/delete/*")(writableUsersOnly { repository =>
|
||||||
params("id").toIntOpt.map { issueId =>
|
params("id").toIntOpt.map { issueId =>
|
||||||
val branchName = multiParams("splat").head
|
val branchName = multiParams("splat").head
|
||||||
val userName = context.loginAccount.get.userName
|
val userName = context.loginAccount.get.userName
|
||||||
@@ -225,7 +225,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/pull/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) =>
|
post("/:owner/:repository/pull/:id/merge", mergeForm)(writableUsersOnly { (form, repository) =>
|
||||||
params("id").toIntOpt.flatMap { issueId =>
|
params("id").toIntOpt.flatMap { issueId =>
|
||||||
val owner = repository.owner
|
val owner = repository.owner
|
||||||
val name = repository.name
|
val name = repository.name
|
||||||
@@ -375,7 +375,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
originRepository,
|
originRepository,
|
||||||
forkedRepository,
|
forkedRepository,
|
||||||
hasWritePermission(originRepository.owner, originRepository.name, context.loginAccount),
|
hasWritePermission(originRepository.owner, originRepository.name, context.loginAccount),
|
||||||
(getCollaborators(originRepository.owner, originRepository.name) ::: (if(getAccountByUserName(originRepository.owner).get.isGroupAccount) Nil else List(originRepository.owner))).sorted,
|
getAssignableUserNames(originRepository.owner, originRepository.name),
|
||||||
getMilestones(originRepository.owner, originRepository.name),
|
getMilestones(originRepository.owner, originRepository.name),
|
||||||
getLabels(originRepository.owner, originRepository.name)
|
getLabels(originRepository.owner, originRepository.name)
|
||||||
)
|
)
|
||||||
@@ -389,7 +389,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
ajaxGet("/:owner/:repository/compare/*...*/mergecheck")(collaboratorsOnly { forkedRepository =>
|
ajaxGet("/:owner/:repository/compare/*...*/mergecheck")(writableUsersOnly { forkedRepository =>
|
||||||
val Seq(origin, forked) = multiParams("splat")
|
val Seq(origin, forked) = multiParams("splat")
|
||||||
val (originOwner, tmpOriginBranch) = parseCompareIdentifie(origin, forkedRepository.owner)
|
val (originOwner, tmpOriginBranch) = parseCompareIdentifie(origin, forkedRepository.owner)
|
||||||
val (forkedOwner, tmpForkedBranch) = parseCompareIdentifie(forked, forkedRepository.owner)
|
val (forkedOwner, tmpForkedBranch) = parseCompareIdentifie(forked, forkedRepository.owner)
|
||||||
@@ -419,64 +419,68 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/pulls/new", pullRequestForm)(referrersOnly { (form, repository) =>
|
post("/:owner/:repository/pulls/new", pullRequestForm)(readableUsersOnly { (form, repository) =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
val writable = hasWritePermission(owner, name, context.loginAccount)
|
val manageable = isManageable(repository)
|
||||||
val loginUserName = context.loginAccount.get.userName
|
val editable = isEditable(repository)
|
||||||
|
|
||||||
val issueId = createIssue(
|
if(editable) {
|
||||||
owner = repository.owner,
|
val loginUserName = context.loginAccount.get.userName
|
||||||
repository = repository.name,
|
|
||||||
loginUser = loginUserName,
|
|
||||||
title = form.title,
|
|
||||||
content = form.content,
|
|
||||||
assignedUserName = if(writable) form.assignedUserName else None,
|
|
||||||
milestoneId = if(writable) form.milestoneId else None,
|
|
||||||
isPullRequest = true)
|
|
||||||
|
|
||||||
createPullRequest(
|
val issueId = createIssue(
|
||||||
originUserName = repository.owner,
|
owner = repository.owner,
|
||||||
originRepositoryName = repository.name,
|
repository = repository.name,
|
||||||
issueId = issueId,
|
loginUser = loginUserName,
|
||||||
originBranch = form.targetBranch,
|
title = form.title,
|
||||||
requestUserName = form.requestUserName,
|
content = form.content,
|
||||||
requestRepositoryName = form.requestRepositoryName,
|
assignedUserName = if (manageable) form.assignedUserName else None,
|
||||||
requestBranch = form.requestBranch,
|
milestoneId = if (manageable) form.milestoneId else None,
|
||||||
commitIdFrom = form.commitIdFrom,
|
isPullRequest = true)
|
||||||
commitIdTo = form.commitIdTo)
|
|
||||||
|
|
||||||
// insert labels
|
createPullRequest(
|
||||||
if(writable){
|
originUserName = repository.owner,
|
||||||
form.labelNames.map { value =>
|
originRepositoryName = repository.name,
|
||||||
val labels = getLabels(owner, name)
|
issueId = issueId,
|
||||||
value.split(",").foreach { labelName =>
|
originBranch = form.targetBranch,
|
||||||
labels.find(_.labelName == labelName).map { label =>
|
requestUserName = form.requestUserName,
|
||||||
registerIssueLabel(repository.owner, repository.name, issueId, label.labelId)
|
requestRepositoryName = form.requestRepositoryName,
|
||||||
|
requestBranch = form.requestBranch,
|
||||||
|
commitIdFrom = form.commitIdFrom,
|
||||||
|
commitIdTo = form.commitIdTo)
|
||||||
|
|
||||||
|
// insert labels
|
||||||
|
if (manageable) {
|
||||||
|
form.labelNames.map { value =>
|
||||||
|
val labels = getLabels(owner, name)
|
||||||
|
value.split(",").foreach { labelName =>
|
||||||
|
labels.find(_.labelName == labelName).map { label =>
|
||||||
|
registerIssueLabel(repository.owner, repository.name, issueId, label.labelId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// fetch requested branch
|
// fetch requested branch
|
||||||
fetchAsPullRequest(owner, name, form.requestUserName, form.requestRepositoryName, form.requestBranch, issueId)
|
fetchAsPullRequest(owner, name, form.requestUserName, form.requestRepositoryName, form.requestBranch, issueId)
|
||||||
|
|
||||||
// record activity
|
// record activity
|
||||||
recordPullRequestActivity(owner, name, loginUserName, issueId, form.title)
|
recordPullRequestActivity(owner, name, loginUserName, issueId, form.title)
|
||||||
|
|
||||||
// call web hook
|
// call web hook
|
||||||
callPullRequestWebHook("opened", repository, issueId, context.baseUrl, context.loginAccount.get)
|
callPullRequestWebHook("opened", repository, issueId, context.baseUrl, context.loginAccount.get)
|
||||||
|
|
||||||
getIssue(owner, name, issueId.toString) foreach { issue =>
|
getIssue(owner, name, issueId.toString) foreach { issue =>
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""), context.loginAccount.get)
|
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""), context.loginAccount.get)
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
Notifier().toNotify(repository, issue, form.content.getOrElse("")){
|
Notifier().toNotify(repository, issue, form.content.getOrElse("")) {
|
||||||
Notifier.msgPullRequest(s"${context.baseUrl}/${owner}/${name}/pull/${issueId}")
|
Notifier.msgPullRequest(s"${context.baseUrl}/${owner}/${name}/pull/${issueId}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
redirect(s"/${owner}/${name}/pull/${issueId}")
|
redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||||
|
} else Unauthorized()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -516,8 +520,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
private def searchPullRequests(userName: Option[String], repository: RepositoryService.RepositoryInfo) =
|
private def searchPullRequests(userName: Option[String], repository: RepositoryService.RepositoryInfo) =
|
||||||
defining(repository.owner, repository.name){ case (owner, repoName) =>
|
defining(repository.owner, repository.name){ case (owner, repoName) =>
|
||||||
val page = IssueSearchCondition.page(request)
|
val page = IssueSearchCondition.page(request)
|
||||||
val sessionKey = Keys.Session.Pulls(owner, repoName)
|
|
||||||
|
|
||||||
// retrieve search condition
|
// retrieve search condition
|
||||||
val condition = IssueSearchCondition(request)
|
val condition = IssueSearchCondition(request)
|
||||||
@@ -526,18 +529,33 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
"pulls",
|
"pulls",
|
||||||
searchIssue(condition, true, (page - 1) * PullRequestLimit, PullRequestLimit, owner -> repoName),
|
searchIssue(condition, true, (page - 1) * PullRequestLimit, PullRequestLimit, owner -> repoName),
|
||||||
page,
|
page,
|
||||||
if(!getAccountByUserName(owner).exists(_.isGroupAccount)){
|
getAssignableUserNames(owner, repoName),
|
||||||
(getCollaborators(owner, repoName) :+ owner).sorted
|
|
||||||
} else {
|
|
||||||
getCollaborators(owner, repoName)
|
|
||||||
},
|
|
||||||
getMilestones(owner, repoName),
|
getMilestones(owner, repoName),
|
||||||
getLabels(owner, repoName),
|
getLabels(owner, repoName),
|
||||||
countIssue(condition.copy(state = "open" ), true, owner -> repoName),
|
countIssue(condition.copy(state = "open" ), true, owner -> repoName),
|
||||||
countIssue(condition.copy(state = "closed"), true, owner -> repoName),
|
countIssue(condition.copy(state = "closed"), true, owner -> repoName),
|
||||||
condition,
|
condition,
|
||||||
repository,
|
repository,
|
||||||
hasWritePermission(owner, repoName, context.loginAccount))
|
isEditable(repository),
|
||||||
|
isManageable(repository))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether an logged-in user can manage pull requests.
|
||||||
|
*/
|
||||||
|
private def isManageable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
|
||||||
|
hasWritePermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether an logged-in user can post pull requests.
|
||||||
|
*/
|
||||||
|
private def isEditable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
|
||||||
|
repository.repository.options.issuesOption match {
|
||||||
|
case "PUBLIC" => hasReadPermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
case "PRIVATE" => hasWritePermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
case "DISABLE" => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,9 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
repositoryName: String,
|
repositoryName: String,
|
||||||
description: Option[String],
|
description: Option[String],
|
||||||
isPrivate: Boolean,
|
isPrivate: Boolean,
|
||||||
enableIssues: Boolean,
|
issuesOption: String,
|
||||||
externalIssuesUrl: Option[String],
|
externalIssuesUrl: Option[String],
|
||||||
enableWiki: Boolean,
|
wikiOption: String,
|
||||||
allowWikiEditing: Boolean,
|
|
||||||
externalWikiUrl: Option[String],
|
externalWikiUrl: Option[String],
|
||||||
allowFork: Boolean
|
allowFork: Boolean
|
||||||
)
|
)
|
||||||
@@ -43,10 +42,9 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
"repositoryName" -> trim(label("Repository Name" , text(required, maxlength(40), identifier, renameRepositoryName))),
|
"repositoryName" -> trim(label("Repository Name" , text(required, maxlength(40), identifier, renameRepositoryName))),
|
||||||
"description" -> trim(label("Description" , optional(text()))),
|
"description" -> trim(label("Description" , optional(text()))),
|
||||||
"isPrivate" -> trim(label("Repository Type" , boolean())),
|
"isPrivate" -> trim(label("Repository Type" , boolean())),
|
||||||
"enableIssues" -> trim(label("Enable Issues" , boolean())),
|
"issuesOption" -> trim(label("Issues Option" , text(required, featureOption))),
|
||||||
"externalIssuesUrl" -> trim(label("External Issues URL", optional(text(maxlength(200))))),
|
"externalIssuesUrl" -> trim(label("External Issues URL", optional(text(maxlength(200))))),
|
||||||
"enableWiki" -> trim(label("Enable Wiki" , boolean())),
|
"wikiOption" -> trim(label("Wiki Option" , text(required, featureOption))),
|
||||||
"allowWikiEditing" -> trim(label("Allow Wiki Editing" , boolean())),
|
|
||||||
"externalWikiUrl" -> trim(label("External Wiki URL" , optional(text(maxlength(200))))),
|
"externalWikiUrl" -> trim(label("External Wiki URL" , optional(text(maxlength(200))))),
|
||||||
"allowFork" -> trim(label("Allow Forking" , boolean()))
|
"allowFork" -> trim(label("Allow Forking" , boolean()))
|
||||||
)(OptionsForm.apply)
|
)(OptionsForm.apply)
|
||||||
@@ -58,12 +56,12 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
"defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100))))
|
"defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100))))
|
||||||
)(DefaultBranchForm.apply)
|
)(DefaultBranchForm.apply)
|
||||||
|
|
||||||
// for collaborator addition
|
// // for collaborator addition
|
||||||
case class CollaboratorForm(userName: String)
|
// case class CollaboratorForm(userName: String)
|
||||||
|
//
|
||||||
val collaboratorForm = mapping(
|
// val collaboratorForm = mapping(
|
||||||
"userName" -> trim(label("Username", text(required, collaborator)))
|
// "userName" -> trim(label("Username", text(required, collaborator)))
|
||||||
)(CollaboratorForm.apply)
|
// )(CollaboratorForm.apply)
|
||||||
|
|
||||||
// for web hook url addition
|
// for web hook url addition
|
||||||
case class WebHookForm(url: String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])
|
case class WebHookForm(url: String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])
|
||||||
@@ -109,10 +107,9 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
repository.repository.parentUserName.map { _ =>
|
repository.repository.parentUserName.map { _ =>
|
||||||
repository.repository.isPrivate
|
repository.repository.isPrivate
|
||||||
} getOrElse form.isPrivate,
|
} getOrElse form.isPrivate,
|
||||||
form.enableIssues,
|
form.issuesOption,
|
||||||
form.externalIssuesUrl,
|
form.externalIssuesUrl,
|
||||||
form.enableWiki,
|
form.wikiOption,
|
||||||
form.allowWikiEditing,
|
|
||||||
form.externalWikiUrl,
|
form.externalWikiUrl,
|
||||||
form.allowFork
|
form.allowFork
|
||||||
)
|
)
|
||||||
@@ -178,22 +175,12 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
repository)
|
repository)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
post("/:owner/:repository/settings/collaborators")(ownerOnly { repository =>
|
||||||
* Add the collaborator.
|
val collaborators = params("collaborators")
|
||||||
*/
|
removeCollaborators(repository.owner, repository.name)
|
||||||
post("/:owner/:repository/settings/collaborators/add", collaboratorForm)(ownerOnly { (form, repository) =>
|
collaborators.split(",").withFilter(_.nonEmpty).map { collaborator =>
|
||||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
val userName :: permission :: Nil = collaborator.split(":").toList
|
||||||
addCollaborator(repository.owner, repository.name, form.userName)
|
addCollaborator(repository.owner, repository.name, userName, permission)
|
||||||
}
|
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the collaborator.
|
|
||||||
*/
|
|
||||||
get("/:owner/:repository/settings/collaborators/remove")(ownerOnly { repository =>
|
|
||||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
|
||||||
removeCollaborator(repository.owner, repository.name, params("name"))
|
|
||||||
}
|
}
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
||||||
})
|
})
|
||||||
@@ -397,20 +384,20 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Provides Constraint to validate the collaborator name.
|
// * Provides Constraint to validate the collaborator name.
|
||||||
*/
|
// */
|
||||||
private def collaborator: Constraint = new Constraint(){
|
// private def collaborator: Constraint = new Constraint(){
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
// override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||||
getAccountByUserName(value) match {
|
// getAccountByUserName(value) match {
|
||||||
case None => Some("User does not exist.")
|
// case None => Some("User does not exist.")
|
||||||
case Some(x) if(x.isGroupAccount)
|
//// case Some(x) if(x.isGroupAccount)
|
||||||
=> Some("User does not exist.")
|
//// => Some("User does not exist.")
|
||||||
case Some(x) if(x.userName == params("owner") || getCollaborators(params("owner"), params("repository")).contains(x.userName))
|
// case Some(x) if(x.userName == params("owner") || getCollaborators(params("owner"), params("repository")).contains(x.userName))
|
||||||
=> Some("User can access this repository already.")
|
// => Some(value + " is repository owner.") // TODO also group members?
|
||||||
case _ => None
|
// case _ => None
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duplicate check for the rename repository name.
|
* Duplicate check for the rename repository name.
|
||||||
@@ -424,6 +411,15 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private def featureOption: Constraint = new Constraint(){
|
||||||
|
override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] =
|
||||||
|
if(Seq("DISABLE", "PRIVATE", "PUBLIC").contains(value)) None else Some("Option is invalid.")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides Constraint to validate the repository transfer user.
|
* Provides Constraint to validate the repository transfer user.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import org.scalatra._
|
|||||||
|
|
||||||
class RepositoryViewerController extends RepositoryViewerControllerBase
|
class RepositoryViewerController extends RepositoryViewerControllerBase
|
||||||
with RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
|
with RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
|
||||||
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with CommitStatusService
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with PullRequestService with CommitStatusService
|
||||||
with WebHookPullRequestService with WebHookPullRequestReviewCommentService with ProtectedBranchService
|
with WebHookPullRequestService with WebHookPullRequestReviewCommentService with ProtectedBranchService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +39,7 @@ class RepositoryViewerController extends RepositoryViewerControllerBase
|
|||||||
*/
|
*/
|
||||||
trait RepositoryViewerControllerBase extends ControllerBase {
|
trait RepositoryViewerControllerBase extends ControllerBase {
|
||||||
self: RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
|
self: RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
|
||||||
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with CommitStatusService
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with PullRequestService with CommitStatusService
|
||||||
with WebHookPullRequestService with WebHookPullRequestReviewCommentService with ProtectedBranchService =>
|
with WebHookPullRequestService with WebHookPullRequestReviewCommentService with ProtectedBranchService =>
|
||||||
|
|
||||||
ArchiveCommand.registerFormat("zip", new ZipFormat)
|
ArchiveCommand.registerFormat("zip", new ZipFormat)
|
||||||
@@ -157,7 +157,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/new/*")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/new/*")(writableUsersOnly { repository =>
|
||||||
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
||||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
|
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
|
||||||
html.editor(branch, repository, if(path.length == 0) Nil else path.split("/").toList,
|
html.editor(branch, repository, if(path.length == 0) Nil else path.split("/").toList,
|
||||||
@@ -165,7 +165,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
protectedBranch)
|
protectedBranch)
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/edit/*")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/edit/*")(writableUsersOnly { repository =>
|
||||||
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
||||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
|
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/remove/*")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/remove/*")(writableUsersOnly { repository =>
|
||||||
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
@@ -194,7 +194,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/create", editorForm)(collaboratorsOnly { (form, repository) =>
|
post("/:owner/:repository/create", editorForm)(writableUsersOnly { (form, repository) =>
|
||||||
commitFile(
|
commitFile(
|
||||||
repository = repository,
|
repository = repository,
|
||||||
branch = form.branch,
|
branch = form.branch,
|
||||||
@@ -211,7 +211,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}")
|
}")
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/update", editorForm)(collaboratorsOnly { (form, repository) =>
|
post("/:owner/:repository/update", editorForm)(writableUsersOnly { (form, repository) =>
|
||||||
commitFile(
|
commitFile(
|
||||||
repository = repository,
|
repository = repository,
|
||||||
branch = form.branch,
|
branch = form.branch,
|
||||||
@@ -232,7 +232,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}")
|
}")
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/remove", deleteForm)(collaboratorsOnly { (form, repository) =>
|
post("/:owner/:repository/remove", deleteForm)(writableUsersOnly { (form, repository) =>
|
||||||
commitFile(repository, form.branch, form.path, None, Some(form.fileName), "", "",
|
commitFile(repository, form.branch, form.path, None, Some(form.fileName), "", "",
|
||||||
form.message.getOrElse(s"Delete ${form.fileName}"))
|
form.message.getOrElse(s"Delete ${form.fileName}"))
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
/**
|
/**
|
||||||
* Creates a branch.
|
* Creates a branch.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/branches")(collaboratorsOnly { repository =>
|
post("/:owner/:repository/branches")(writableUsersOnly { repository =>
|
||||||
val newBranchName = params.getOrElse("new", halt(400))
|
val newBranchName = params.getOrElse("new", halt(400))
|
||||||
val fromBranchName = params.getOrElse("from", halt(400))
|
val fromBranchName = params.getOrElse("from", halt(400))
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
@@ -461,7 +461,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
/**
|
/**
|
||||||
* Deletes branch.
|
* Deletes branch.
|
||||||
*/
|
*/
|
||||||
get("/:owner/:repository/delete/*")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/delete/*")(writableUsersOnly { repository =>
|
||||||
val branchName = multiParams("splat").head
|
val branchName = multiParams("splat").head
|
||||||
val userName = context.loginAccount.get.userName
|
val userName = context.loginAccount.get.userName
|
||||||
if(repository.repository.defaultBranch != branchName){
|
if(repository.repository.defaultBranch != branchName){
|
||||||
|
|||||||
@@ -279,13 +279,13 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
} else {
|
} else {
|
||||||
// Update GROUP_MEMBER
|
// Update GROUP_MEMBER
|
||||||
updateGroupMembers(form.groupName, members)
|
updateGroupMembers(form.groupName, members)
|
||||||
// Update COLLABORATOR for group repositories
|
// // Update COLLABORATOR for group repositories
|
||||||
getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
// getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||||
removeCollaborators(form.groupName, repositoryName)
|
// removeCollaborators(form.groupName, repositoryName)
|
||||||
members.foreach { case (userName, isManager) =>
|
// members.foreach { case (userName, isManager) =>
|
||||||
addCollaborator(form.groupName, repositoryName, userName)
|
// addCollaborator(form.groupName, repositoryName, userName)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import org.scalatra.i18n.Messages
|
|||||||
|
|
||||||
class WikiController extends WikiControllerBase
|
class WikiController extends WikiControllerBase
|
||||||
with WikiService with RepositoryService with AccountService with ActivityService
|
with WikiService with RepositoryService with AccountService with ActivityService
|
||||||
with CollaboratorsAuthenticator with ReferrerAuthenticator
|
with ReadableUsersAuthenticator with ReferrerAuthenticator
|
||||||
|
|
||||||
trait WikiControllerBase extends ControllerBase {
|
trait WikiControllerBase extends ControllerBase {
|
||||||
self: WikiService with RepositoryService with ActivityService with CollaboratorsAuthenticator with ReferrerAuthenticator =>
|
self: WikiService with RepositoryService with ActivityService with ReadableUsersAuthenticator with ReferrerAuthenticator =>
|
||||||
|
|
||||||
case class WikiPageEditForm(pageName: String, content: String, message: Option[String], currentPageName: String, id: String)
|
case class WikiPageEditForm(pageName: String, content: String, message: Option[String], currentPageName: String, id: String)
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
|
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
|
||||||
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository)
|
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
|
||||||
case Left(_) => NotFound()
|
case Left(_) => NotFound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/:page/_revert/:commitId")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/:page/_revert/:commitId")(readableUsersOnly { repository =>
|
||||||
if(isEditable(repository)){
|
if(isEditable(repository)){
|
||||||
val pageName = StringUtil.urlDecode(params("page"))
|
val pageName = StringUtil.urlDecode(params("page"))
|
||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||||
@@ -101,7 +101,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/_revert/:commitId")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_revert/:commitId")(readableUsersOnly { repository =>
|
||||||
if(isEditable(repository)){
|
if(isEditable(repository)){
|
||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||||
|
|
||||||
@@ -114,14 +114,14 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/:page/_edit")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/:page/_edit")(readableUsersOnly { repository =>
|
||||||
if(isEditable(repository)){
|
if(isEditable(repository)){
|
||||||
val pageName = StringUtil.urlDecode(params("page"))
|
val pageName = StringUtil.urlDecode(params("page"))
|
||||||
html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName), repository)
|
html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName), repository)
|
||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/wiki/_edit", editForm)(referrersOnly { (form, repository) =>
|
post("/:owner/:repository/wiki/_edit", editForm)(readableUsersOnly { (form, repository) =>
|
||||||
if(isEditable(repository)){
|
if(isEditable(repository)){
|
||||||
defining(context.loginAccount.get){ loginAccount =>
|
defining(context.loginAccount.get){ loginAccount =>
|
||||||
saveWikiPage(
|
saveWikiPage(
|
||||||
@@ -146,13 +146,13 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/_new")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_new")(readableUsersOnly { repository =>
|
||||||
if(isEditable(repository)){
|
if(isEditable(repository)){
|
||||||
html.edit("", None, repository)
|
html.edit("", None, repository)
|
||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/wiki/_new", newForm)(referrersOnly { (form, repository) =>
|
post("/:owner/:repository/wiki/_new", newForm)(readableUsersOnly { (form, repository) =>
|
||||||
if(isEditable(repository)){
|
if(isEditable(repository)){
|
||||||
defining(context.loginAccount.get){ loginAccount =>
|
defining(context.loginAccount.get){ loginAccount =>
|
||||||
saveWikiPage(repository.owner, repository.name, form.currentPageName, form.pageName,
|
saveWikiPage(repository.owner, repository.name, form.currentPageName, form.pageName,
|
||||||
@@ -170,7 +170,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/:page/_delete")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/:page/_delete")(readableUsersOnly { repository =>
|
||||||
if(isEditable(repository)){
|
if(isEditable(repository)){
|
||||||
val pageName = StringUtil.urlDecode(params("page"))
|
val pageName = StringUtil.urlDecode(params("page"))
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
JGitUtil.getCommitLog(git, "master") match {
|
JGitUtil.getCommitLog(git, "master") match {
|
||||||
case Right((logs, hasNext)) => html.history(None, logs, repository)
|
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
||||||
case Left(_) => NotFound()
|
case Left(_) => NotFound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,9 +240,13 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
private def targetWikiPage = getWikiPage(params("owner"), params("repository"), params("pageName"))
|
private def targetWikiPage = getWikiPage(params("owner"), params("repository"), params("pageName"))
|
||||||
|
|
||||||
private def isEditable(repository: RepositoryInfo)(implicit context: Context): Boolean =
|
private def isEditable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
|
||||||
repository.repository.options.allowWikiEditing || (
|
repository.repository.options.wikiOption match {
|
||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount)
|
// case "ALL" => repository.repository.isPrivate == false || hasReadPermission(repository.owner, repository.name, context.loginAccount)
|
||||||
)
|
case "PUBLIC" => hasReadPermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
case "PRIVATE" => hasWritePermission(repository.owner, repository.name, context.loginAccount)
|
||||||
|
case "DISABLE" => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ trait CollaboratorComponent extends TemplateComponent { self: Profile =>
|
|||||||
|
|
||||||
class Collaborators(tag: Tag) extends Table[Collaborator](tag, "COLLABORATOR") with BasicTemplate {
|
class Collaborators(tag: Tag) extends Table[Collaborator](tag, "COLLABORATOR") with BasicTemplate {
|
||||||
val collaboratorName = column[String]("COLLABORATOR_NAME")
|
val collaboratorName = column[String]("COLLABORATOR_NAME")
|
||||||
def * = (userName, repositoryName, collaboratorName) <> (Collaborator.tupled, Collaborator.unapply)
|
val permission = column[String]("PERMISSION")
|
||||||
|
def * = (userName, repositoryName, collaboratorName, permission) <> (Collaborator.tupled, Collaborator.unapply)
|
||||||
|
|
||||||
def byPrimaryKey(owner: String, repository: String, collaborator: String) =
|
def byPrimaryKey(owner: String, repository: String, collaborator: String) =
|
||||||
byRepository(owner, repository) && (collaboratorName === collaborator.bind)
|
byRepository(owner, repository) && (collaboratorName === collaborator.bind)
|
||||||
@@ -17,5 +18,23 @@ trait CollaboratorComponent extends TemplateComponent { self: Profile =>
|
|||||||
case class Collaborator(
|
case class Collaborator(
|
||||||
userName: String,
|
userName: String,
|
||||||
repositoryName: String,
|
repositoryName: String,
|
||||||
collaboratorName: String
|
collaboratorName: String,
|
||||||
|
permission: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sealed abstract class Permission(val name: String)
|
||||||
|
|
||||||
|
object Permission {
|
||||||
|
object ADMIN extends Permission("ADMIN")
|
||||||
|
object WRITE extends Permission("WRITE")
|
||||||
|
object READ extends Permission("READ")
|
||||||
|
|
||||||
|
// val values: Vector[Permission] = Vector(ADMIN, WRITE, READ)
|
||||||
|
//
|
||||||
|
// private val map: Map[String, Permission] = values.map(enum => enum.name -> enum).toMap
|
||||||
|
//
|
||||||
|
// def apply(name: String): Permission = map(name)
|
||||||
|
//
|
||||||
|
// def valueOf(name: String): Option[Permission] = map.get(name)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,17 +17,16 @@ trait RepositoryComponent extends TemplateComponent { self: Profile =>
|
|||||||
val originRepositoryName = column[String]("ORIGIN_REPOSITORY_NAME")
|
val originRepositoryName = column[String]("ORIGIN_REPOSITORY_NAME")
|
||||||
val parentUserName = column[String]("PARENT_USER_NAME")
|
val parentUserName = column[String]("PARENT_USER_NAME")
|
||||||
val parentRepositoryName = column[String]("PARENT_REPOSITORY_NAME")
|
val parentRepositoryName = column[String]("PARENT_REPOSITORY_NAME")
|
||||||
val enableIssues = column[Boolean]("ENABLE_ISSUES")
|
val issuesOption = column[String]("ISSUES_OPTION")
|
||||||
val externalIssuesUrl = column[String]("EXTERNAL_ISSUES_URL")
|
val externalIssuesUrl = column[String]("EXTERNAL_ISSUES_URL")
|
||||||
val enableWiki = column[Boolean]("ENABLE_WIKI")
|
val wikiOption = column[String]("WIKI_OPTION")
|
||||||
val allowWikiEditing = column[Boolean]("ALLOW_WIKI_EDITING")
|
|
||||||
val externalWikiUrl = column[String]("EXTERNAL_WIKI_URL")
|
val externalWikiUrl = column[String]("EXTERNAL_WIKI_URL")
|
||||||
val allowFork = column[Boolean]("ALLOW_FORK")
|
val allowFork = column[Boolean]("ALLOW_FORK")
|
||||||
|
|
||||||
def * = (
|
def * = (
|
||||||
(userName, repositoryName, isPrivate, description.?, defaultBranch,
|
(userName, repositoryName, isPrivate, description.?, defaultBranch,
|
||||||
registeredDate, updatedDate, lastActivityDate, originUserName.?, originRepositoryName.?, parentUserName.?, parentRepositoryName.?),
|
registeredDate, updatedDate, lastActivityDate, originUserName.?, originRepositoryName.?, parentUserName.?, parentRepositoryName.?),
|
||||||
(enableIssues, externalIssuesUrl.?, enableWiki, allowWikiEditing, externalWikiUrl.?, allowFork)
|
(issuesOption, externalIssuesUrl.?, wikiOption, externalWikiUrl.?, allowFork)
|
||||||
).shaped <> (
|
).shaped <> (
|
||||||
{ case (repository, options) =>
|
{ case (repository, options) =>
|
||||||
Repository(
|
Repository(
|
||||||
@@ -85,10 +84,9 @@ case class Repository(
|
|||||||
)
|
)
|
||||||
|
|
||||||
case class RepositoryOptions(
|
case class RepositoryOptions(
|
||||||
enableIssues: Boolean,
|
issuesOption: String,
|
||||||
externalIssuesUrl: Option[String],
|
externalIssuesUrl: Option[String],
|
||||||
enableWiki: Boolean,
|
wikiOption: String,
|
||||||
allowWikiEditing: Boolean,
|
|
||||||
externalWikiUrl: Option[String],
|
externalWikiUrl: Option[String],
|
||||||
allowFork: Boolean
|
allowFork: Boolean
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ trait HandleCommentService {
|
|||||||
with WebHookService with WebHookIssueCommentService with WebHookPullRequestService =>
|
with WebHookService with WebHookIssueCommentService with WebHookPullRequestService =>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [[https://github.com/takezoe/gitbucket/wiki/CommentAction]]
|
* @see [[https://github.com/gitbucket/gitbucket/wiki/CommentAction]]
|
||||||
*/
|
*/
|
||||||
def handleComment(issue: Issue, content: Option[String], repository: RepositoryService.RepositoryInfo, actionOpt: Option[String])
|
def handleComment(issue: Issue, content: Option[String], repository: RepositoryService.RepositoryInfo, actionOpt: Option[String])
|
||||||
(implicit context: Context, s: Session) = {
|
(implicit context: Context, s: Session) = {
|
||||||
@@ -54,18 +54,20 @@ trait HandleCommentService {
|
|||||||
|
|
||||||
// call web hooks
|
// call web hooks
|
||||||
action match {
|
action match {
|
||||||
case None => commentId.map{ commentIdSome => callIssueCommentWebHook(repository, issue, commentIdSome, context.loginAccount.get) }
|
case None => commentId.map { commentIdSome => callIssueCommentWebHook(repository, issue, commentIdSome, context.loginAccount.get) }
|
||||||
case Some(act) => val webHookAction = act match {
|
case Some(act) => {
|
||||||
case "open" => "opened"
|
val webHookAction = act match {
|
||||||
case "reopen" => "reopened"
|
case "open" => "opened"
|
||||||
case "close" => "closed"
|
case "reopen" => "reopened"
|
||||||
case _ => act
|
case "close" => "closed"
|
||||||
}
|
case _ => act
|
||||||
if(issue.isPullRequest){
|
}
|
||||||
|
if (issue.isPullRequest) {
|
||||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, context.baseUrl, context.loginAccount.get)
|
callPullRequestWebHook(webHookAction, repository, issue.issueId, context.baseUrl, context.loginAccount.get)
|
||||||
} else {
|
} else {
|
||||||
callIssuesWebHook(webHookAction, repository, issue, context.baseUrl, context.loginAccount.get)
|
callIssuesWebHook(webHookAction, repository, issue, context.baseUrl, context.loginAccount.get)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import Q.interpolation
|
|||||||
|
|
||||||
|
|
||||||
trait IssuesService {
|
trait IssuesService {
|
||||||
self: AccountService =>
|
self: AccountService with RepositoryService =>
|
||||||
import IssuesService._
|
import IssuesService._
|
||||||
|
|
||||||
def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) =
|
def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) =
|
||||||
@@ -433,6 +433,11 @@ trait IssuesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getAssignableUserNames(owner: String, repository: String)(implicit s: Session): List[String] = {
|
||||||
|
(getCollaboratorUserNames(owner, repository, Seq(Permission.ADMIN, Permission.WRITE)) :::
|
||||||
|
(if (getAccountByUserName(owner).get.isGroupAccount) getGroupMembers(owner).map(_.userName) else List(owner))).sorted
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object IssuesService {
|
object IssuesService {
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ trait RepositoryCreationService {
|
|||||||
// Insert to the database at first
|
// Insert to the database at first
|
||||||
insertRepository(name, owner, description, isPrivate)
|
insertRepository(name, owner, description, isPrivate)
|
||||||
|
|
||||||
// Add collaborators for group repository
|
// // Add collaborators for group repository
|
||||||
if(ownerAccount.isGroupAccount){
|
// if(ownerAccount.isGroupAccount){
|
||||||
getGroupMembers(owner).foreach { member =>
|
// getGroupMembers(owner).foreach { member =>
|
||||||
addCollaborator(owner, name, member.userName)
|
// addCollaborator(owner, name, member.userName)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Insert default labels
|
// Insert default labels
|
||||||
insertDefaultLabels(owner, name)
|
insertDefaultLabels(owner, name)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package gitbucket.core.service
|
package gitbucket.core.service
|
||||||
|
|
||||||
import gitbucket.core.controller.Context
|
import gitbucket.core.controller.Context
|
||||||
import gitbucket.core.model.{Collaborator, Repository, RepositoryOptions, Account}
|
import gitbucket.core.model.{Collaborator, Repository, RepositoryOptions, Account, Permission}
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.util.JGitUtil
|
import gitbucket.core.util.JGitUtil
|
||||||
import profile.simple._
|
import profile.simple._
|
||||||
@@ -38,10 +38,9 @@ trait RepositoryService { self: AccountService =>
|
|||||||
parentUserName = parentUserName,
|
parentUserName = parentUserName,
|
||||||
parentRepositoryName = parentRepositoryName,
|
parentRepositoryName = parentRepositoryName,
|
||||||
options = RepositoryOptions(
|
options = RepositoryOptions(
|
||||||
enableIssues = true,
|
issuesOption = "PUBLIC", // TODO DISABLE for the forked repository?
|
||||||
externalIssuesUrl = None,
|
externalIssuesUrl = None,
|
||||||
enableWiki = true,
|
wikiOption = "PUBLIC", // TODO DISABLE for the forked repository?
|
||||||
allowWikiEditing = true,
|
|
||||||
externalWikiUrl = None,
|
externalWikiUrl = None,
|
||||||
allowFork = true
|
allowFork = true
|
||||||
)
|
)
|
||||||
@@ -124,11 +123,8 @@ trait RepositoryService { self: AccountService =>
|
|||||||
repositoryName = newRepositoryName
|
repositoryName = newRepositoryName
|
||||||
)) :_*)
|
)) :_*)
|
||||||
|
|
||||||
if(account.isGroupAccount){
|
// TODO Drop transfered owner from collaborators?
|
||||||
Collaborators.insertAll(getGroupMembers(newUserName).map(m => Collaborator(newUserName, newRepositoryName, m.userName)) :_*)
|
Collaborators.insertAll(collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
} else {
|
|
||||||
Collaborators.insertAll(collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update activity messages
|
// Update activity messages
|
||||||
Activities.filter { t =>
|
Activities.filter { t =>
|
||||||
@@ -323,12 +319,12 @@ trait RepositoryService { self: AccountService =>
|
|||||||
*/
|
*/
|
||||||
def saveRepositoryOptions(userName: String, repositoryName: String,
|
def saveRepositoryOptions(userName: String, repositoryName: String,
|
||||||
description: Option[String], isPrivate: Boolean,
|
description: Option[String], isPrivate: Boolean,
|
||||||
enableIssues: Boolean, externalIssuesUrl: Option[String],
|
issuesOption: String, externalIssuesUrl: Option[String],
|
||||||
enableWiki: Boolean, allowWikiEditing: Boolean, externalWikiUrl: Option[String],
|
wikiOption: String, externalWikiUrl: Option[String],
|
||||||
allowFork: Boolean)(implicit s: Session): Unit =
|
allowFork: Boolean)(implicit s: Session): Unit =
|
||||||
Repositories.filter(_.byRepository(userName, repositoryName))
|
Repositories.filter(_.byRepository(userName, repositoryName))
|
||||||
.map { r => (r.description.?, r.isPrivate, r.enableIssues, r.externalIssuesUrl.?, r.enableWiki, r.allowWikiEditing, r.externalWikiUrl.?, r.allowFork, r.updatedDate) }
|
.map { r => (r.description.?, r.isPrivate, r.issuesOption, r.externalIssuesUrl.?, r.wikiOption, r.externalWikiUrl.?, r.allowFork, r.updatedDate) }
|
||||||
.update (description, isPrivate, enableIssues, externalIssuesUrl, enableWiki, allowWikiEditing, externalWikiUrl, allowFork, currentDate)
|
.update (description, isPrivate, issuesOption, externalIssuesUrl, wikiOption, externalWikiUrl, allowFork, currentDate)
|
||||||
|
|
||||||
def saveRepositoryDefaultBranch(userName: String, repositoryName: String,
|
def saveRepositoryDefaultBranch(userName: String, repositoryName: String,
|
||||||
defaultBranch: String)(implicit s: Session): Unit =
|
defaultBranch: String)(implicit s: Session): Unit =
|
||||||
@@ -337,49 +333,64 @@ trait RepositoryService { self: AccountService =>
|
|||||||
.update (defaultBranch)
|
.update (defaultBranch)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add collaborator to the repository.
|
* Add collaborator (user or group) to the repository.
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
* @param collaboratorName the collaborator name
|
|
||||||
*/
|
*/
|
||||||
def addCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit s: Session): Unit =
|
def addCollaborator(userName: String, repositoryName: String, collaboratorName: String, permission: String)(implicit s: Session): Unit =
|
||||||
Collaborators insert Collaborator(userName, repositoryName, collaboratorName)
|
Collaborators insert Collaborator(userName, repositoryName, collaboratorName, permission)
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove collaborator from the repository.
|
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
* @param collaboratorName the collaborator name
|
|
||||||
*/
|
|
||||||
def removeCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit s: Session): Unit =
|
|
||||||
Collaborators.filter(_.byPrimaryKey(userName, repositoryName, collaboratorName)).delete
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all collaborators from the repository.
|
* Remove all collaborators from the repository.
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
*/
|
*/
|
||||||
def removeCollaborators(userName: String, repositoryName: String)(implicit s: Session): Unit =
|
def removeCollaborators(userName: String, repositoryName: String)(implicit s: Session): Unit =
|
||||||
Collaborators.filter(_.byRepository(userName, repositoryName)).delete
|
Collaborators.filter(_.byRepository(userName, repositoryName)).delete
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of collaborators name which is sorted with ascending order.
|
* Returns the list of collaborators name (user name or group name) which is sorted with ascending order.
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
* @return the list of collaborators name
|
|
||||||
*/
|
*/
|
||||||
def getCollaborators(userName: String, repositoryName: String)(implicit s: Session): List[String] =
|
def getCollaborators(userName: String, repositoryName: String)(implicit s: Session): List[(Collaborator, Boolean)] =
|
||||||
Collaborators.filter(_.byRepository(userName, repositoryName)).sortBy(_.collaboratorName).map(_.collaboratorName).list
|
Collaborators
|
||||||
|
.innerJoin(Accounts).on(_.collaboratorName === _.userName)
|
||||||
|
.filter { case (t1, t2) => t1.byRepository(userName, repositoryName) }
|
||||||
|
.map { case (t1, t2) => (t1, t2.groupAccount) }
|
||||||
|
.sortBy { case (t1, t2) => t1.collaboratorName }
|
||||||
|
.list
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of all collaborator name and permission which is sorted with ascending order.
|
||||||
|
* If a group is added as a collaborator, this method returns users who are belong to that group.
|
||||||
|
*/
|
||||||
|
def getCollaboratorUserNames(userName: String, repositoryName: String, filter: Seq[Permission] = Nil)(implicit s: Session): List[String] = {
|
||||||
|
val q1 = Collaborators
|
||||||
|
.innerJoin(Accounts).on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === false.bind) }
|
||||||
|
.filter { case (t1, t2) => t1.byRepository(userName, repositoryName) }
|
||||||
|
.map { case (t1, t2) => (t1.collaboratorName, t1.permission) }
|
||||||
|
|
||||||
|
val q2 = Collaborators
|
||||||
|
.innerJoin(Accounts).on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === true.bind) }
|
||||||
|
.innerJoin(GroupMembers).on { case ((t1, t2), t3) => t2.userName === t3.groupName }
|
||||||
|
.filter { case ((t1, t2), t3) => t1.byRepository(userName, repositoryName) }
|
||||||
|
.map { case ((t1, t2), t3) => (t3.userName, t1.permission) }
|
||||||
|
|
||||||
|
q1.union(q2).list.filter { x => filter.isEmpty || filter.exists(_.name == x._2) }.map(_._1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def hasWritePermission(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
def hasWritePermission(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||||
loginAccount match {
|
loginAccount match {
|
||||||
case Some(a) if(a.isAdmin) => true
|
case Some(a) if(a.isAdmin) => true
|
||||||
case Some(a) if(a.userName == owner) => true
|
case Some(a) if(a.userName == owner) => true
|
||||||
case Some(a) if(getCollaborators(owner, repository).contains(a.userName)) => true
|
case Some(a) if(getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||||
|
case Some(a) if(getCollaboratorUserNames(owner, repository, Seq(Permission.ADMIN, Permission.WRITE)).contains(a.userName)) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def hasReadPermission(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||||
|
loginAccount match {
|
||||||
|
case Some(a) if(a.isAdmin) => true
|
||||||
|
case Some(a) if(a.userName == owner) => true
|
||||||
|
case Some(a) if(getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||||
|
case Some(a) if(getCollaboratorUserNames(owner, repository, Seq(Permission.ADMIN, Permission.WRITE, Permission.READ)).contains(a.userName)) => true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import Implicits.request2Session
|
|||||||
* It may be called many times in one request, so each method stores
|
* It may be called many times in one request, so each method stores
|
||||||
* its result into the cache which available during a request.
|
* its result into the cache which available during a request.
|
||||||
*/
|
*/
|
||||||
trait RequestCache extends SystemSettingsService with AccountService with IssuesService {
|
trait RequestCache extends SystemSettingsService with AccountService with IssuesService with RepositoryService {
|
||||||
|
|
||||||
private implicit def context2Session(implicit context: Context): Session =
|
private implicit def context2Session(implicit context: Context): Session =
|
||||||
request2Session(context.request)
|
request2Session(context.request)
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package gitbucket.core.util
|
package gitbucket.core.util
|
||||||
|
|
||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.service.{RepositoryService, AccountService}
|
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||||
|
import gitbucket.core.model.Permission
|
||||||
import RepositoryService.RepositoryInfo
|
import RepositoryService.RepositoryInfo
|
||||||
import Implicits._
|
import Implicits._
|
||||||
import ControlUtil._
|
import ControlUtil._
|
||||||
|
|
||||||
|
import scala.collection.Searching.search
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows only oneself and administrators.
|
* Allows only oneself and administrators.
|
||||||
*/
|
*/
|
||||||
@@ -40,9 +43,9 @@ trait OwnerAuthenticator { self: ControllerBase with RepositoryService with Acco
|
|||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(repository.owner == x.userName) => action(repository)
|
case Some(x) if(repository.owner == x.userName) => action(repository)
|
||||||
case Some(x) if(getGroupMembers(repository.owner).exists { member =>
|
// TODO Repository management is allowed for only group managers?
|
||||||
member.userName == x.userName && member.isManager == true
|
case Some(x) if(getGroupMembers(repository.owner).exists { m => m.userName == x.userName && m.isManager == true }) => action(repository)
|
||||||
}) => action(repository)
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1), Seq(Permission.ADMIN)).contains(x.userName)) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -86,32 +89,9 @@ trait AdminAuthenticator { self: ControllerBase =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows only collaborators and administrators.
|
* Allows only guests and signed in users who can access the repository.
|
||||||
*/
|
*/
|
||||||
trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =>
|
trait ReferrerAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
|
||||||
protected def collaboratorsOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
|
||||||
protected def collaboratorsOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
|
||||||
|
|
||||||
private def authenticate(action: (RepositoryInfo) => Any) = {
|
|
||||||
{
|
|
||||||
defining(request.paths){ paths =>
|
|
||||||
getRepository(paths(0), paths(1)).map { repository =>
|
|
||||||
context.loginAccount match {
|
|
||||||
case Some(x) if(x.isAdmin) => action(repository)
|
|
||||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
|
||||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
|
||||||
case _ => Unauthorized()
|
|
||||||
}
|
|
||||||
} getOrElse NotFound()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows only the repository owner (or manager for group repository) and administrators.
|
|
||||||
*/
|
|
||||||
trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
|
||||||
protected def referrersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def referrersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
protected def referrersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
protected def referrersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
@@ -125,7 +105,8 @@ trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
|||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
|
||||||
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,9 +117,9 @@ trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows only signed in users which can access the repository.
|
* Allows only signed in users who have read permission for the repository.
|
||||||
*/
|
*/
|
||||||
trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =>
|
trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
|
||||||
protected def readableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def readableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
protected def readableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
protected def readableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
@@ -150,7 +131,32 @@ trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =
|
|||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(!repository.repository.isPrivate) => action(repository)
|
case Some(x) if(!repository.repository.isPrivate) => action(repository)
|
||||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
|
||||||
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
||||||
|
case _ => Unauthorized()
|
||||||
|
}
|
||||||
|
} getOrElse NotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows only signed in users who have write permission for the repository.
|
||||||
|
*/
|
||||||
|
trait WritableUsersAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
|
||||||
|
protected def writableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
|
protected def writableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
|
private def authenticate(action: (RepositoryInfo) => Any) = {
|
||||||
|
{
|
||||||
|
defining(request.paths){ paths =>
|
||||||
|
getRepository(paths(0), paths(1)).map { repository =>
|
||||||
|
context.loginAccount match {
|
||||||
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
|
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||||
|
case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
|
||||||
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1), Seq(Permission.ADMIN, Permission.WRITE)).contains(x.userName)) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ trait Notifier extends RepositoryService with AccountService with IssuesService
|
|||||||
(
|
(
|
||||||
// individual repository's owner
|
// individual repository's owner
|
||||||
issue.userName ::
|
issue.userName ::
|
||||||
|
// group members of group repository
|
||||||
|
getGroupMembers(issue.userName).map(_.userName) :::
|
||||||
// collaborators
|
// collaborators
|
||||||
getCollaborators(issue.userName, issue.repositoryName) :::
|
getCollaboratorUserNames(issue.userName, issue.repositoryName) :::
|
||||||
// participants
|
// participants
|
||||||
issue.openedUserName ::
|
issue.openedUserName ::
|
||||||
getComments(issue.userName, issue.repositoryName, issue.issueId).map(_.commentedUserName)
|
getComments(issue.userName, issue.repositoryName, issue.issueId).map(_.commentedUserName)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<label class="strong">Members</label>
|
<label class="strong">Members</label>
|
||||||
<ul id="member-list" class="collaborator">
|
<ul id="member-list" class="collaborator">
|
||||||
</ul>
|
</ul>
|
||||||
@gitbucket.core.helper.html.account("memberName", 200)
|
@gitbucket.core.helper.html.account("memberName", 200, true, false)
|
||||||
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
|
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
|
||||||
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
|
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
|
||||||
<div>
|
<div>
|
||||||
@@ -80,15 +80,14 @@ $(function(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check existence
|
// check existence
|
||||||
$.post('@context.path/_user/existence', {
|
$.post('@context.path/_user/existence', { 'userName': userName },
|
||||||
'userName': userName
|
function(data, status){
|
||||||
}, function(data, status){
|
if(data == 'user'){
|
||||||
if(data == 'true'){
|
addMemberHTML(userName, false);
|
||||||
addMemberHTML(userName, false);
|
} else {
|
||||||
} else {
|
$('#error-members').text('User does not exist.');
|
||||||
$('#error-members').text('User does not exist.');
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.remove', function(){
|
$(document).on('click', '.remove', function(){
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<label class="strong">Members</label>
|
<label class="strong">Members</label>
|
||||||
<ul id="member-list" class="collaborator">
|
<ul id="member-list" class="collaborator">
|
||||||
</ul>
|
</ul>
|
||||||
@gitbucket.core.helper.html.account("memberName", 200)
|
@gitbucket.core.helper.html.account("memberName", 200, true, false)
|
||||||
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
|
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
|
||||||
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
|
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
|
||||||
<div>
|
<div>
|
||||||
@@ -75,16 +75,14 @@ $(function(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check existence
|
// check existence
|
||||||
$.post('@context.path/_user/existence', {
|
$.post('@context.path/_user/existence', { 'userName': userName },
|
||||||
'userName': userName,
|
function(data, status){
|
||||||
'userOnly': true
|
if(data == 'user'){
|
||||||
}, function(data, status){
|
addMemberHTML(userName, false);
|
||||||
if(data == 'true'){
|
} else {
|
||||||
addMemberHTML(userName, false);
|
$('#error-members').text('User does not exist.');
|
||||||
} else {
|
}
|
||||||
$('#error-members').text('User does not exist.');
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.remove', function(){
|
$(document).on('click', '.remove', function(){
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
@(id: String, width: Int)(implicit context: gitbucket.core.controller.Context)
|
@(id: String, width: Int, user: Boolean, group: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||||
<span style="margin-right: 0px;">
|
<span style="margin-right: 0px;">
|
||||||
<input type="text" name="@id" id="@id" class="form-control" autocomplete="off" style="width: @{width}px; margin-bottom: 0px; display: inline; vertical-align: middle;"/>
|
<input type="text" name="@id" id="@id" class="form-control" autocomplete="off" style="width: @{width}px; margin-bottom: 0px; display: inline; vertical-align: middle;"/>
|
||||||
</span>
|
</span>
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
$('#@id').typeahead({
|
$('#@id').typeahead({
|
||||||
|
// highlighter: function(item) {
|
||||||
|
// var x = item.split(':');
|
||||||
|
// return $('<div><strong>' + x[0] + '</strong>' + (x[1] == 'true' ? ' (group)' : '') + '</div>');
|
||||||
|
// },
|
||||||
|
// updater: function (item) {
|
||||||
|
// return item.split(':')[0];
|
||||||
|
// },
|
||||||
source: function (query, process) {
|
source: function (query, process) {
|
||||||
return $.get('@context.path/_user/proposals', { query: query },
|
return $.get('@context.path/_user/proposals', { query: query, user: @user, group: @group },
|
||||||
function (data) {
|
function (data) {
|
||||||
return process(data.options);
|
return process(data.options);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
@(issue: gitbucket.core.model.Issue,
|
@(issue: gitbucket.core.model.Issue,
|
||||||
reopenable: Boolean,
|
reopenable: Boolean,
|
||||||
hasWritePermission: Boolean,
|
isEditable: Boolean,
|
||||||
|
isManageable: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@if(context.loginAccount.isDefined){
|
@if(isEditable){
|
||||||
<hr/><br/>
|
<hr/><br/>
|
||||||
<form method="POST" validate="true">
|
<form method="POST" validate="true">
|
||||||
<div class="issue-avatar-image">@helpers.avatarLink(context.loginAccount.get.userName, 48)</div>
|
<div class="issue-avatar-image">@helpers.avatarLink(context.loginAccount.get.userName, 48)</div>
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
enableRefsLink = true,
|
enableRefsLink = true,
|
||||||
enableLineBreaks = true,
|
enableLineBreaks = true,
|
||||||
enableTaskList = true,
|
enableTaskList = true,
|
||||||
hasWritePermission = hasWritePermission,
|
hasWritePermission = isEditable,
|
||||||
completionContext = "issues",
|
completionContext = "issues",
|
||||||
style = "",
|
style = "",
|
||||||
elastic = true,
|
elastic = true,
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
)
|
)
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<input type="hidden" name="issueId" value="@issue.issueId"/>
|
<input type="hidden" name="issueId" value="@issue.issueId"/>
|
||||||
@if((reopenable || !issue.closed) && (hasWritePermission || issue.openedUserName == context.loginAccount.get.userName)){
|
@if((reopenable || !issue.closed) && (isManageable || issue.openedUserName == context.loginAccount.get.userName)){
|
||||||
<input type="submit" class="btn btn-default" tabindex="3" formaction="@helpers.url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
|
<input type="submit" class="btn btn-default" tabindex="3" formaction="@helpers.url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
|
||||||
}
|
}
|
||||||
<input type="submit" class="btn btn-success" tabindex="2" formaction="@helpers.url(repository)/issue_comments/new" value="Comment"/>
|
<input type="submit" class="btn btn-success" tabindex="2" formaction="@helpers.url(repository)/issue_comments/new" value="Comment"/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@(issue: Option[gitbucket.core.model.Issue],
|
@(issue: Option[gitbucket.core.model.Issue],
|
||||||
comments: List[gitbucket.core.model.Comment],
|
comments: List[gitbucket.core.model.Comment],
|
||||||
hasWritePermission: Boolean,
|
isManageable: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
pullreq: Option[gitbucket.core.model.PullRequest] = None)(implicit context: gitbucket.core.controller.Context)
|
pullreq: Option[gitbucket.core.model.PullRequest] = None)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
@helpers.user(issue.get.openedUserName, styleClass="username strong") <span class="muted">commented @gitbucket.core.helper.html.datetimeago(issue.get.registeredDate)</span>
|
@helpers.user(issue.get.openedUserName, styleClass="username strong") <span class="muted">commented @gitbucket.core.helper.html.datetimeago(issue.get.registeredDate)</span>
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
@if(hasWritePermission || context.loginAccount.map(_.userName == issue.get.openedUserName).getOrElse(false)){
|
@if(isManageable || context.loginAccount.map(_.userName == issue.get.openedUserName).getOrElse(false)){
|
||||||
<a href="#" data-issue-id="@issue.get.issueId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
|
<a href="#" data-issue-id="@issue.get.issueId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
enableRefsLink = true,
|
enableRefsLink = true,
|
||||||
enableLineBreaks = true,
|
enableLineBreaks = true,
|
||||||
enableTaskList = true,
|
enableTaskList = true,
|
||||||
hasWritePermission = hasWritePermission
|
hasWritePermission = isManageable
|
||||||
)
|
)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
@gitbucket.core.helper.html.datetimeago(comment.registeredDate)
|
@gitbucket.core.helper.html.datetimeago(comment.registeredDate)
|
||||||
</span>
|
</span>
|
||||||
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer"
|
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer"
|
||||||
&& (hasWritePermission || context.loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
|
&& (isManageable || context.loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
|
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
|
||||||
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-x" aria-label="Remove"></i></a>
|
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-x" aria-label="Remove"></i></a>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
enableRefsLink = true,
|
enableRefsLink = true,
|
||||||
enableLineBreaks = true,
|
enableLineBreaks = true,
|
||||||
enableTaskList = true,
|
enableTaskList = true,
|
||||||
hasWritePermission = hasWritePermission
|
hasWritePermission = isManageable
|
||||||
)
|
)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case comment: CommitComment => {
|
case comment: CommitComment => {
|
||||||
@gitbucket.core.helper.html.commitcomment(comment, hasWritePermission, repository, pullreq.map(_.commitIdTo))
|
@gitbucket.core.helper.html.commitcomment(comment, isManageable, repository, pullreq.map(_.commitIdTo))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@(collaborators: List[String],
|
@(collaborators: List[String],
|
||||||
milestones: List[gitbucket.core.model.Milestone],
|
milestones: List[gitbucket.core.model.Milestone],
|
||||||
labels: List[gitbucket.core.model.Label],
|
labels: List[gitbucket.core.model.Label],
|
||||||
hasWritePermission: Boolean,
|
isManageable: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@gitbucket.core.html.main(s"New Issue - ${repository.owner}/${repository.name}", Some(repository)){
|
@gitbucket.core.html.main(s"New Issue - ${repository.owner}/${repository.name}", Some(repository)){
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
enableRefsLink = true,
|
enableRefsLink = true,
|
||||||
enableLineBreaks = true,
|
enableLineBreaks = true,
|
||||||
enableTaskList = true,
|
enableTaskList = true,
|
||||||
hasWritePermission = hasWritePermission,
|
hasWritePermission = isManageable,
|
||||||
completionContext = "issues",
|
completionContext = "issues",
|
||||||
style = "height: 200px; max-height: 250px;",
|
style = "height: 200px; max-height: 250px;",
|
||||||
elastic = true
|
elastic = true
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
@gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map(x => (x, 0, 0)), labels, hasWritePermission, repository)
|
@gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map(x => (x, 0, 0)), labels, isManageable, repository)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -4,17 +4,20 @@
|
|||||||
collaborators: List[String],
|
collaborators: List[String],
|
||||||
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
|
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
|
||||||
labels: List[gitbucket.core.model.Label],
|
labels: List[gitbucket.core.model.Label],
|
||||||
hasWritePermission: Boolean,
|
isEditable: Boolean,
|
||||||
|
isManageable: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@gitbucket.core.html.main(s"${issue.title} - Issue #${issue.issueId} - ${repository.owner}/${repository.name}", Some(repository)){
|
@gitbucket.core.html.main(s"${issue.title} - Issue #${issue.issueId} - ${repository.owner}/${repository.name}", Some(repository)){
|
||||||
@gitbucket.core.html.menu("issues", repository){
|
@gitbucket.core.html.menu("issues", repository){
|
||||||
<div>
|
<div>
|
||||||
<div class="show-title pull-right">
|
<div class="show-title pull-right">
|
||||||
@if(hasWritePermission || context.loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
|
@if(isManageable || context.loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
|
||||||
<a class="btn btn-default" href="#" id="edit">Edit</a>
|
<a class="btn btn-default" href="#" id="edit">Edit</a>
|
||||||
}
|
}
|
||||||
<a class="btn btn-success" href="@helpers.url(repository)/issues/new">New issue</a>
|
@if(isEditable){
|
||||||
|
<a class="btn btn-success" href="@helpers.url(repository)/issues/new">New issue</a>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="edit-title pull-right" style="display: none;">
|
<div class="edit-title pull-right" style="display: none;">
|
||||||
<a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a>
|
<a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a>
|
||||||
@@ -47,16 +50,16 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<div style="margin-top: 15px;">
|
<div style="margin-top: 15px;">
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
@gitbucket.core.issues.html.commentlist(Some(issue), comments, hasWritePermission, repository)
|
@gitbucket.core.issues.html.commentlist(Some(issue), comments, isManageable, repository)
|
||||||
@gitbucket.core.issues.html.commentform(issue, true, hasWritePermission, repository)
|
@gitbucket.core.issues.html.commentform(issue, true, isEditable, isManageable, repository)
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
@gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
@gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, isManageable, repository)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<script>
|
<script><script>
|
||||||
$(function(){
|
$(function(){
|
||||||
$('#edit').click(function(){
|
$('#edit').click(function(){
|
||||||
$('.edit-title').show();
|
$('.edit-title').show();
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
collaborators: List[String],
|
collaborators: List[String],
|
||||||
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
|
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
|
||||||
labels: List[gitbucket.core.model.Label],
|
labels: List[gitbucket.core.model.Label],
|
||||||
hasWritePermission: Boolean,
|
isManageable: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
<div style="margin-bottom: 14px;">
|
<div style="margin-bottom: 14px;">
|
||||||
<span class="muted small strong">Labels</span>
|
<span class="muted small strong">Labels</span>
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
@gitbucket.core.helper.html.dropdown("Edit", right = true) {
|
@gitbucket.core.helper.html.dropdown("Edit", right = true) {
|
||||||
@labels.map { label =>
|
@labels.map { label =>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
<div style="margin-bottom: 14px;">
|
<div style="margin-bottom: 14px;">
|
||||||
<span class="muted small strong">Milestone</span>
|
<span class="muted small strong">Milestone</span>
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
@gitbucket.core.helper.html.dropdown("Edit", right = true) {
|
@gitbucket.core.helper.html.dropdown("Edit", right = true) {
|
||||||
<li><a href="javascript:void(0);" class="milestone" data-id=""><i class="octicon octicon-x"></i> Clear this milestone</a></li>
|
<li><a href="javascript:void(0);" class="milestone" data-id=""><i class="octicon octicon-x"></i> Clear this milestone</a></li>
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
<div style="margin-bottom: 14px;">
|
<div style="margin-bottom: 14px;">
|
||||||
<span class="muted small strong">Assignee</span>
|
<span class="muted small strong">Assignee</span>
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
@gitbucket.core.helper.html.dropdown("Edit", right = true) {
|
@gitbucket.core.helper.html.dropdown("Edit", right = true) {
|
||||||
<li><a href="javascript:void(0);" class="assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
|
<li><a href="javascript:void(0);" class="assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
closedCount: Int,
|
closedCount: Int,
|
||||||
condition: gitbucket.core.service.IssuesService.IssueSearchCondition,
|
condition: gitbucket.core.service.IssuesService.IssueSearchCondition,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
|
isEditable: Boolean,
|
||||||
|
isManageable: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@gitbucket.core.html.main((if(target == "issues") "Issues" else "Pull requests") + s" - ${repository.owner}/${repository.name}", Some(repository)){
|
@gitbucket.core.html.main((if(target == "issues") "Issues" else "Pull requests") + s" - ${repository.owner}/${repository.name}", Some(repository)){
|
||||||
@gitbucket.core.html.menu(target, repository){
|
@gitbucket.core.html.menu(target, repository){
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<form method="GET" id="search-filter-form" class="form-inline pull-right">
|
<form method="GET" id="search-filter-form" class="form-inline pull-right">
|
||||||
@if(context.loginAccount.isDefined){
|
@if(isEditable){
|
||||||
@if(target == "issues"){
|
@if(target == "issues"){
|
||||||
<a class="btn btn-success" href="@helpers.url(repository)/issues/new">New issue</a>
|
<a class="btn btn-success" href="@helpers.url(repository)/issues/new">New issue</a>
|
||||||
}
|
}
|
||||||
@@ -30,8 +31,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</form>
|
</form>
|
||||||
@gitbucket.core.issues.html.listparts(target, issues, page, openCount, closedCount, condition, collaborators, milestones, labels, Some(repository), hasWritePermission)
|
@gitbucket.core.issues.html.listparts(target, issues, page, openCount, closedCount, condition, collaborators, milestones, labels, Some(repository), isManageable)
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
<form id="batcheditForm" method="POST">
|
<form id="batcheditForm" method="POST">
|
||||||
<input type="hidden" name="value"/>
|
<input type="hidden" name="value"/>
|
||||||
<input type="hidden" name="checked"/>
|
<input type="hidden" name="checked"/>
|
||||||
@@ -40,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
$('a.header-link').mouseover(function(e){
|
$('a.header-link').mouseover(function(e){
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
milestones: List[gitbucket.core.model.Milestone] = Nil,
|
milestones: List[gitbucket.core.model.Milestone] = Nil,
|
||||||
labels: List[gitbucket.core.model.Label] = Nil,
|
labels: List[gitbucket.core.model.Label] = Nil,
|
||||||
repository: Option[gitbucket.core.service.RepositoryService.RepositoryInfo] = None,
|
repository: Option[gitbucket.core.service.RepositoryService.RepositoryInfo] = None,
|
||||||
hasWritePermission: Boolean = false)(implicit context: gitbucket.core.controller.Context)
|
isManageable: Boolean = false)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@import gitbucket.core.service.IssuesService.IssueInfo
|
@import gitbucket.core.service.IssuesService.IssueInfo
|
||||||
@*
|
@*
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
<span id="table-issues-batchedit">
|
<span id="table-issues-batchedit">
|
||||||
@gitbucket.core.helper.html.dropdown("Mark as") {
|
@gitbucket.core.helper.html.dropdown("Mark as") {
|
||||||
<li><a href="javascript:void(0);" class="toggle-state" data-id="open">Open</a></li>
|
<li><a href="javascript:void(0);" class="toggle-state" data-id="open">Open</a></li>
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
|
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding-top: 12px; padding-bottom: 12px;">
|
<td style="padding-top: 12px; padding-bottom: 12px;">
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
<input type="checkbox" value="@issue.issueId"/>
|
<input type="checkbox" value="@issue.issueId"/>
|
||||||
}
|
}
|
||||||
@*
|
@*
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
@menuitem("/branches", "branches", "Branches", "git-branch", repository.branchList.length)
|
@menuitem("/branches", "branches", "Branches", "git-branch", repository.branchList.length)
|
||||||
@menuitem("/tags", "tags", "Tags", "tag", repository.tags.length)
|
@menuitem("/tags", "tags", "Tags", "tag", repository.tags.length)
|
||||||
}
|
}
|
||||||
@if(repository.repository.options.enableIssues) {
|
@if(repository.repository.options.issuesOption != "DISABLE") {
|
||||||
@menuitem("/issues", "issues", "Issues", "issue-opened", repository.issueCount)
|
@menuitem("/issues", "issues", "Issues", "issue-opened", repository.issueCount)
|
||||||
@menuitem("/pulls", "pulls", "Pull Requests", "git-pull-request", repository.pullCount)
|
@menuitem("/pulls", "pulls", "Pull Requests", "git-pull-request", repository.pullCount)
|
||||||
@menuitem("/issues/labels", "labels", "Labels", "tag")
|
@menuitem("/issues/labels", "labels", "Labels", "tag")
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
@menuitem(externalIssuesUrl, "issues", "Issues", "issue-opened")
|
@menuitem(externalIssuesUrl, "issues", "Issues", "issue-opened")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if(repository.repository.options.enableWiki) {
|
@if(repository.repository.options.wikiOption != "DISABLE") {
|
||||||
@menuitem("/wiki", "wiki", "Wiki", "book")
|
@menuitem("/wiki", "wiki", "Wiki", "book")
|
||||||
} else {
|
} else {
|
||||||
@repository.repository.options.externalWikiUrl.map { externalWikiUrl =>
|
@repository.repository.options.externalWikiUrl.map { externalWikiUrl =>
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
collaborators: List[String],
|
collaborators: List[String],
|
||||||
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
|
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
|
||||||
labels: List[gitbucket.core.model.Label],
|
labels: List[gitbucket.core.model.Label],
|
||||||
hasWritePermission: Boolean,
|
isEditable: Boolean,
|
||||||
|
isManageable: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<div id="comment-list">
|
<div id="comment-list">
|
||||||
@gitbucket.core.issues.html.commentlist(Some(issue), comments, hasWritePermission, repository, Some(pullreq))
|
@gitbucket.core.issues.html.commentlist(Some(issue), comments, isManageable, repository, Some(pullreq))
|
||||||
</div>
|
</div>
|
||||||
@defining(comments.flatMap {
|
@defining(comments.flatMap {
|
||||||
case comment: gitbucket.core.model.IssueComment => Some(comment)
|
case comment: gitbucket.core.model.IssueComment => Some(comment)
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if(hasWritePermission && issue.closed && pullreq.userName == pullreq.requestUserName && merged &&
|
@if(isManageable && issue.closed && pullreq.userName == pullreq.requestUserName && merged &&
|
||||||
pullreq.repositoryName == pullreq.requestRepositoryName && repository.branchList.contains(pullreq.requestBranch)){
|
pullreq.repositoryName == pullreq.requestRepositoryName && repository.branchList.contains(pullreq.requestBranch)){
|
||||||
<div class="issue-comment-box" style="background-color: #d0eeff;">
|
<div class="issue-comment-box" style="background-color: #d0eeff;">
|
||||||
<div class="box-content"class="issue-content" style="border: 1px solid #87a8c9; padding: 10px;">
|
<div class="box-content"class="issue-content" style="border: 1px solid #87a8c9; padding: 10px;">
|
||||||
@@ -37,11 +38,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@gitbucket.core.issues.html.commentform(issue, !merged, hasWritePermission, repository)
|
@gitbucket.core.issues.html.commentform(issue, !merged, isEditable, isManageable, repository)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
@gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
@gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, isManageable, repository)
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
@@ -55,7 +56,7 @@ $(function(){
|
|||||||
$.get('@helpers.url(repository)/pull/@issue.issueId/mergeguide', function(data){ $('.check-conflict').html(data); });
|
$.get('@helpers.url(repository)/pull/@issue.issueId/mergeguide', function(data){ $('.check-conflict').html(data); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@if(hasWritePermission){
|
@if(isManageable){
|
||||||
$('.delete-branch').click(function(e){
|
$('.delete-branch').click(function(e){
|
||||||
var branchName = $(e.target).data('name');
|
var branchName = $(e.target).data('name');
|
||||||
return confirm('Are you sure you want to remove the ' + branchName + ' branch?');
|
return confirm('Are you sure you want to remove the ' + branchName + ' branch?');
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
labels: List[gitbucket.core.model.Label],
|
labels: List[gitbucket.core.model.Label],
|
||||||
dayByDayCommits: Seq[Seq[gitbucket.core.util.JGitUtil.CommitInfo]],
|
dayByDayCommits: Seq[Seq[gitbucket.core.util.JGitUtil.CommitInfo]],
|
||||||
diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo],
|
diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo],
|
||||||
hasWritePermission: Boolean,
|
isEditable: Boolean,
|
||||||
|
isManageable: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
flash: Map[String, String])(implicit context: gitbucket.core.controller.Context)
|
flash: Map[String, String])(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@@ -18,7 +19,7 @@
|
|||||||
@defining(dayByDayCommits.flatten){ commits =>
|
@defining(dayByDayCommits.flatten){ commits =>
|
||||||
<div>
|
<div>
|
||||||
<div class="show-title pull-right">
|
<div class="show-title pull-right">
|
||||||
@if(hasWritePermission || context.loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
|
@if(isManageable || context.loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
|
||||||
<a class="btn" href="#" id="edit">Edit</a>
|
<a class="btn" href="#" id="edit">Edit</a>
|
||||||
}
|
}
|
||||||
@if(context.loginAccount.isDefined){
|
@if(context.loginAccount.isDefined){
|
||||||
@@ -82,13 +83,13 @@
|
|||||||
@flash.get("info").map{ info =>
|
@flash.get("info").map{ info =>
|
||||||
<div class="alert alert-info">@info</div>
|
<div class="alert alert-info">@info</div>
|
||||||
}
|
}
|
||||||
@gitbucket.core.pulls.html.conversation(issue, pullreq, comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
@gitbucket.core.pulls.html.conversation(issue, pullreq, comments, issueLabels, collaborators, milestones, labels, isEditable, isManageable, repository)
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="commits">
|
<div class="tab-pane" id="commits">
|
||||||
@gitbucket.core.pulls.html.commits(dayByDayCommits, Some(comments), repository)
|
@gitbucket.core.pulls.html.commits(dayByDayCommits, Some(comments), repository)
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="files">
|
<div class="tab-pane" id="files">
|
||||||
@gitbucket.core.helper.html.diff(diffs, repository, Some(commits.head.id), Some(commits.last.id), true, Some(pullreq.issueId), hasWritePermission, true)
|
@gitbucket.core.helper.html.diff(diffs, repository, Some(commits.head.id), Some(commits.last.id), true, Some(pullreq.issueId), isManageable, true)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,139 @@
|
|||||||
@(collaborators: List[String],
|
@(collaborators: List[(gitbucket.core.model.Collaborator, Boolean)],
|
||||||
isGroupRepository: Boolean,
|
isGroupRepository: Boolean,
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
|
@import gitbucket.core.model.Permission
|
||||||
@gitbucket.core.html.main("Settings", Some(repository)){
|
@gitbucket.core.html.main("Settings", Some(repository)){
|
||||||
@gitbucket.core.html.menu("settings", repository){
|
@gitbucket.core.html.menu("settings", repository){
|
||||||
@gitbucket.core.settings.html.menu("collaborators", repository){
|
@gitbucket.core.settings.html.menu("collaborators", repository){
|
||||||
<h3>Manage Collaborators</h3>
|
<form id="form" method="post" action="@helpers.url(repository)/settings/collaborators">
|
||||||
<ul class="collaborator">
|
<div class="panel panel-default">
|
||||||
@collaborators.map { collaboratorName =>
|
<div class="panel-heading strong">Collaborators</div>
|
||||||
<li>
|
<div class="panel-body">
|
||||||
<a href="@helpers.url(collaboratorName)">@collaboratorName</a>
|
<ul id="collaborator-list" class="collaborator">
|
||||||
@if(!isGroupRepository){
|
</ul>
|
||||||
<a href="@helpers.url(repository)/settings/collaborators/remove?name=@collaboratorName" class="remove">(remove)</a>
|
@gitbucket.core.helper.html.account("userName-collaborator", 200, true, false)
|
||||||
} else {
|
<input type="button" class="btn btn-default add" value="Add" id="addCollaborator"/>
|
||||||
@if(repository.managers.contains(collaboratorName)){
|
<div>
|
||||||
(Manager)
|
<span class="error" id="error-collaborator"></span>
|
||||||
}
|
</div>
|
||||||
}
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
@if(!isGroupRepository){
|
|
||||||
<form method="POST" action="@helpers.url(repository)/settings/collaborators/add" validate="true" autocomplete="off">
|
|
||||||
<div>
|
|
||||||
<span class="error" id="error-userName"></span>
|
|
||||||
</div>
|
</div>
|
||||||
@gitbucket.core.helper.html.account("userName", 300)
|
</div>
|
||||||
<input type="submit" class="btn btn-default" value="Add"/>
|
|
||||||
</form>
|
<div class="panel panel-default">
|
||||||
}
|
<div class="panel-heading strong">Groups</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<ul id="group-list" class="collaborator">
|
||||||
|
</ul>
|
||||||
|
@gitbucket.core.helper.html.account("userName-group", 200, false, true)
|
||||||
|
<input type="button" class="btn btn-default add" value="Add" id="addGroup"/>
|
||||||
|
<div>
|
||||||
|
<span class="error" id="error-group"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="align-right" style="margin-top: 20px;">
|
||||||
|
<input type="hidden" id="collaborators" name="collaborators" />
|
||||||
|
<input type="submit" class="btn btn-success" value="Apply changes"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
$('input[type=submit]').click(function(){
|
||||||
|
updateValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.add').click(function(){
|
||||||
|
var id = $(this).attr('id') == 'addCollaborator' ? 'collaborator' : 'group';
|
||||||
|
|
||||||
|
$('#error-' + id).text('');
|
||||||
|
var userName = $('#userName-' + id).val();
|
||||||
|
|
||||||
|
// check empty
|
||||||
|
if($.trim(userName) == ''){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check owner
|
||||||
|
var owner = '@repository.owner' == userName
|
||||||
|
if(owner){
|
||||||
|
$('#error-' + id).text('User is owner of this repository.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// check duplication
|
||||||
|
var exists = $('#' + id + '-list li').filter(function(){
|
||||||
|
return $(this).data('name') == userName;
|
||||||
|
}).length > 0;
|
||||||
|
if(exists){
|
||||||
|
$('#error-' + id).text('User has been already added.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check existence
|
||||||
|
$.post('@context.path/_user/existence', { 'userName': userName },
|
||||||
|
function(data, status){
|
||||||
|
if(data != ''){
|
||||||
|
addListHTML(userName, '@Permission.ADMIN.name', '#' + id + '-list');
|
||||||
|
} else {
|
||||||
|
$('#error-' + id).text('User does not exist.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.remove', function(){
|
||||||
|
$(this).parent().remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't submit form by ENTER key
|
||||||
|
$('#userName-collaborator, #userName-group').keypress(function(e){
|
||||||
|
return !(e.keyCode == 13);
|
||||||
|
});
|
||||||
|
|
||||||
|
@collaborators.map { case (collaborator, isGroup) =>
|
||||||
|
addListHTML('@collaborator.collaboratorName', '@collaborator.permission', @if(isGroup){'#group-list'}else{'#collaborator-list'});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addListHTML(userName, permission, id){
|
||||||
|
var adminButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Permission.ADMIN.name" name="' + userName + '">Admin</label>');
|
||||||
|
if(permission == '@Permission.ADMIN.name'){
|
||||||
|
adminButton.addClass('active');
|
||||||
|
}
|
||||||
|
var writeButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Permission.WRITE.name" name="' + userName + '">Write</label>');
|
||||||
|
if(permission == '@Permission.WRITE.name'){
|
||||||
|
writeButton.addClass('active');
|
||||||
|
}
|
||||||
|
var readButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Permission.READ.name" name="' + userName + '">Read</label>');
|
||||||
|
if(permission == '@Permission.READ.name'){
|
||||||
|
readButton.addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(id).append($('<li>')
|
||||||
|
.data('name', userName)
|
||||||
|
.append($('<div class="btn-group permission" data-toggle="buttons">')
|
||||||
|
.append(adminButton)
|
||||||
|
.append(writeButton)
|
||||||
|
.append(readButton))
|
||||||
|
.append(' ')
|
||||||
|
.append($('<a target="_blank">').attr('href', '@context.path/' + userName).text(userName))
|
||||||
|
.append($('<a href="#" class="remove pull-right">(remove)</a>')));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateValues(){
|
||||||
|
var collaborators = $('#collaborator-list li').map(function(i, e){
|
||||||
|
var userName = $(e).data('name');
|
||||||
|
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
|
||||||
|
}).get().join(',');
|
||||||
|
|
||||||
|
var groups = $('#group-list li').map(function(i, e){
|
||||||
|
var userName = $(e).data('name');
|
||||||
|
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
|
||||||
|
}).get().join(',');
|
||||||
|
|
||||||
|
$('#collaborators').val(collaborators + ',' + groups);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<div>
|
<div>
|
||||||
Transfer this repo to another user or to group.
|
Transfer this repo to another user or to group.
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
@gitbucket.core.helper.html.account("newOwner", 200)
|
@gitbucket.core.helper.html.account("newOwner", 200, true, true)
|
||||||
<input type="submit" class="btn btn-danger" value="Transfer"/>
|
<input type="submit" class="btn btn-danger" value="Transfer"/>
|
||||||
<div>
|
<div>
|
||||||
<span id="error-newOwner" class="error"></span>
|
<span id="error-newOwner" class="error"></span>
|
||||||
|
|||||||
@@ -39,41 +39,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading strong">Features</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<fieldset class="form-group">
|
|
||||||
<label class="checkbox" for="enableIssues">
|
|
||||||
<input type="checkbox" id="enableIssues" name="enableIssues"@if(repository.repository.options.enableIssues){ checked}/>
|
|
||||||
Issues<br>
|
|
||||||
<div class="normal muted">
|
|
||||||
Provides Lightweight issue tracking integrated with this repository. Add issues to milestones, label issues, and close & reference issues from commit messages.
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label for="externalIssuesUrl" class="strong">External URL:
|
|
||||||
<span class="normal muted">(Put if you have the external issue tracking system for this project)</span>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="form-control" id="externalIssuesUrl" name="externalIssuesUrl" value="@repository.repository.options.externalIssuesUrl"/>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="form-group">
|
|
||||||
<label class="checkbox" for="enableWiki">
|
|
||||||
<input type="checkbox" id="enableWiki" name="enableWiki"@if(repository.repository.options.enableWiki){ checked}/>
|
|
||||||
Wiki<br>
|
|
||||||
<div class="normal muted">
|
|
||||||
Provides a simple solution to manage documents. All users who can look this repository can read and collaborators can edit pages.
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label class="checkbox" for="allowWikiEditing">
|
|
||||||
<input type="checkbox" id="allowWikiEditing" name="allowWikiEditing"@if(repository.repository.options.allowWikiEditing){ checked}/>
|
|
||||||
Allow read-only users to edit Wiki pages<br>
|
|
||||||
</label>
|
|
||||||
<label for="externalWikiUrl" class="strong">External URL:
|
|
||||||
<span class="normal muted">(Put if you have the external Wiki for this project)</span>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="form-control" id="externalWikiUrl" name="externalWikiUrl" value="@repository.repository.options.externalWikiUrl"/>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<label class="checkbox" for="allowFork">
|
<label class="checkbox" for="allowFork">
|
||||||
<input type="checkbox" id="allowFork" name="allowFork"@if(repository.repository.options.allowFork){ checked}/>
|
<input type="checkbox" id="allowFork" name="allowFork"@if(repository.repository.options.allowFork){ checked}/>
|
||||||
@@ -85,6 +50,58 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading strong">Issues</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<fieldset class="form-group">
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="issuesOption" value="DISABLE" @if(repository.repository.options.issuesOption == "DISABLE"){ checked}> Disables issues tracking system
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="issuesOption" value="PRIVATE" @if(repository.repository.options.issuesOption == "PRIVATE"){ checked}> Writable users can view, create and comment on issues
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="issuesOption" value="PUBLIC" @if(repository.repository.options.issuesOption == "PUBLIC"){ checked}> Readable users can view, create and comment on isues
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<label for="externalIssuesUrl" class="strong">External URL:
|
||||||
|
<span class="normal muted">(Put if you have the external issue tracking system for this project)</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control" id="externalIssuesUrl" name="externalIssuesUrl" value="@repository.repository.options.externalIssuesUrl"/>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading strong">Wiki</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<fieldset class="form-group">
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="wikiOption" value="DISABLE" @if(repository.repository.options.wikiOption == "DISABLE"){ checked}> Disables wiki
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="wikiOption" value="PRIVATE" @if(repository.repository.options.wikiOption == "PRIVATE"){ checked}> Writable users can view, create and edit wiki pages
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="wikiOption" value="PUBLIC" @if(repository.repository.options.wikiOption == "PUBLIC"){ checked}> Readable users can view, create and edit wiki pages
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<label for="externalWikiUrl" class="strong">External URL:
|
||||||
|
<span class="normal muted">(Put if you have the external Wiki for this project)</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control" id="externalWikiUrl" name="externalWikiUrl" value="@repository.repository.options.externalWikiUrl"/>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="align-right" style="margin-top: 20px;">
|
<div class="align-right" style="margin-top: 20px;">
|
||||||
<input type="submit" class="btn btn-success" value="Apply changes"/>
|
<input type="submit" class="btn btn-success" value="Apply changes"/>
|
||||||
</div>
|
</div>
|
||||||
@@ -96,14 +113,13 @@
|
|||||||
$(function(){
|
$(function(){
|
||||||
updateFeatures();
|
updateFeatures();
|
||||||
|
|
||||||
$('#enableIssues, #enableWiki').click(function(){
|
$('input[name=issuesOption], input[name=wikiOption]').click(function(){
|
||||||
updateFeatures();
|
updateFeatures();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateFeatures() {
|
function updateFeatures() {
|
||||||
$('#externalIssuesUrl').prop('disabled', $('#enableIssues').prop('checked'));
|
$('#externalIssuesUrl').prop('disabled', !$('input[name=issuesOption]').select('[value=DISABLE]').prop('checked'));
|
||||||
$('#allowWikiEditing').prop('disabled', !$('#enableWiki').prop('checked'));
|
$('#externalWikiUrl').prop('disabled', !$('input[name=wikiOption]').select('[value=DISABLE]').prop('checked'));
|
||||||
$('#externalWikiUrl').prop('disabled', $('#enableWiki').prop('checked'));
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
to: String,
|
to: String,
|
||||||
diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo],
|
diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo],
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
hasWritePermission: Boolean,
|
isEditable: Boolean,
|
||||||
info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@gitbucket.core.html.main(s"Compare Revisions - ${repository.owner}/${repository.name}", Some(repository)){
|
@gitbucket.core.html.main(s"Compare Revisions - ${repository.owner}/${repository.name}", Some(repository)){
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<div class="pull-left">
|
<div class="pull-left">
|
||||||
@gitbucket.core.helper.html.diff(diffs, repository, None, None, false, None, false, false)
|
@gitbucket.core.helper.html.diff(diffs, repository, None, None, false, None, false, false)
|
||||||
</div>
|
</div>
|
||||||
@if(hasWritePermission){
|
@if(isEditable){
|
||||||
<div>
|
<div>
|
||||||
@if(pageName.isDefined){
|
@if(pageName.isDefined){
|
||||||
<a href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_revert/@from...@to" class="btn">Revert Changes</a>
|
<a href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_revert/@from...@to" class="btn">Revert Changes</a>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<script>
|
<script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script><script>
|
||||||
$(function(){
|
$(function(){
|
||||||
try {
|
try {
|
||||||
$('.clickable').dropzone({
|
$('.clickable').dropzone({
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
@(pageName: Option[String],
|
@(pageName: Option[String],
|
||||||
commits: List[gitbucket.core.util.JGitUtil.CommitInfo],
|
commits: List[gitbucket.core.util.JGitUtil.CommitInfo],
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
|
isEditable: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@gitbucket.core.html.main(s"History - ${repository.owner}/${repository.name}", Some(repository)){
|
@gitbucket.core.html.main(s"History - ${repository.owner}/${repository.name}", Some(repository)){
|
||||||
@gitbucket.core.html.menu("wiki", repository){
|
@gitbucket.core.html.menu("wiki", repository){
|
||||||
<div class="pull-right">
|
@if(isEditable) {
|
||||||
@if(pageName.isEmpty){
|
<div class="pull-right">
|
||||||
@if(context.loginAccount.isDefined){
|
@if(pageName.isEmpty) {
|
||||||
<a class="btn btn-small" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
<a class="btn btn-small" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
||||||
}
|
} else {
|
||||||
} else {
|
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_edit">Edit Page</a>
|
||||||
@if(context.loginAccount.isDefined){
|
<a class="btn btn-small btn-success" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
||||||
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_edit">Edit Page</a>
|
}
|
||||||
<a class="btn btn-small btn-success" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
</div>
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</div>
|
|
||||||
<h1 class="wiki-title">
|
<h1 class="wiki-title">
|
||||||
@if(pageName.isEmpty){
|
@if(pageName.isEmpty){
|
||||||
<span class="muted">History</span>
|
<span class="muted">History</span>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
page: gitbucket.core.service.WikiService.WikiPageInfo,
|
page: gitbucket.core.service.WikiService.WikiPageInfo,
|
||||||
pages: List[String],
|
pages: List[String],
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
hasWritePermission: Boolean,
|
isEditable: Boolean,
|
||||||
sidebar: Option[gitbucket.core.service.WikiService.WikiPageInfo],
|
sidebar: Option[gitbucket.core.service.WikiService.WikiPageInfo],
|
||||||
footer: Option[gitbucket.core.service.WikiService.WikiPageInfo])(implicit context: gitbucket.core.controller.Context)
|
footer: Option[gitbucket.core.service.WikiService.WikiPageInfo])(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_history">Page History</a>
|
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_history">Page History</a>
|
||||||
@if(hasWritePermission){
|
@if(isEditable){
|
||||||
<a class="btn btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_edit">Edit Page</a>
|
<a class="btn btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_edit">Edit Page</a>
|
||||||
<a class="btn btn-success" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
<a class="btn btn-success" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
||||||
}
|
}
|
||||||
@@ -49,13 +49,13 @@
|
|||||||
}
|
}
|
||||||
@sidebar.map { sidebarPage =>
|
@sidebar.map { sidebarPage =>
|
||||||
<div class="wiki-sidebar">
|
<div class="wiki-sidebar">
|
||||||
@if(hasWritePermission){
|
@if(isEditable){
|
||||||
<a href="@helpers.url(repository)/wiki/_Sidebar/_edit" style="text-decoration: none;"><span class="octicon octicon-pencil pull-right"></span></a>
|
<a href="@helpers.url(repository)/wiki/_Sidebar/_edit" style="text-decoration: none;"><span class="octicon octicon-pencil pull-right"></span></a>
|
||||||
}
|
}
|
||||||
@helpers.markdown(sidebarPage.content, repository, true, false, false, false, pages)
|
@helpers.markdown(sidebarPage.content, repository, true, false, false, false, pages)
|
||||||
</div>
|
</div>
|
||||||
}.getOrElse{
|
}.getOrElse{
|
||||||
@if(hasWritePermission){
|
@if(isEditable){
|
||||||
<a class="button-link" href="@helpers.url(repository)/wiki/_Sidebar/_edit" style="text-decoration: none;">
|
<a class="button-link" href="@helpers.url(repository)/wiki/_Sidebar/_edit" style="text-decoration: none;">
|
||||||
<div class="wiki-sidebar-dotted text-center"><i class="octicon octicon-plus"></i> Add a custom sidebar</div>
|
<div class="wiki-sidebar-dotted text-center"><i class="octicon octicon-plus"></i> Add a custom sidebar</div>
|
||||||
</a>
|
</a>
|
||||||
@@ -88,13 +88,13 @@
|
|||||||
</div>
|
</div>
|
||||||
@footer.map { footerPage =>
|
@footer.map { footerPage =>
|
||||||
<div class="wiki-sidebar wiki-footer">
|
<div class="wiki-sidebar wiki-footer">
|
||||||
@if(hasWritePermission){
|
@if(isEditable){
|
||||||
<a href="@helpers.url(repository)/wiki/_Footer/_edit" style="text-decoration: none;"><span class="octicon octicon-pencil pull-right"></span></a>
|
<a href="@helpers.url(repository)/wiki/_Footer/_edit" style="text-decoration: none;"><span class="octicon octicon-pencil pull-right"></span></a>
|
||||||
}
|
}
|
||||||
@helpers.markdown(footerPage.content, repository, true, false, false, false, pages)
|
@helpers.markdown(footerPage.content, repository, true, false, false, false, pages)
|
||||||
</div>
|
</div>
|
||||||
}.getOrElse{
|
}.getOrElse{
|
||||||
@if(hasWritePermission){
|
@if(isEditable){
|
||||||
<a class="button-link" href="@helpers.url(repository)/wiki/_Footer/_edit" style="text-decoration: none;">
|
<a class="button-link" href="@helpers.url(repository)/wiki/_Footer/_edit" style="text-decoration: none;">
|
||||||
<div class="wiki-sidebar-dotted text-center"><i class="octicon octicon-plus"></i> Add a custom footer</div>
|
<div class="wiki-sidebar-dotted text-center"><i class="octicon octicon-plus"></i> Add a custom footer</div>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@(pages: List[String],
|
@(pages: List[String],
|
||||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
|
isEditable: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@gitbucket.core.html.main(s"Pages - ${repository.owner}/${repository.name}", Some(repository)){
|
@gitbucket.core.html.main(s"Pages - ${repository.owner}/${repository.name}", Some(repository)){
|
||||||
@gitbucket.core.html.menu("wiki", repository){
|
@gitbucket.core.html.menu("wiki", repository){
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="pull-right">
|
<li class="pull-right">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
@if(hasWritePermission){
|
@if(isEditable){
|
||||||
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/_new">New Page</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package gitbucket.core.service
|
|||||||
import gitbucket.core.model._
|
import gitbucket.core.model._
|
||||||
import org.scalatest.FunSpec
|
import org.scalatest.FunSpec
|
||||||
|
|
||||||
class PullRequestServiceSpec extends FunSpec with ServiceSpecBase with PullRequestService with IssuesService with AccountService {
|
class PullRequestServiceSpec extends FunSpec with ServiceSpecBase
|
||||||
|
with PullRequestService with IssuesService with AccountService with RepositoryService {
|
||||||
|
|
||||||
def swap(r: (Issue, PullRequest)) = (r._2 -> r._1)
|
def swap(r: (Issue, PullRequest)) = (r._2 -> r._1)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user