diff --git a/src/main/scala/app/UserManagementController.scala b/src/main/scala/app/UserManagementController.scala index f3ff60b..f625126 100644 --- a/src/main/scala/app/UserManagementController.scala +++ b/src/main/scala/app/UserManagementController.scala @@ -5,27 +5,32 @@ import util.StringUtil._ import jp.sf.amateras.scalatra.forms._ -class UserManagementController extends UserManagementControllerBase with AccountService with AdminAuthenticator +class UserManagementController extends UserManagementControllerBase + with AccountService with RepositoryService with AdminAuthenticator trait UserManagementControllerBase extends AccountManagementControllerBase { - self: AccountService with AdminAuthenticator => + self: AccountService with RepositoryService with AdminAuthenticator => - case class UserNewForm(userName: String, password: String, mailAddress: String, isAdmin: Boolean, + case class NewUserForm(userName: String, password: String, mailAddress: String, isAdmin: Boolean, url: Option[String], fileId: Option[String]) - case class UserEditForm(userName: String, password: Option[String], mailAddress: String, isAdmin: Boolean, + case class EditUserForm(userName: String, password: Option[String], mailAddress: String, isAdmin: Boolean, url: Option[String], fileId: Option[String], clearImage: Boolean) - val newForm = mapping( + case class NewGroupForm(groupName: String, fileId: Option[String], memberNames: Option[String]) + + case class EditGroupForm(groupName: String, fileId: Option[String], memberNames: Option[String], clearImage: Boolean) + + val newUserForm = mapping( "userName" -> trim(label("Username" , text(required, maxlength(100), identifier, uniqueUserName))), "password" -> trim(label("Password" , text(required, maxlength(20)))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress()))), "isAdmin" -> trim(label("User Type" , boolean())), "url" -> trim(label("URL" , optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" , optional(text()))) - )(UserNewForm.apply) + )(NewUserForm.apply) - val editForm = mapping( + val editUserForm = mapping( "userName" -> trim(label("Username" , text(required, maxlength(100), identifier))), "password" -> trim(label("Password" , optional(text(maxlength(20))))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress("userName")))), @@ -33,8 +38,21 @@ "url" -> trim(label("URL" , optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" , optional(text()))), "clearImage" -> trim(label("Clear image" , boolean())) - )(UserEditForm.apply) - + )(EditUserForm.apply) + + val newGroupForm = mapping( + "groupName" -> trim(label("Group name" , text(required, maxlength(100), identifier))), + "fileId" -> trim(label("File ID" , optional(text()))), + "memberNames" -> trim(label("Member Names" , optional(text()))) + )(NewGroupForm.apply) + + val editGroupForm = mapping( + "groupName" -> trim(label("Group name" , text(required, maxlength(100), identifier))), + "fileId" -> trim(label("File ID" , optional(text()))), + "memberNames" -> trim(label("Member Names" , optional(text()))), + "clearImage" -> trim(label("Clear image" , boolean())) + )(EditGroupForm.apply) + get("/admin/users")(adminOnly { admin.users.html.list(getAllUsers()) }) @@ -43,7 +61,7 @@ admin.users.html.edit(None) }) - post("/admin/users/_new", newForm)(adminOnly { form => + post("/admin/users/_new", newUserForm)(adminOnly { form => createAccount(form.userName, sha1(form.password), form.mailAddress, form.isAdmin, form.url) updateImage(form.userName, form.fileId, false) redirect("/admin/users") @@ -54,7 +72,7 @@ admin.users.html.edit(getAccountByUserName(userName)) }) - post("/admin/users/:name/_edit", editForm)(adminOnly { form => + post("/admin/users/:name/_edit", editUserForm)(adminOnly { form => val userName = params("userName") getAccountByUserName(userName).map { account => updateAccount(getAccountByUserName(userName).get.copy( @@ -69,9 +87,40 @@ } getOrElse NotFound }) - get("/admin/users/_newgroup"){ - admin.users.html.group(None) - } + get("/admin/users/_newgroup")(adminOnly { + admin.users.html.group(None, Nil) + }) + + post("/admin/users/_newgroup", newGroupForm)(adminOnly { form => + createGroup(form.groupName) + updateGroupMembers(form.groupName, form.memberNames.map(_.split(",").toList).getOrElse(Nil)) + updateImage(form.groupName, form.fileId, false) + redirect("/admin/users") + }) + + get("/admin/users/:groupName/_editgroup")(adminOnly { + val groupName = params("groupName") + admin.users.html.group(getAccountByUserName(groupName), getGroupMembers(groupName)) + }) + + post("/admin/users/:groupName/_editgroup", editGroupForm)(adminOnly { form => + val groupName = params("groupName") + getAccountByUserName(groupName).map { account => + val memberNames = form.memberNames.map(_.split(",").toList).getOrElse(Nil) + updateGroupMembers(form.groupName, memberNames) + + getRepositoryNamesOfUser(form.groupName).foreach { repositoryName => + removeCollaborators(form.groupName, repositoryName) + memberNames.foreach { userName => + addCollaborator(form.groupName, repositoryName, userName) + } + } + + updateImage(form.groupName, form.fileId, form.clearImage) + redirect("/admin/users") + + } getOrElse NotFound + }) /** * JSON API for collaborator completion. diff --git a/src/main/scala/service/AccountService.scala b/src/main/scala/service/AccountService.scala index eae2949..5a9aea3 100644 --- a/src/main/scala/service/AccountService.scala +++ b/src/main/scala/service/AccountService.scala @@ -45,5 +45,33 @@ def updateLastLoginDate(userName: String): Unit = Accounts.filter(_.userName is userName.bind).map(_.lastLoginDate).update(currentDate) - + + def createGroup(groupName: String): Unit = + Accounts insert Account( + userName = groupName, + password = "", + mailAddress = "", + isAdmin = false, + url = None, + registeredDate = currentDate, + updatedDate = currentDate, + lastLoginDate = None, + image = None, + isGroupAccount = true) + + def updateGroupMembers(groupName: String, members: List[String]): Unit = { + Query(GroupMembers).filter(_.groupName is groupName.bind).delete + + members.foreach { userName => + GroupMembers insert GroupMember (groupName, userName) + } + } + + def getGroupMembers(groupName: String): List[String] = + Query(GroupMembers) + .filter(_.groupName is groupName.bind) + .sortBy(_.userName) + .map(_.userName) + .list + } diff --git a/src/main/scala/service/RepositoryService.scala b/src/main/scala/service/RepositoryService.scala index 153aa95..b8c7596 100644 --- a/src/main/scala/service/RepositoryService.scala +++ b/src/main/scala/service/RepositoryService.scala @@ -162,6 +162,15 @@ Collaborators.filter(_.byPrimaryKey(userName, repositoryName, collaboratorName)).delete /** + * 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): Unit = + Collaborators.filter(_.byRepository(userName, repositoryName)).delete + + /** * Returns the list of collaborators name which is sorted with ascending order. * * @param userName the user name of the repository owner diff --git a/src/main/twirl/admin/users/group.scala.html b/src/main/twirl/admin/users/group.scala.html index da55c0b..0fdfbd7 100644 --- a/src/main/twirl/admin/users/group.scala.html +++ b/src/main/twirl/admin/users/group.scala.html @@ -1,15 +1,15 @@ -@(account: Option[model.Account])(implicit context: app.Context) +@(account: Option[model.Account], members: List[String])(implicit context: app.Context) @import context._ @import view.helpers._ @html.main(if(account.isEmpty) "New User" else "Update User"){ @admin.html.menu("users"){ -
+
- - - + + +
@@ -20,14 +20,22 @@
- + + +
- + Cancel
@@ -45,9 +53,23 @@ }); $('#addMember').click(function(){ + $('#error-memberName').text(''); var userName = $('#memberName').val(); + + // TODO check existence + + // check duplication + var exists = $('#members li').filter(function(){ + return $(this).data('userName') == userName; + }).length > 0; + if(exists){ + $('#error-memberName').text('User has been already added.'); + return false; + } + $('#members').append($('
  • ') - .append($('').attr('href', '#').text(userName)) + .data('userName', userName) + .append($('').attr('href', '@path/' + userName).text(userName)) .append(' ') .append($('').attr('href', '#').addClass('remove').text('(remove)'))); $('#memberName').val(''); @@ -56,5 +78,13 @@ $(document).on('click', '.remove', function(){ $(this).parent().remove(); }); + + $('#submit').click(function(){ + var userNames = $('#members li').map(function(i, e){ + return $(e).data('userName'); + }).get().join(','); + $('#memberNames').val(userNames); + return true; + }); }); \ No newline at end of file diff --git a/src/main/twirl/admin/users/list.scala.html b/src/main/twirl/admin/users/list.scala.html index 70fe434..e73d0fa 100644 --- a/src/main/twirl/admin/users/list.scala.html +++ b/src/main/twirl/admin/users/list.scala.html @@ -12,28 +12,40 @@
    - Edit + @if(account.isGroupAccount){ + Edit + } else { + Edit + }
    @avatar(account.userName, 20) @account.userName - @if(account.isAdmin){ - (Administrator) + @if(account.isGroupAccount){ + (Group) } else { - (Normal) + @if(account.isAdmin){ + (Administrator) + } else { + (Normal) + } }

    - @account.mailAddress - @account.url.map { url => - @url + @if(!account.isGroupAccount){ + @account.mailAddress + @account.url.map { url => + @url + } }
    Registered: @datetime(account.registeredDate) Updated: @datetime(account.updatedDate) - Last Login: @account.lastLoginDate.map(datetime) + @if(!account.isGroupAccount){ + Last Login: @account.lastLoginDate.map(datetime) + }