Add full name to account and use it to create commits (#125)

The Git practice is to use the full name when creating commits, not a
user name. This commit fixes that by introducing a fullName field to
Account and using it when creating commits.

For migrating from earlier versions, the user name is used as an initial
value for the full name field.
This commit is contained in:
Robin Stocker
2013-10-06 23:11:09 +02:00
parent 65e6de5ba4
commit 13bff2963e
12 changed files with 49 additions and 20 deletions

View File

@@ -0,0 +1,5 @@
ALTER TABLE ACCOUNT ADD COLUMN FULL_NAME VARCHAR(100);
UPDATE ACCOUNT SET FULL_NAME = USER_NAME WHERE FULL_NAME IS NULL;
ALTER TABLE ACCOUNT ALTER COLUMN FULL_NAME SET NOT NULL;

View File

@@ -15,15 +15,16 @@ trait AccountControllerBase extends AccountManagementControllerBase with FlashMa
self: SystemSettingsService with AccountService with RepositoryService with ActivityService self: SystemSettingsService with AccountService with RepositoryService with ActivityService
with OneselfAuthenticator => with OneselfAuthenticator =>
case class AccountNewForm(userName: String, password: String,mailAddress: String, case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
url: Option[String], fileId: Option[String]) url: Option[String], fileId: Option[String])
case class AccountEditForm(password: Option[String], mailAddress: String, case class AccountEditForm(password: Option[String], fullName: String, mailAddress: String,
url: Option[String], fileId: Option[String], clearImage: Boolean) url: Option[String], fileId: Option[String], clearImage: Boolean)
val newForm = mapping( val newForm = mapping(
"userName" -> trim(label("User name" , text(required, maxlength(100), identifier, uniqueUserName))), "userName" -> trim(label("User name" , text(required, maxlength(100), identifier, uniqueUserName))),
"password" -> trim(label("Password" , text(required, maxlength(20)))), "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()))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress()))),
"url" -> trim(label("URL" , optional(text(maxlength(200))))), "url" -> trim(label("URL" , optional(text(maxlength(200))))),
"fileId" -> trim(label("File ID" , optional(text()))) "fileId" -> trim(label("File ID" , optional(text())))
@@ -31,6 +32,7 @@ trait AccountControllerBase extends AccountManagementControllerBase with FlashMa
val editForm = mapping( val editForm = mapping(
"password" -> trim(label("Password" , optional(text(maxlength(20))))), "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")))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress("userName")))),
"url" -> trim(label("URL" , optional(text(maxlength(200))))), "url" -> trim(label("URL" , optional(text(maxlength(200))))),
"fileId" -> trim(label("File ID" , optional(text()))), "fileId" -> trim(label("File ID" , optional(text()))),
@@ -84,6 +86,7 @@ trait AccountControllerBase extends AccountManagementControllerBase with FlashMa
getAccountByUserName(userName).map { account => getAccountByUserName(userName).map { account =>
updateAccount(account.copy( updateAccount(account.copy(
password = form.password.map(sha1).getOrElse(account.password), password = form.password.map(sha1).getOrElse(account.password),
fullName = form.fullName,
mailAddress = form.mailAddress, mailAddress = form.mailAddress,
url = form.url)) url = form.url))
@@ -106,7 +109,7 @@ trait AccountControllerBase extends AccountManagementControllerBase with FlashMa
post("/register", newForm){ form => post("/register", newForm){ form =>
if(loadSystemSettings().allowAccountRegistration){ if(loadSystemSettings().allowAccountRegistration){
createAccount(form.userName, sha1(form.password), form.mailAddress, false, form.url) createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, false, form.url)
updateImage(form.userName, form.fileId, false) updateImage(form.userName, form.fileId, false)
redirect("/signin") redirect("/signin")
} else NotFound } else NotFound

View File

@@ -94,7 +94,7 @@ trait CreateRepositoryControllerBase extends ControllerBase {
val git = Git.open(tmpdir) val git = Git.open(tmpdir)
git.add.addFilepattern("README.md").call git.add.addFilepattern("README.md").call
git.commit git.commit
.setCommitter(new PersonIdent(loginUserName, loginAccount.mailAddress)) .setCommitter(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
.setMessage("Initial commit").call .setMessage("Initial commit").call
git.push.call git.push.call

View File

@@ -138,7 +138,7 @@ trait PullRequestsControllerBase extends ControllerBase {
+ form.message) + form.message)
git.commit git.commit
.setCommitter(new PersonIdent(loginAccount.userName, loginAccount.mailAddress)) .setCommitter(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
.call .call
// push // push

View File

@@ -12,10 +12,12 @@ class UserManagementController extends UserManagementControllerBase
trait UserManagementControllerBase extends AccountManagementControllerBase { trait UserManagementControllerBase extends AccountManagementControllerBase {
self: AccountService with RepositoryService with AdminAuthenticator => self: AccountService with RepositoryService with AdminAuthenticator =>
case class NewUserForm(userName: String, password: String, mailAddress: String, isAdmin: Boolean, case class NewUserForm(userName: String, password: String, fullName: String,
mailAddress: String, isAdmin: Boolean,
url: Option[String], fileId: Option[String]) url: Option[String], fileId: Option[String])
case class EditUserForm(userName: String, password: Option[String], mailAddress: String, isAdmin: Boolean, case class EditUserForm(userName: String, password: Option[String], fullName: String,
mailAddress: String, isAdmin: Boolean,
url: Option[String], fileId: Option[String], clearImage: Boolean) url: Option[String], fileId: Option[String], clearImage: Boolean)
case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String], case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String],
@@ -27,6 +29,7 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
val newUserForm = mapping( val newUserForm = mapping(
"userName" -> trim(label("Username" , text(required, maxlength(100), identifier, uniqueUserName))), "userName" -> trim(label("Username" , text(required, maxlength(100), identifier, uniqueUserName))),
"password" -> trim(label("Password" , text(required, maxlength(20)))), "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()))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress()))),
"isAdmin" -> trim(label("User Type" , boolean())), "isAdmin" -> trim(label("User Type" , boolean())),
"url" -> trim(label("URL" , optional(text(maxlength(200))))), "url" -> trim(label("URL" , optional(text(maxlength(200))))),
@@ -36,6 +39,7 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
val editUserForm = mapping( val editUserForm = mapping(
"userName" -> trim(label("Username" , text(required, maxlength(100), identifier))), "userName" -> trim(label("Username" , text(required, maxlength(100), identifier))),
"password" -> trim(label("Password" , optional(text(maxlength(20))))), "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")))), "mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress("userName")))),
"isAdmin" -> trim(label("User Type" , boolean())), "isAdmin" -> trim(label("User Type" , boolean())),
"url" -> trim(label("URL" , optional(text(maxlength(200))))), "url" -> trim(label("URL" , optional(text(maxlength(200))))),
@@ -71,7 +75,7 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
}) })
post("/admin/users/_newuser", newUserForm)(adminOnly { form => post("/admin/users/_newuser", newUserForm)(adminOnly { form =>
createAccount(form.userName, sha1(form.password), form.mailAddress, form.isAdmin, form.url) createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, form.isAdmin, form.url)
updateImage(form.userName, form.fileId, false) updateImage(form.userName, form.fileId, false)
redirect("/admin/users") redirect("/admin/users")
}) })
@@ -86,6 +90,7 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
getAccountByUserName(userName).map { account => getAccountByUserName(userName).map { account =>
updateAccount(getAccountByUserName(userName).get.copy( updateAccount(getAccountByUserName(userName).get.copy(
password = form.password.map(sha1).getOrElse(account.password), password = form.password.map(sha1).getOrElse(account.password),
fullName = form.fullName,
mailAddress = form.mailAddress, mailAddress = form.mailAddress,
isAdmin = form.isAdmin, isAdmin = form.isAdmin,
url = form.url)) url = form.url))

View File

@@ -139,7 +139,7 @@ trait WikiControllerBase extends ControllerBase with FlashMapSupport {
val pageName = StringUtil.urlDecode(params("page")) val pageName = StringUtil.urlDecode(params("page"))
defining(context.loginAccount.get){ loginAccount => defining(context.loginAccount.get){ loginAccount =>
deleteWikiPage(repository.owner, repository.name, pageName, loginAccount.userName, loginAccount.mailAddress, s"Destroyed ${pageName}") deleteWikiPage(repository.owner, repository.name, pageName, loginAccount.fullName, loginAccount.mailAddress, s"Destroyed ${pageName}")
updateLastActivityDate(repository.owner, repository.name) updateLastActivityDate(repository.owner, repository.name)
redirect(s"/${repository.owner}/${repository.name}/wiki") redirect(s"/${repository.owner}/${repository.name}/wiki")

View File

@@ -4,6 +4,7 @@ import scala.slick.driver.H2Driver.simple._
object Accounts extends Table[Account]("ACCOUNT") { object Accounts extends Table[Account]("ACCOUNT") {
def userName = column[String]("USER_NAME", O PrimaryKey) def userName = column[String]("USER_NAME", O PrimaryKey)
def fullName = column[String]("FULL_NAME")
def mailAddress = column[String]("MAIL_ADDRESS") def mailAddress = column[String]("MAIL_ADDRESS")
def password = column[String]("PASSWORD") def password = column[String]("PASSWORD")
def isAdmin = column[Boolean]("ADMINISTRATOR") def isAdmin = column[Boolean]("ADMINISTRATOR")
@@ -13,11 +14,12 @@ object Accounts extends Table[Account]("ACCOUNT") {
def lastLoginDate = column[java.util.Date]("LAST_LOGIN_DATE") def lastLoginDate = column[java.util.Date]("LAST_LOGIN_DATE")
def image = column[String]("IMAGE") def image = column[String]("IMAGE")
def groupAccount = column[Boolean]("GROUP_ACCOUNT") def groupAccount = column[Boolean]("GROUP_ACCOUNT")
def * = userName ~ mailAddress ~ password ~ isAdmin ~ url.? ~ registeredDate ~ updatedDate ~ lastLoginDate.? ~ image.? ~ groupAccount <> (Account, Account.unapply _) def * = userName ~ fullName ~ mailAddress ~ password ~ isAdmin ~ url.? ~ registeredDate ~ updatedDate ~ lastLoginDate.? ~ image.? ~ groupAccount <> (Account, Account.unapply _)
} }
case class Account( case class Account(
userName: String, userName: String,
fullName: String,
mailAddress: String, mailAddress: String,
password: String, password: String,
isAdmin: Boolean, isAdmin: Boolean,

View File

@@ -37,7 +37,7 @@ trait AccountService {
// Create or update account by LDAP information // Create or update account by LDAP information
getAccountByUserName(userName) match { getAccountByUserName(userName) match {
case Some(x) => updateAccount(x.copy(mailAddress = mailAddress)) case Some(x) => updateAccount(x.copy(mailAddress = mailAddress))
case None => createAccount(userName, "", mailAddress, false, None) case None => createAccount(userName, "", userName, mailAddress, false, None)
} }
getAccountByUserName(userName) getAccountByUserName(userName)
} }
@@ -53,10 +53,11 @@ trait AccountService {
def getAllUsers(): List[Account] = Query(Accounts) sortBy(_.userName) list def getAllUsers(): List[Account] = Query(Accounts) sortBy(_.userName) list
def createAccount(userName: String, password: String, mailAddress: String, isAdmin: Boolean, url: Option[String]): Unit = def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, url: Option[String]): Unit =
Accounts insert Account( Accounts insert Account(
userName = userName, userName = userName,
password = password, password = password,
fullName = fullName,
mailAddress = mailAddress, mailAddress = mailAddress,
isAdmin = isAdmin, isAdmin = isAdmin,
url = url, url = url,
@@ -69,9 +70,10 @@ trait AccountService {
def updateAccount(account: Account): Unit = def updateAccount(account: Account): Unit =
Accounts Accounts
.filter { a => a.userName is account.userName.bind } .filter { a => a.userName is account.userName.bind }
.map { a => a.password ~ a.mailAddress ~ a.isAdmin ~ a.url.? ~ a.registeredDate ~ a.updatedDate ~ a.lastLoginDate.? } .map { a => a.password ~ a.fullName ~ a.mailAddress ~ a.isAdmin ~ a.url.? ~ a.registeredDate ~ a.updatedDate ~ a.lastLoginDate.? }
.update ( .update (
account.password, account.password,
account.fullName,
account.mailAddress, account.mailAddress,
account.isAdmin, account.isAdmin,
account.url, account.url,
@@ -89,6 +91,7 @@ trait AccountService {
Accounts insert Account( Accounts insert Account(
userName = groupName, userName = groupName,
password = "", password = "",
fullName = groupName,
mailAddress = groupName + "@devnull", mailAddress = groupName + "@devnull",
isAdmin = false, isAdmin = false,
url = url, url = url,

View File

@@ -130,7 +130,7 @@ trait WikiService {
try { try {
git.apply.setPatch(new java.io.ByteArrayInputStream(patch.getBytes("UTF-8"))).call git.apply.setPatch(new java.io.ByteArrayInputStream(patch.getBytes("UTF-8"))).call
git.add.addFilepattern(".").call git.add.addFilepattern(".").call
git.commit.setCommitter(committer.userName, committer.mailAddress).setMessage(pageName match { git.commit.setCommitter(committer.fullName, committer.mailAddress).setMessage(pageName match {
case Some(x) => s"Revert ${from} ... ${to} on ${x}" case Some(x) => s"Revert ${from} ... ${to} on ${x}"
case None => s"Revert ${from} ... ${to}" case None => s"Revert ${from} ... ${to}"
}).call }).call
@@ -175,7 +175,7 @@ trait WikiService {
// commit and push // commit and push
optionIf(added || deleted){ optionIf(added || deleted){
defining(git.commit.setCommitter(committer.userName, committer.mailAddress) defining(git.commit.setCommitter(committer.fullName, committer.mailAddress)
.setMessage(if(message.trim.length == 0){ .setMessage(if(message.trim.length == 0){
if(deleted){ if(deleted){
s"Rename ${currentPageName} to ${newPageName}" s"Rename ${currentPageName} to ${newPageName}"

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, 7),
Version(1, 6), Version(1, 6),
Version(1, 5), Version(1, 5),
Version(1, 4), Version(1, 4),

View File

@@ -31,6 +31,11 @@
<span id="error-password" class="error"></span> <span id="error-password" class="error"></span>
</fieldset> </fieldset>
} }
<fieldset>
<label for="fullName" class="strong">Full Name:</label>
<input type="text" name="fullName" id="fullName" value="@account.map(_.fullName)"/>
<span id="error-fullName" class="error"></span>
</fieldset>
<fieldset> <fieldset>
<label for="mailAddress" class="strong">Mail Address:</label> <label for="mailAddress" class="strong">Mail Address:</label>
<input type="text" name="mailAddress" id="mailAddress" value="@account.map(_.mailAddress)"/> <input type="text" name="mailAddress" id="mailAddress" value="@account.map(_.mailAddress)"/>

View File

@@ -23,6 +23,11 @@
<input type="password" name="password" id="password" value="" autocomplete="off"/> <input type="password" name="password" id="password" value="" autocomplete="off"/>
</fieldset> </fieldset>
} }
<fieldset>
<label for="fullName" class="strong">Full Name:</label>
<span id="error-fullName" class="error"></span>
<input type="text" name="fullName" id="fullName" value="@account.map(_.fullName)"/>
</fieldset>
<fieldset> <fieldset>
<label for="mailAddress" class="strong">Mail Address:</label> <label for="mailAddress" class="strong">Mail Address:</label>
<span id="error-mailAddress" class="error"></span> <span id="error-mailAddress" class="error"></span>