(refs #241)Work for specifying group manager

This commit is contained in:
takezoe
2014-03-04 04:25:44 +09:00
parent 17920e1195
commit e3fd564efd
13 changed files with 65 additions and 41 deletions

View File

@@ -0,0 +1 @@
ALTER TABLE GROUP_MEMBER ADD COLUMN MANAGER BOOLEAN DEFAULT FALSE;

View File

@@ -53,8 +53,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
// Members // Members
case "members" if(account.isGroupAccount) => { case "members" if(account.isGroupAccount) => {
val members = getGroupMembers(account.userName) val members = getGroupMembers(account.userName)
_root_.account.html.members(account, members, _root_.account.html.members(account, members.map(_._1),
context.loginAccount.exists(x => members.contains(x.userName))) context.loginAccount.exists(x => members.exists { case (userName, isManager) => userName == x.userName && isManager }))
} }
// Repositories // Repositories
@@ -63,7 +63,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
_root_.account.html.repositories(account, _root_.account.html.repositories(account,
if(account.isGroupAccount) Nil else getGroupsByUserName(userName), if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
getVisibleRepositories(context.loginAccount, baseUrl, Some(userName)), getVisibleRepositories(context.loginAccount, baseUrl, Some(userName)),
context.loginAccount.exists(x => members.contains(x.userName))) context.loginAccount.exists(x => members.exists { case (userName, isManager) => userName == x.userName && isManager }))
} }
} }
} getOrElse NotFound } getOrElse NotFound

View File

@@ -13,14 +13,14 @@ import org.apache.commons.io.FileUtils
class CreateController extends CreateControllerBase class CreateController extends CreateControllerBase
with RepositoryService with AccountService with WikiService with LabelsService with ActivityService with RepositoryService with AccountService with WikiService with LabelsService with ActivityService
with UsersAuthenticator with ReadableUsersAuthenticator with GroupMemberAuthenticator with UsersAuthenticator with ReadableUsersAuthenticator with GroupManagerAuthenticator
/** /**
* Creates new repository or group. * Creates new repository or group.
*/ */
trait CreateControllerBase extends AccountManagementControllerBase { trait CreateControllerBase extends AccountManagementControllerBase {
self: RepositoryService with AccountService with WikiService with LabelsService with ActivityService self: RepositoryService with AccountService with WikiService with LabelsService with ActivityService
with UsersAuthenticator with ReadableUsersAuthenticator with GroupMemberAuthenticator => with UsersAuthenticator with ReadableUsersAuthenticator with GroupManagerAuthenticator =>
case class RepositoryCreationForm(owner: String, name: String, description: Option[String], case class RepositoryCreationForm(owner: String, name: String, description: Option[String],
isPrivate: Boolean, createReadme: Boolean) isPrivate: Boolean, createReadme: Boolean)
@@ -84,7 +84,7 @@ trait CreateControllerBase extends AccountManagementControllerBase {
// Add collaborators for group repository // Add collaborators for group repository
if(ownerAccount.isGroupAccount){ if(ownerAccount.isGroupAccount){
getGroupMembers(form.owner).foreach { userName => getGroupMembers(form.owner).foreach { case (userName, isManager) =>
addCollaborator(form.owner, form.name, userName) addCollaborator(form.owner, form.name, userName)
} }
} }
@@ -202,19 +202,27 @@ trait CreateControllerBase extends AccountManagementControllerBase {
post("/groups/new", newGroupForm)(usersOnly { form => post("/groups/new", newGroupForm)(usersOnly { form =>
createGroup(form.groupName, form.url) createGroup(form.groupName, form.url)
updateGroupMembers(form.groupName, form.memberNames.map(_.split(",").toList).getOrElse(Nil)) updateGroupMembers(form.groupName, form.memberNames.map(_.split(",").map {
_.split(":") match {
case Array(userName, isManager) => (userName, isManager.toBoolean)
}
}.toList).getOrElse(Nil))
updateImage(form.groupName, form.fileId, false) updateImage(form.groupName, form.fileId, false)
redirect(s"/${form.groupName}") redirect(s"/${form.groupName}")
}) })
get("/:groupName/_edit")(membersOnly { get("/:groupName/_edit")(managersOnly {
defining(params("groupName")){ groupName => defining(params("groupName")){ groupName =>
html.group(getAccountByUserName(groupName, true), getGroupMembers(groupName)) html.group(getAccountByUserName(groupName, true), getGroupMembers(groupName))
} }
}) })
post("/:groupName/_edit", editGroupForm)(membersOnly { form => post("/:groupName/_edit", editGroupForm)(managersOnly { form =>
defining(params("groupName"), form.memberNames.map(_.split(",").toList).getOrElse(Nil)){ case (groupName, memberNames) => defining(params("groupName"), form.memberNames.map(_.split(",").map {
_.split(":") match {
case Array(userName, isManager) => (userName, isManager.toBoolean)
}
}.toList).getOrElse(Nil)){ case (groupName, members) =>
getAccountByUserName(groupName, true).map { account => getAccountByUserName(groupName, true).map { account =>
updateGroup(groupName, form.url, form.isRemoved) updateGroup(groupName, form.url, form.isRemoved)
@@ -230,11 +238,11 @@ trait CreateControllerBase extends AccountManagementControllerBase {
} }
} else { } else {
// Update GROUP_MEMBER // Update GROUP_MEMBER
updateGroupMembers(form.groupName, memberNames) 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)
memberNames.foreach { userName => members.foreach { case (userName, isManager) =>
addCollaborator(form.groupName, repositoryName, userName) addCollaborator(form.groupName, repositoryName, userName)
} }
} }

View File

@@ -71,7 +71,7 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
val users = getAllUsers(includeRemoved) val users = getAllUsers(includeRemoved)
val members = users.collect { case account if(account.isGroupAccount) => val members = users.collect { case account if(account.isGroupAccount) =>
account.userName -> getGroupMembers(account.userName) account.userName -> getGroupMembers(account.userName).map(_._1)
}.toMap }.toMap
admin.users.html.list(users, members, includeRemoved) admin.users.html.list(users, members, includeRemoved)
}) })
@@ -127,7 +127,11 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
post("/admin/users/_newgroup", newGroupForm)(adminOnly { form => post("/admin/users/_newgroup", newGroupForm)(adminOnly { form =>
createGroup(form.groupName, form.url) createGroup(form.groupName, form.url)
updateGroupMembers(form.groupName, form.memberNames.map(_.split(",").toList).getOrElse(Nil)) updateGroupMembers(form.groupName, form.memberNames.map(_.split(",").map {
_.split(":") match {
case Array(userName, isManager) => (userName, isManager.toBoolean)
}
}.toList).getOrElse(Nil))
updateImage(form.groupName, form.fileId, false) updateImage(form.groupName, form.fileId, false)
redirect("/admin/users") redirect("/admin/users")
}) })
@@ -139,7 +143,11 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
}) })
post("/admin/users/:groupName/_editgroup", editGroupForm)(adminOnly { form => post("/admin/users/:groupName/_editgroup", editGroupForm)(adminOnly { form =>
defining(params("groupName"), form.memberNames.map(_.split(",").toList).getOrElse(Nil)){ case (groupName, memberNames) => defining(params("groupName"), form.memberNames.map(_.split(",").map {
_.split(":") match {
case Array(userName, isManager) => (userName, isManager.toBoolean)
}
}.toList).getOrElse(Nil)){ case (groupName, members) =>
getAccountByUserName(groupName, true).map { account => getAccountByUserName(groupName, true).map { account =>
updateGroup(groupName, form.url, form.isRemoved) updateGroup(groupName, form.url, form.isRemoved)
@@ -155,11 +163,11 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
} }
} else { } else {
// Update GROUP_MEMBER // Update GROUP_MEMBER
updateGroupMembers(form.groupName, memberNames) 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)
memberNames.foreach { userName => members.foreach { case (userName, isManager) =>
addCollaborator(form.groupName, repositoryName, userName) addCollaborator(form.groupName, repositoryName, userName)
} }
} }

View File

@@ -5,10 +5,12 @@ import scala.slick.driver.H2Driver.simple._
object GroupMembers extends Table[GroupMember]("GROUP_MEMBER") { object GroupMembers extends Table[GroupMember]("GROUP_MEMBER") {
def groupName = column[String]("GROUP_NAME", O PrimaryKey) def groupName = column[String]("GROUP_NAME", O PrimaryKey)
def userName = column[String]("USER_NAME", O PrimaryKey) def userName = column[String]("USER_NAME", O PrimaryKey)
def * = groupName ~ userName <> (GroupMember, GroupMember.unapply _) def isManager = column[Boolean]("MANAGER")
def * = groupName ~ userName ~ isManager <> (GroupMember, GroupMember.unapply _)
} }
case class GroupMember( case class GroupMember(
groupName: String, groupName: String,
userName: String userName: String,
isManager: Boolean
) )

View File

@@ -122,18 +122,18 @@ trait AccountService {
def updateGroup(groupName: String, url: Option[String], removed: Boolean): Unit = def updateGroup(groupName: String, url: Option[String], removed: Boolean): Unit =
Accounts.filter(_.userName is groupName.bind).map(t => t.url.? ~ t.removed).update(url, removed) Accounts.filter(_.userName is groupName.bind).map(t => t.url.? ~ t.removed).update(url, removed)
def updateGroupMembers(groupName: String, members: List[String]): Unit = { def updateGroupMembers(groupName: String, members: List[(String, Boolean)]): Unit = {
Query(GroupMembers).filter(_.groupName is groupName.bind).delete Query(GroupMembers).filter(_.groupName is groupName.bind).delete
members.foreach { userName => members.foreach { case (userName, isManager) =>
GroupMembers insert GroupMember (groupName, userName) GroupMembers insert GroupMember (groupName, userName, isManager)
} }
} }
def getGroupMembers(groupName: String): List[String] = def getGroupMembers(groupName: String): List[(String, Boolean)] =
Query(GroupMembers) Query(GroupMembers)
.filter(_.groupName is groupName.bind) .filter(_.groupName is groupName.bind)
.sortBy(_.userName) .sortBy(_.userName)
.map(_.userName) .map(m => m.userName ~ m.isManager)
.list .list
def getGroupsByUserName(userName: String): List[String] = def getGroupsByUserName(userName: String): List[String] =

View File

@@ -50,6 +50,7 @@ object AutoUpdate {
* The history of versions. A head of this sequence is the current BitBucket version. * The history of versions. A head of this sequence is the current BitBucket version.
*/ */
val versions = Seq( val versions = Seq(
Version(1, 12),
Version(1, 11), Version(1, 11),
Version(1, 10), Version(1, 10),
Version(1, 9), Version(1, 9),

View File

@@ -157,17 +157,17 @@ trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =
} }
/** /**
* Allows only the group members. * Allows only the group managers.
*/ */
trait GroupMemberAuthenticator { self: ControllerBase with AccountService => trait GroupManagerAuthenticator { self: ControllerBase with AccountService =>
protected def membersOnly(action: => Any) = { authenticate(action) } protected def managersOnly(action: => Any) = { authenticate(action) }
protected def membersOnly[T](action: T => Any) = (form: T) => { authenticate(action(form)) } protected def managersOnly[T](action: T => Any) = (form: T) => { authenticate(action(form)) }
private def authenticate(action: => Any) = { private def authenticate(action: => Any) = {
{ {
defining(request.paths){ paths => defining(request.paths){ paths =>
context.loginAccount match { context.loginAccount match {
case Some(x) if(getGroupMembers(paths(0)).contains(x.userName)) => action case Some(x) if(getGroupMembers(paths(0)).exists { case (userName, isManager) => userName == x.userName && isManager }) => action
case _ => Unauthorized() case _ => Unauthorized()
} }
} }

View File

@@ -1,5 +1,5 @@
@(account: model.Account, groupNames: List[String], active: String, @(account: model.Account, groupNames: List[String], active: String,
isGroupMember: Boolean = false)(body: Html)(implicit context: app.Context) isGroupManager: Boolean = false)(body: Html)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(account.userName){ @html.main(account.userName){
@@ -42,7 +42,7 @@
</div> </div>
</li> </li>
} }
@if(loginAccount.isDefined && account.isGroupAccount && isGroupMember){ @if(loginAccount.isDefined && account.isGroupAccount && isGroupManager){
<li class="pull-right"> <li class="pull-right">
<div class="button-group"> <div class="button-group">
<a href="@url(account.userName)/_edit" class="btn">Edit Group</a> <a href="@url(account.userName)/_edit" class="btn">Edit Group</a>

View File

@@ -1,7 +1,7 @@
@(account: model.Account, members: List[String], isGroupMember: Boolean)(implicit context: app.Context) @(account: model.Account, members: List[String], isGroupManager: Boolean)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@main(account, Nil, "members", isGroupMember){ @main(account, Nil, "members", isGroupManager){
@if(members.isEmpty){ @if(members.isEmpty){
No members No members
} else { } else {

View File

@@ -1,9 +1,9 @@
@(account: model.Account, groupNames: List[String], @(account: model.Account, groupNames: List[String],
repositories: List[service.RepositoryService.RepositoryInfo], repositories: List[service.RepositoryService.RepositoryInfo],
isGroupMember: Boolean)(implicit context: app.Context) isGroupManager: Boolean)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@main(account, groupNames, "repositories", isGroupMember){ @main(account, groupNames, "repositories", isGroupManager){
@if(repositories.isEmpty){ @if(repositories.isEmpty){
No repositories No repositories
} else { } else {

View File

@@ -1,4 +1,4 @@
@(account: Option[model.Account], members: List[String])(implicit context: app.Context) @(account: Option[model.Account], members: List[(String, Boolean)])(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(if(account.isEmpty) "New Group" else "Update Group"){ @html.main(if(account.isEmpty) "New Group" else "Update Group"){
@@ -35,7 +35,7 @@
<fieldset> <fieldset>
<label class="strong">Members</label> <label class="strong">Members</label>
<ul id="members" class="collaborator"> <ul id="members" class="collaborator">
@members.map { userName => @members.map { case (userName, isManager) =>
<li data-name="@userName"> <li data-name="@userName">
<a href="@path/@url(userName)">@userName</a> <a href="@path/@url(userName)">@userName</a>
<a href="#" class="remove">(remove)</a> <a href="#" class="remove">(remove)</a>

View File

@@ -1,4 +1,4 @@
@(account: Option[model.Account], members: List[String])(implicit context: app.Context) @(account: Option[model.Account], members: List[(String, Boolean)])(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@main("Create a group"){ @main("Create a group"){
@@ -35,8 +35,9 @@
<fieldset> <fieldset>
<label class="strong">Members</label> <label class="strong">Members</label>
<ul id="members" class="collaborator"> <ul id="members" class="collaborator">
@members.map { userName => @members.map { case (userName, isManager) =>
<li data-name="@userName"> <li data-name="@userName">
<input type="checkbox" id="is_manager_@userName" @if(isManager){checked}/>
<a href="@path/@url(userName)">@userName</a> <a href="@path/@url(userName)">@userName</a>
<a href="#" class="remove">(remove)</a> <a href="#" class="remove">(remove)</a>
</li> </li>
@@ -85,6 +86,7 @@ $(function(){
// add member // add member
$('#members').append($('<li>') $('#members').append($('<li>')
.data('name', userName) .data('name', userName)
.append($('<input type="checkbox">').attr('id', 'is_manager_' + userName))
.append($('<a>').attr('href', '@path/' + userName).text(userName)) .append($('<a>').attr('href', '@path/' + userName).text(userName))
.append(' ') .append(' ')
.append($('<a>').attr('href', '#').addClass('remove').text('(remove)'))); .append($('<a>').attr('href', '#').addClass('remove').text('(remove)')));
@@ -92,7 +94,8 @@ $(function(){
// update hidden value // update hidden value
var userNames = $('#members li').map(function(i, e){ var userNames = $('#members li').map(function(i, e){
return $(e).data('name'); var userName = $(e).data('name');
return userName + ':' + $('#is_manager_' + userName).prop('checked');
}).get().join(','); }).get().join(',');
$('#memberNames').val(userNames); $('#memberNames').val(userNames);
} else { } else {
@@ -107,7 +110,8 @@ $(function(){
// update hidden value // update hidden value
var userNames = $('#members li').map(function(i, e){ var userNames = $('#members li').map(function(i, e){
return $(e).data('name'); var userName = $(e).data('name');
return userName + ':' + $('#is_manager_' + userName).prop('checked');
}).get().join(','); }).get().join(',');
$('#memberNames').val(userNames); $('#memberNames').val(userNames);
}); });