diff --git a/src/main/resources/update/gitbucket-core_4.9.xml b/src/main/resources/update/gitbucket-core_4.9.xml new file mode 100644 index 0000000..afeffc2 --- /dev/null +++ b/src/main/resources/update/gitbucket-core_4.9.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala index 3fadd2d..66b35aa 100644 --- a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala +++ b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala @@ -24,5 +24,8 @@ new SqlMigration("update/gitbucket-core_4.7.sql") ), new Version("4.7.1"), - new Version("4.8") + new Version("4.8"), + new Version("4.9", + new LiquibaseMigration("update/gitbucket-core_4.9.xml") + ) ) diff --git a/src/main/scala/gitbucket/core/controller/AccountController.scala b/src/main/scala/gitbucket/core/controller/AccountController.scala index bfb9ccd..b16c747 100644 --- a/src/main/scala/gitbucket/core/controller/AccountController.scala +++ b/src/main/scala/gitbucket/core/controller/AccountController.scala @@ -29,10 +29,10 @@ with AccessTokenService with WebHookService with RepositoryCreationService => case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String, - url: Option[String], fileId: Option[String]) + description: Option[String], url: Option[String], fileId: Option[String]) case class AccountEditForm(password: Option[String], fullName: String, mailAddress: String, - url: Option[String], fileId: Option[String], clearImage: Boolean) + description: Option[String], url: Option[String], fileId: Option[String], clearImage: Boolean) case class SshKeyForm(title: String, publicKey: String) @@ -43,6 +43,7 @@ "password" -> trim(label("Password" , text(required, maxlength(20)))), "fullName" -> trim(label("Full Name" , text(required, maxlength(100)))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress()))), + "description" -> trim(label("bio" , optional(text()))), "url" -> trim(label("URL" , optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" , optional(text()))) )(AccountNewForm.apply) @@ -51,6 +52,7 @@ "password" -> trim(label("Password" , optional(text(maxlength(20))))), "fullName" -> trim(label("Full Name" , text(required, maxlength(100)))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress("userName")))), + "description" -> trim(label("bio" , optional(text()))), "url" -> trim(label("URL" , optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" , optional(text()))), "clearImage" -> trim(label("Clear image" , boolean())) @@ -65,11 +67,12 @@ "note" -> trim(label("Token", text(required, maxlength(100)))) )(PersonalTokenForm.apply) - case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String], members: String) - case class EditGroupForm(groupName: String, url: Option[String], fileId: Option[String], members: String, clearImage: Boolean) + case class NewGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String) + case class EditGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String, clearImage: Boolean) val newGroupForm = mapping( "groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName, reservedNames))), + "description" -> trim(label("Group description", optional(text()))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" ,optional(text()))), "members" -> trim(label("Members" ,text(required, members))) @@ -77,6 +80,7 @@ val editGroupForm = mapping( "groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))), + "description" -> trim(label("Group description", optional(text()))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" ,optional(text()))), "members" -> trim(label("Members" ,text(required, members))), @@ -167,6 +171,7 @@ password = form.password.map(sha1).getOrElse(account.password), fullName = form.fullName, mailAddress = form.mailAddress, + description = form.description, url = form.url)) updateImage(userName, form.fileId, form.clearImage) @@ -266,7 +271,7 @@ post("/register", newForm){ form => if(context.settings.allowAccountRegistration){ - createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, false, form.url) + createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, false, form.description, form.url) updateImage(form.userName, form.fileId, false) redirect("/signin") } else NotFound() @@ -277,7 +282,7 @@ }) post("/groups/new", newGroupForm)(usersOnly { form => - createGroup(form.groupName, form.url) + createGroup(form.groupName, form.description, form.url) updateGroupMembers(form.groupName, form.members.split(",").map { _.split(":") match { case Array(userName, isManager) => (userName, isManager.toBoolean) @@ -315,7 +320,7 @@ } }.toList){ case (groupName, members) => getAccountByUserName(groupName, true).map { account => - updateGroup(groupName, form.url, false) + updateGroup(groupName, form.description, form.url, false) // Update GROUP_MEMBER updateGroupMembers(form.groupName, members) diff --git a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala index f19fc90..801b873 100644 --- a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala @@ -91,16 +91,16 @@ case class NewUserForm(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, - url: Option[String], fileId: Option[String]) + description: Option[String], url: Option[String], fileId: Option[String]) case class EditUserForm(userName: String, password: Option[String], fullName: String, - mailAddress: String, isAdmin: Boolean, url: Option[String], + mailAddress: String, isAdmin: Boolean, description: Option[String], url: Option[String], fileId: Option[String], clearImage: Boolean, isRemoved: Boolean) - case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String], + case class NewGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String) - case class EditGroupForm(groupName: String, url: Option[String], fileId: Option[String], + case class EditGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String, clearImage: Boolean, isRemoved: Boolean) @@ -110,6 +110,7 @@ "fullName" -> trim(label("Full Name" ,text(required, maxlength(100)))), "mailAddress" -> trim(label("Mail Address" ,text(required, maxlength(100), uniqueMailAddress()))), "isAdmin" -> trim(label("User Type" ,boolean())), + "description" -> trim(label("bio" ,optional(text()))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" ,optional(text()))) )(NewUserForm.apply) @@ -120,6 +121,7 @@ "fullName" -> trim(label("Full Name" ,text(required, maxlength(100)))), "mailAddress" -> trim(label("Mail Address" ,text(required, maxlength(100), uniqueMailAddress("userName")))), "isAdmin" -> trim(label("User Type" ,boolean())), + "description" -> trim(label("bio" ,optional(text()))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" ,optional(text()))), "clearImage" -> trim(label("Clear image" ,boolean())), @@ -128,6 +130,7 @@ val newGroupForm = mapping( "groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName, reservedNames))), + "description" -> trim(label("Group description", optional(text()))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" ,optional(text()))), "members" -> trim(label("Members" ,text(required, members))) @@ -135,6 +138,7 @@ val editGroupForm = mapping( "groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))), + "description" -> trim(label("Group description", optional(text()))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))), "fileId" -> trim(label("File ID" ,optional(text()))), "members" -> trim(label("Members" ,text(required, members))), @@ -196,7 +200,7 @@ }) post("/admin/users/_newuser", newUserForm)(adminOnly { form => - createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, form.isAdmin, form.url) + createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, form.isAdmin, form.description, form.url) updateImage(form.userName, form.fileId, false) redirect("/admin/users") }) @@ -230,6 +234,7 @@ fullName = form.fullName, mailAddress = form.mailAddress, isAdmin = form.isAdmin, + description = form.description, url = form.url, isRemoved = form.isRemoved)) @@ -244,7 +249,7 @@ }) post("/admin/users/_newgroup", newGroupForm)(adminOnly { form => - createGroup(form.groupName, form.url) + createGroup(form.groupName, form.description, form.url) updateGroupMembers(form.groupName, form.members.split(",").map { _.split(":") match { case Array(userName, isManager) => (userName, isManager.toBoolean) @@ -267,7 +272,7 @@ } }.toList){ case (groupName, members) => getAccountByUserName(groupName, true).map { account => - updateGroup(groupName, form.url, form.isRemoved) + updateGroup(groupName, form.url, form.description, form.isRemoved) if(form.isRemoved){ // Remove from GROUP_MEMBER diff --git a/src/main/scala/gitbucket/core/model/Account.scala b/src/main/scala/gitbucket/core/model/Account.scala index cd2190a..aa0d553 100644 --- a/src/main/scala/gitbucket/core/model/Account.scala +++ b/src/main/scala/gitbucket/core/model/Account.scala @@ -19,7 +19,8 @@ val image = column[String]("IMAGE") val groupAccount = column[Boolean]("GROUP_ACCOUNT") val removed = column[Boolean]("REMOVED") - def * = (userName, fullName, mailAddress, password, isAdmin, url.?, registeredDate, updatedDate, lastLoginDate.?, image.?, groupAccount, removed) <> (Account.tupled, Account.unapply) + val description = column[String]("DESCRIPTION") + def * = (userName, fullName, mailAddress, password, isAdmin, url.?, registeredDate, updatedDate, lastLoginDate.?, image.?, groupAccount, removed, description.?) <> (Account.tupled, Account.unapply) } } @@ -35,5 +36,6 @@ lastLoginDate: Option[java.util.Date], image: Option[String], isGroupAccount: Boolean, - isRemoved: Boolean + isRemoved: Boolean, + description: Option[String] ) diff --git a/src/main/scala/gitbucket/core/service/AccountService.scala b/src/main/scala/gitbucket/core/service/AccountService.scala index d583255..0032cea 100644 --- a/src/main/scala/gitbucket/core/service/AccountService.scala +++ b/src/main/scala/gitbucket/core/service/AccountService.scala @@ -61,7 +61,7 @@ defaultAuthentication(userName, password) } case None => { - createAccount(ldapUserInfo.userName, "", ldapUserInfo.fullName, ldapUserInfo.mailAddress, false, None) + createAccount(ldapUserInfo.userName, "", ldapUserInfo.fullName, ldapUserInfo.mailAddress, false, None, None) getAccountByUserName(ldapUserInfo.userName) } } @@ -103,7 +103,7 @@ } else false } - def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, url: Option[String]) + def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, description: Option[String], url: Option[String]) (implicit s: Session): Unit = Accounts insert Account( userName = userName, @@ -117,12 +117,13 @@ lastLoginDate = None, image = None, isGroupAccount = false, - isRemoved = false) + isRemoved = false, + description = description) def updateAccount(account: Account)(implicit s: Session): Unit = Accounts .filter { a => a.userName === account.userName.bind } - .map { a => (a.password, a.fullName, a.mailAddress, a.isAdmin, a.url.?, a.registeredDate, a.updatedDate, a.lastLoginDate.?, a.removed) } + .map { a => (a.password, a.fullName, a.mailAddress, a.isAdmin, a.url.?, a.registeredDate, a.updatedDate, a.lastLoginDate.?, a.removed, a.description.?) } .update ( account.password, account.fullName, @@ -132,7 +133,8 @@ account.registeredDate, currentDate, account.lastLoginDate, - account.isRemoved) + account.isRemoved, + account.description) def updateAvatarImage(userName: String, image: Option[String])(implicit s: Session): Unit = Accounts.filter(_.userName === userName.bind).map(_.image.?).update(image) @@ -140,7 +142,7 @@ def updateLastLoginDate(userName: String)(implicit s: Session): Unit = Accounts.filter(_.userName === userName.bind).map(_.lastLoginDate).update(currentDate) - def createGroup(groupName: String, url: Option[String])(implicit s: Session): Unit = + def createGroup(groupName: String, description: Option[String], url: Option[String])(implicit s: Session): Unit = Accounts insert Account( userName = groupName, password = "", @@ -153,10 +155,13 @@ lastLoginDate = None, image = None, isGroupAccount = true, - isRemoved = false) + isRemoved = false, + description = description) - def updateGroup(groupName: String, url: Option[String], removed: Boolean)(implicit s: Session): Unit = - Accounts.filter(_.userName === groupName.bind).map(t => t.url.? -> t.removed).update(url, removed) + def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(implicit s: Session): Unit = + Accounts.filter(_.userName === groupName.bind) + .map(t => (t.url.?, t.description.?, t.removed)) + .update(url, description, removed) def updateGroupMembers(groupName: String, members: List[(String, Boolean)])(implicit s: Session): Unit = { GroupMembers.filter(_.groupName === groupName.bind).delete diff --git a/src/main/twirl/gitbucket/core/account/edit.scala.html b/src/main/twirl/gitbucket/core/account/edit.scala.html index 21377e5..757e068 100644 --- a/src/main/twirl/gitbucket/core/account/edit.scala.html +++ b/src/main/twirl/gitbucket/core/account/edit.scala.html @@ -37,6 +37,11 @@ +
+ + + +
diff --git a/src/main/twirl/gitbucket/core/account/group.scala.html b/src/main/twirl/gitbucket/core/account/group.scala.html index 6be84c9..725581f 100644 --- a/src/main/twirl/gitbucket/core/account/group.scala.html +++ b/src/main/twirl/gitbucket/core/account/group.scala.html @@ -22,6 +22,12 @@
+ +
+ +
+
+
@gitbucket.core.helper.html.uploadavatar(account)
diff --git a/src/main/twirl/gitbucket/core/account/main.scala.html b/src/main/twirl/gitbucket/core/account/main.scala.html index 8d68399..4cdd5e5 100644 --- a/src/main/twirl/gitbucket/core/account/main.scala.html +++ b/src/main/twirl/gitbucket/core/account/main.scala.html @@ -12,6 +12,9 @@
+ @account.description.map{ description => +

@description

+ } @if(account.url.isDefined){

@account.url diff --git a/src/main/twirl/gitbucket/core/account/register.scala.html b/src/main/twirl/gitbucket/core/account/register.scala.html index e54b5d8..169102e 100644 --- a/src/main/twirl/gitbucket/core/account/register.scala.html +++ b/src/main/twirl/gitbucket/core/account/register.scala.html @@ -33,6 +33,11 @@ +

+ + + +
diff --git a/src/main/twirl/gitbucket/core/admin/user.scala.html b/src/main/twirl/gitbucket/core/admin/user.scala.html index 843354f..279f953 100644 --- a/src/main/twirl/gitbucket/core/admin/user.scala.html +++ b/src/main/twirl/gitbucket/core/admin/user.scala.html @@ -66,6 +66,13 @@
+
+ +
+ +
+ +
diff --git a/src/main/twirl/gitbucket/core/admin/usergroup.scala.html b/src/main/twirl/gitbucket/core/admin/usergroup.scala.html index a656342..e8b03cf 100644 --- a/src/main/twirl/gitbucket/core/admin/usergroup.scala.html +++ b/src/main/twirl/gitbucket/core/admin/usergroup.scala.html @@ -25,6 +25,10 @@
+ + +
+
@gitbucket.core.helper.html.uploadavatar(account)
diff --git a/src/test/scala/gitbucket/core/service/AccountServiceSpec.scala b/src/test/scala/gitbucket/core/service/AccountServiceSpec.scala index 7516256..a4730cb 100644 --- a/src/test/scala/gitbucket/core/service/AccountServiceSpec.scala +++ b/src/test/scala/gitbucket/core/service/AccountServiceSpec.scala @@ -10,7 +10,7 @@ test("getAllUsers") { withTestDB { implicit session => assert(AccountService.getAllUsers() match { - case List(Account("root", "root", RootMailAddress, _, true, _, _, _, None, None, false, false)) => true + case List(Account("root", "root", RootMailAddress, _, true, _, _, _, None, None, false, false, None)) => true case _ => false }) }} @@ -47,12 +47,20 @@ val newAddress = "new mail address" AccountService.updateAccount(user().copy(mailAddress = newAddress)) assert(user().mailAddress == newAddress) + + val newUrl = Some("http://new.url.example/path") + AccountService.updateAccount(user().copy(url = newUrl)) + assert(user().url == newUrl) + + val newDescription = Some("http://new.url.example/path") + AccountService.updateAccount(user().copy(description = newDescription)) + assert(user().description == newDescription) }} test("group") { withTestDB { implicit session => val group1 = "group1" val user1 = "root" - AccountService.createGroup(group1, None) + AccountService.createGroup(group1, None, None) assert(AccountService.getGroupMembers(group1) == Nil) assert(AccountService.getGroupsByUserName(user1) == Nil) @@ -67,5 +75,20 @@ assert(AccountService.getGroupMembers(group1) == Nil) assert(AccountService.getGroupsByUserName(user1) == Nil) }} -} + test("createGroup save description") { withTestDB { implicit session => + AccountService.createGroup("some-group", Some("some clever description"), None) + val maybeGroup = AccountService.getAccountByUserName("some-group") + + assert(maybeGroup.flatMap(_.description) == Some("some clever description")) + }} + + test("updateGroup save description") { withTestDB { implicit session => + AccountService.createGroup("a-group", None, None) + + AccountService.updateGroup("a-group", Some("new description"), None, false) + + val group = AccountService.getAccountByUserName("a-group") + assert(group.flatMap(_.description) == Some("new description")) + }} +} diff --git a/src/test/scala/gitbucket/core/service/ProtectedBranchServiceSpec.scala b/src/test/scala/gitbucket/core/service/ProtectedBranchServiceSpec.scala index a2c2edf..8265950 100644 --- a/src/test/scala/gitbucket/core/service/ProtectedBranchServiceSpec.scala +++ b/src/test/scala/gitbucket/core/service/ProtectedBranchServiceSpec.scala @@ -134,7 +134,7 @@ it("administrator is manager") { withTestDB { implicit session => val x = ProtectedBranchInfo("grp1", "repo1", true, Nil, false) - x.createGroup("grp1", None) + x.createGroup("grp1", None, None) generateNewAccount("user1") generateNewAccount("user2") generateNewAccount("user3") diff --git a/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala b/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala index cfb3a3e..037a098 100644 --- a/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala +++ b/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala @@ -37,7 +37,7 @@ } def generateNewAccount(name:String)(implicit s:Session):Account = { - AccountService.createAccount(name, name, name, s"${name}@example.com", false, None) + AccountService.createAccount(name, name, name, s"${name}@example.com", false, None, None) user(name) } diff --git a/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala b/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala index d30eb7f..176807b 100644 --- a/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala +++ b/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala @@ -94,7 +94,8 @@ lastLoginDate = None, image = image, isGroupAccount = false, - isRemoved = false) + isRemoved = false, + description = None) private def createSystemSettings(useGravatar: Boolean) = SystemSettings(