diff --git a/src/main/scala/app/IndexController.scala b/src/main/scala/app/IndexController.scala
index 84270b8..c3d1928 100644
--- a/src/main/scala/app/IndexController.scala
+++ b/src/main/scala/app/IndexController.scala
@@ -1,86 +1,86 @@
-package app
-
-import util._
-import util.Implicits._
-import service._
-import jp.sf.amateras.scalatra.forms._
-
-class IndexController extends IndexControllerBase
- with RepositoryService with ActivityService with AccountService with UsersAuthenticator
-
-trait IndexControllerBase extends ControllerBase {
- self: RepositoryService with ActivityService with AccountService with UsersAuthenticator =>
-
- case class SignInForm(userName: String, password: String)
-
- val form = mapping(
- "userName" -> trim(label("Username", text(required))),
- "password" -> trim(label("Password", text(required)))
- )(SignInForm.apply)
-
- get("/"){
- val loginAccount = context.loginAccount
-
- html.index(getRecentActivities(),
- getVisibleRepositories(loginAccount, context.baseUrl),
- loginAccount.map{ account => getUserRepositories(account.userName, context.baseUrl) }.getOrElse(Nil)
- )
- }
-
- get("/signin"){
- val redirect = params.get("redirect")
- if(redirect.isDefined && redirect.get.startsWith("/")){
- flash += Keys.Flash.Redirect -> redirect.get
- }
- html.signin()
- }
-
- post("/signin", form){ form =>
- authenticate(context.settings, form.userName, form.password) match {
- case Some(account) => signin(account)
- case None => redirect("/signin")
- }
- }
-
- get("/signout"){
- session.invalidate
- redirect("/")
- }
-
- get("/activities.atom"){
- contentType = "application/atom+xml; type=feed"
- helper.xml.feed(getRecentActivities())
- }
-
- /**
- * Set account information into HttpSession and redirect.
- */
- private def signin(account: model.Account) = {
- session.setAttribute(Keys.Session.LoginAccount, account)
- updateLastLoginDate(account.userName)
-
- flash.get(Keys.Flash.Redirect).asInstanceOf[Option[String]].map { redirectUrl =>
- if(redirectUrl.stripSuffix("/") == request.getContextPath){
- redirect("/")
- } else {
- redirect(redirectUrl)
- }
- }.getOrElse {
- redirect("/")
- }
- }
-
- /**
- * JSON API for collaborator completion.
- *
- * TODO Move to other controller?
- */
- get("/_user/proposals")(usersOnly {
- contentType = formats("json")
- org.json4s.jackson.Serialization.write(
- Map("options" -> getAllUsers().filter(!_.isGroupAccount).map(_.userName).toArray)
- )
- })
-
-
-}
+package app
+
+import util._
+import util.Implicits._
+import service._
+import jp.sf.amateras.scalatra.forms._
+
+class IndexController extends IndexControllerBase
+ with RepositoryService with ActivityService with AccountService with UsersAuthenticator
+
+trait IndexControllerBase extends ControllerBase {
+ self: RepositoryService with ActivityService with AccountService with UsersAuthenticator =>
+
+ case class SignInForm(userName: String, password: String)
+
+ val form = mapping(
+ "userName" -> trim(label("Username", text(required))),
+ "password" -> trim(label("Password", text(required)))
+ )(SignInForm.apply)
+
+ get("/"){
+ val loginAccount = context.loginAccount
+
+ html.index(getRecentActivities(),
+ getVisibleRepositories(loginAccount, context.baseUrl),
+ loginAccount.map{ account => getUserRepositories(account.userName, context.baseUrl) }.getOrElse(Nil)
+ )
+ }
+
+ get("/signin"){
+ val redirect = params.get("redirect")
+ if(redirect.isDefined && redirect.get.startsWith("/")){
+ flash += Keys.Flash.Redirect -> redirect.get
+ }
+ html.signin()
+ }
+
+ post("/signin", form){ form =>
+ authenticate(context.settings, form.userName, form.password) match {
+ case Some(account) => signin(account)
+ case None => redirect("/signin")
+ }
+ }
+
+ get("/signout"){
+ session.invalidate
+ redirect("/")
+ }
+
+ get("/activities.atom"){
+ contentType = "application/atom+xml; type=feed"
+ helper.xml.feed(getRecentActivities())
+ }
+
+ /**
+ * Set account information into HttpSession and redirect.
+ */
+ private def signin(account: model.Account) = {
+ session.setAttribute(Keys.Session.LoginAccount, account)
+ updateLastLoginDate(account.userName)
+
+ flash.get(Keys.Flash.Redirect).asInstanceOf[Option[String]].map { redirectUrl =>
+ if(redirectUrl.stripSuffix("/") == request.getContextPath){
+ redirect("/")
+ } else {
+ redirect(redirectUrl)
+ }
+ }.getOrElse {
+ redirect("/")
+ }
+ }
+
+ /**
+ * JSON API for collaborator completion.
+ *
+ * TODO Move to other controller?
+ */
+ get("/_user/proposals")(usersOnly {
+ contentType = formats("json")
+ org.json4s.jackson.Serialization.write(
+ Map("options" -> getAllUsers().filter(!_.isGroupAccount).map(_.userName).toArray)
+ )
+ })
+
+
+}
diff --git a/src/main/scala/app/RepositorySettingsController.scala b/src/main/scala/app/RepositorySettingsController.scala
index 1f2c35e..41450ea 100644
--- a/src/main/scala/app/RepositorySettingsController.scala
+++ b/src/main/scala/app/RepositorySettingsController.scala
@@ -1,267 +1,267 @@
-package app
-
-import service._
-import util.Directory._
-import util.ControlUtil._
-import util.Implicits._
-import util.{UsersAuthenticator, OwnerAuthenticator}
-import util.JGitUtil.CommitInfo
-import jp.sf.amateras.scalatra.forms._
-import org.apache.commons.io.FileUtils
-import org.scalatra.i18n.Messages
-import service.WebHookService.WebHookPayload
-import org.eclipse.jgit.api.Git
-
-class RepositorySettingsController extends RepositorySettingsControllerBase
- with RepositoryService with AccountService with WebHookService
- with OwnerAuthenticator with UsersAuthenticator
-
-trait RepositorySettingsControllerBase extends ControllerBase {
- self: RepositoryService with AccountService with WebHookService
- with OwnerAuthenticator with UsersAuthenticator =>
-
- // for repository options
- case class OptionsForm(repositoryName: String, description: Option[String], defaultBranch: String, isPrivate: Boolean)
-
- val optionsForm = mapping(
- "repositoryName" -> trim(label("Description" , text(required, maxlength(40), identifier, renameRepositoryName))),
- "description" -> trim(label("Description" , optional(text()))),
- "defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100)))),
- "isPrivate" -> trim(label("Repository Type", boolean()))
- )(OptionsForm.apply)
-
- // for collaborator addition
- case class CollaboratorForm(userName: String)
-
- val collaboratorForm = mapping(
- "userName" -> trim(label("Username", text(required, collaborator)))
- )(CollaboratorForm.apply)
-
- // for web hook url addition
- case class WebHookForm(url: String)
-
- val webHookForm = mapping(
- "url" -> trim(label("url", text(required, webHook)))
- )(WebHookForm.apply)
-
- // for transfer ownership
- case class TransferOwnerShipForm(newOwner: String)
-
- val transferForm = mapping(
- "newOwner" -> trim(label("New owner", text(required, transferUser)))
- )(TransferOwnerShipForm.apply)
-
- /**
- * Redirect to the Options page.
- */
- get("/:owner/:repository/settings")(ownerOnly { repository =>
- redirect(s"/${repository.owner}/${repository.name}/settings/options")
- })
-
- /**
- * Display the Options page.
- */
- get("/:owner/:repository/settings/options")(ownerOnly {
- settings.html.options(_, flash.get("info"))
- })
-
- /**
- * Save the repository options.
- */
- post("/:owner/:repository/settings/options", optionsForm)(ownerOnly { (form, repository) =>
- saveRepositoryOptions(
- repository.owner,
- repository.name,
- form.description,
- if(repository.branchList.isEmpty) "master" else form.defaultBranch,
- repository.repository.parentUserName.map { _ =>
- repository.repository.isPrivate
- } getOrElse form.isPrivate
- )
- // Change repository name
- if(repository.name != form.repositoryName){
- // Update database
- renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
- // Move git repository
- defining(getRepositoryDir(repository.owner, repository.name)){ dir =>
- FileUtils.moveDirectory(dir, getRepositoryDir(repository.owner, form.repositoryName))
- }
- // Move wiki repository
- defining(getWikiRepositoryDir(repository.owner, repository.name)){ dir =>
- FileUtils.moveDirectory(dir, getWikiRepositoryDir(repository.owner, form.repositoryName))
- }
- }
- flash += "info" -> "Repository settings has been updated."
- redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
- })
-
- /**
- * Display the Collaborators page.
- */
- get("/:owner/:repository/settings/collaborators")(ownerOnly { repository =>
- settings.html.collaborators(
- getCollaborators(repository.owner, repository.name),
- getAccountByUserName(repository.owner).get.isGroupAccount,
- repository)
- })
-
- /**
- * Add the collaborator.
- */
- post("/:owner/:repository/settings/collaborators/add", collaboratorForm)(ownerOnly { (form, repository) =>
- if(!getAccountByUserName(repository.owner).get.isGroupAccount){
- addCollaborator(repository.owner, repository.name, form.userName)
- }
- 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")
- })
-
- /**
- * Display the web hook page.
- */
- get("/:owner/:repository/settings/hooks")(ownerOnly { repository =>
- settings.html.hooks(getWebHookURLs(repository.owner, repository.name), repository, flash.get("info"))
- })
-
- /**
- * Add the web hook URL.
- */
- post("/:owner/:repository/settings/hooks/add", webHookForm)(ownerOnly { (form, repository) =>
- addWebHookURL(repository.owner, repository.name, form.url)
- redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
- })
-
- /**
- * Delete the web hook URL.
- */
- get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
- deleteWebHookURL(repository.owner, repository.name, params("url"))
- redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
- })
-
- /**
- * Send the test request to registered web hook URLs.
- */
- get("/:owner/:repository/settings/hooks/test")(ownerOnly { repository =>
- using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
- import scala.collection.JavaConverters._
- val commits = git.log
- .add(git.getRepository.resolve(repository.repository.defaultBranch))
- .setMaxCount(3)
- .call.iterator.asScala.map(new CommitInfo(_))
-
- getWebHookURLs(repository.owner, repository.name) match {
- case webHookURLs if(webHookURLs.nonEmpty) =>
- for(ownerAccount <- getAccountByUserName(repository.owner)){
- callWebHook(repository.owner, repository.name, webHookURLs,
- WebHookPayload(git, ownerAccount, "refs/heads/" + repository.repository.defaultBranch, repository, commits.toList, ownerAccount))
- }
- case _ =>
- }
-
- flash += "info" -> "Test payload deployed!"
- }
- redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
- })
-
- /**
- * Display the danger zone.
- */
- get("/:owner/:repository/settings/danger")(ownerOnly {
- settings.html.danger(_)
- })
-
- /**
- * Transfer repository ownership.
- */
- post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
- // Change repository owner
- if(repository.owner != form.newOwner){
- // Update database
- renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
- // Move git repository
- defining(getRepositoryDir(repository.owner, repository.name)){ dir =>
- FileUtils.moveDirectory(dir, getRepositoryDir(form.newOwner, repository.name))
- }
- // Move wiki repository
- defining(getWikiRepositoryDir(repository.owner, repository.name)){ dir =>
- FileUtils.moveDirectory(dir, getWikiRepositoryDir(form.newOwner, repository.name))
- }
- }
- redirect(s"/${form.newOwner}/${repository.name}")
- })
-
- /**
- * Delete the repository.
- */
- post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
- deleteRepository(repository.owner, repository.name)
-
- FileUtils.deleteDirectory(getRepositoryDir(repository.owner, repository.name))
- FileUtils.deleteDirectory(getWikiRepositoryDir(repository.owner, repository.name))
- FileUtils.deleteDirectory(getTemporaryDir(repository.owner, repository.name))
-
- redirect(s"/${repository.owner}")
- })
-
- /**
- * Provides duplication check for web hook url.
- */
- private def webHook: Constraint = new Constraint(){
- override def validate(name: String, value: String, messages: Messages): Option[String] =
- getWebHookURLs(params("owner"), params("repository")).map(_.url).find(_ == value).map(_ => "URL had been registered already.")
- }
-
- /**
- * Provides Constraint to validate the collaborator name.
- */
- private def collaborator: Constraint = new Constraint(){
- override def validate(name: String, value: String, messages: Messages): Option[String] =
- getAccountByUserName(value) match {
- case None => Some("User does not exist.")
- case Some(x) if(x.isGroupAccount)
- => Some("User does not exist.")
- case Some(x) if(x.userName == params("owner") || getCollaborators(params("owner"), params("repository")).contains(x.userName))
- => Some("User can access this repository already.")
- case _ => None
- }
- }
-
- /**
- * Duplicate check for the rename repository name.
- */
- private def renameRepositoryName: Constraint = new Constraint(){
- override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] =
- params.get("repository").filter(_ != value).flatMap { _ =>
- params.get("owner").flatMap { userName =>
- getRepositoryNamesOfUser(userName).find(_ == value).map(_ => "Repository already exists.")
- }
- }
- }
-
- /**
- * Provides Constraint to validate the repository transfer user.
- */
- private def transferUser: Constraint = new Constraint(){
- override def validate(name: String, value: String, messages: Messages): Option[String] =
- getAccountByUserName(value) match {
- case None => Some("User does not exist.")
- case Some(x) => if(x.userName == params("owner")){
- Some("This is current repository owner.")
- } else {
- params.get("repository").flatMap { repositoryName =>
- getRepositoryNamesOfUser(x.userName).find(_ == repositoryName).map{ _ => "User already has same repository." }
- }
- }
- }
- }
+package app
+
+import service._
+import util.Directory._
+import util.ControlUtil._
+import util.Implicits._
+import util.{UsersAuthenticator, OwnerAuthenticator}
+import util.JGitUtil.CommitInfo
+import jp.sf.amateras.scalatra.forms._
+import org.apache.commons.io.FileUtils
+import org.scalatra.i18n.Messages
+import service.WebHookService.WebHookPayload
+import org.eclipse.jgit.api.Git
+
+class RepositorySettingsController extends RepositorySettingsControllerBase
+ with RepositoryService with AccountService with WebHookService
+ with OwnerAuthenticator with UsersAuthenticator
+
+trait RepositorySettingsControllerBase extends ControllerBase {
+ self: RepositoryService with AccountService with WebHookService
+ with OwnerAuthenticator with UsersAuthenticator =>
+
+ // for repository options
+ case class OptionsForm(repositoryName: String, description: Option[String], defaultBranch: String, isPrivate: Boolean)
+
+ val optionsForm = mapping(
+ "repositoryName" -> trim(label("Description" , text(required, maxlength(40), identifier, renameRepositoryName))),
+ "description" -> trim(label("Description" , optional(text()))),
+ "defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100)))),
+ "isPrivate" -> trim(label("Repository Type", boolean()))
+ )(OptionsForm.apply)
+
+ // for collaborator addition
+ case class CollaboratorForm(userName: String)
+
+ val collaboratorForm = mapping(
+ "userName" -> trim(label("Username", text(required, collaborator)))
+ )(CollaboratorForm.apply)
+
+ // for web hook url addition
+ case class WebHookForm(url: String)
+
+ val webHookForm = mapping(
+ "url" -> trim(label("url", text(required, webHook)))
+ )(WebHookForm.apply)
+
+ // for transfer ownership
+ case class TransferOwnerShipForm(newOwner: String)
+
+ val transferForm = mapping(
+ "newOwner" -> trim(label("New owner", text(required, transferUser)))
+ )(TransferOwnerShipForm.apply)
+
+ /**
+ * Redirect to the Options page.
+ */
+ get("/:owner/:repository/settings")(ownerOnly { repository =>
+ redirect(s"/${repository.owner}/${repository.name}/settings/options")
+ })
+
+ /**
+ * Display the Options page.
+ */
+ get("/:owner/:repository/settings/options")(ownerOnly {
+ settings.html.options(_, flash.get("info"))
+ })
+
+ /**
+ * Save the repository options.
+ */
+ post("/:owner/:repository/settings/options", optionsForm)(ownerOnly { (form, repository) =>
+ saveRepositoryOptions(
+ repository.owner,
+ repository.name,
+ form.description,
+ if(repository.branchList.isEmpty) "master" else form.defaultBranch,
+ repository.repository.parentUserName.map { _ =>
+ repository.repository.isPrivate
+ } getOrElse form.isPrivate
+ )
+ // Change repository name
+ if(repository.name != form.repositoryName){
+ // Update database
+ renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
+ // Move git repository
+ defining(getRepositoryDir(repository.owner, repository.name)){ dir =>
+ FileUtils.moveDirectory(dir, getRepositoryDir(repository.owner, form.repositoryName))
+ }
+ // Move wiki repository
+ defining(getWikiRepositoryDir(repository.owner, repository.name)){ dir =>
+ FileUtils.moveDirectory(dir, getWikiRepositoryDir(repository.owner, form.repositoryName))
+ }
+ }
+ flash += "info" -> "Repository settings has been updated."
+ redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
+ })
+
+ /**
+ * Display the Collaborators page.
+ */
+ get("/:owner/:repository/settings/collaborators")(ownerOnly { repository =>
+ settings.html.collaborators(
+ getCollaborators(repository.owner, repository.name),
+ getAccountByUserName(repository.owner).get.isGroupAccount,
+ repository)
+ })
+
+ /**
+ * Add the collaborator.
+ */
+ post("/:owner/:repository/settings/collaborators/add", collaboratorForm)(ownerOnly { (form, repository) =>
+ if(!getAccountByUserName(repository.owner).get.isGroupAccount){
+ addCollaborator(repository.owner, repository.name, form.userName)
+ }
+ 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")
+ })
+
+ /**
+ * Display the web hook page.
+ */
+ get("/:owner/:repository/settings/hooks")(ownerOnly { repository =>
+ settings.html.hooks(getWebHookURLs(repository.owner, repository.name), repository, flash.get("info"))
+ })
+
+ /**
+ * Add the web hook URL.
+ */
+ post("/:owner/:repository/settings/hooks/add", webHookForm)(ownerOnly { (form, repository) =>
+ addWebHookURL(repository.owner, repository.name, form.url)
+ redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
+ })
+
+ /**
+ * Delete the web hook URL.
+ */
+ get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
+ deleteWebHookURL(repository.owner, repository.name, params("url"))
+ redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
+ })
+
+ /**
+ * Send the test request to registered web hook URLs.
+ */
+ get("/:owner/:repository/settings/hooks/test")(ownerOnly { repository =>
+ using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
+ import scala.collection.JavaConverters._
+ val commits = git.log
+ .add(git.getRepository.resolve(repository.repository.defaultBranch))
+ .setMaxCount(3)
+ .call.iterator.asScala.map(new CommitInfo(_))
+
+ getWebHookURLs(repository.owner, repository.name) match {
+ case webHookURLs if(webHookURLs.nonEmpty) =>
+ for(ownerAccount <- getAccountByUserName(repository.owner)){
+ callWebHook(repository.owner, repository.name, webHookURLs,
+ WebHookPayload(git, ownerAccount, "refs/heads/" + repository.repository.defaultBranch, repository, commits.toList, ownerAccount))
+ }
+ case _ =>
+ }
+
+ flash += "info" -> "Test payload deployed!"
+ }
+ redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
+ })
+
+ /**
+ * Display the danger zone.
+ */
+ get("/:owner/:repository/settings/danger")(ownerOnly {
+ settings.html.danger(_)
+ })
+
+ /**
+ * Transfer repository ownership.
+ */
+ post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
+ // Change repository owner
+ if(repository.owner != form.newOwner){
+ // Update database
+ renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
+ // Move git repository
+ defining(getRepositoryDir(repository.owner, repository.name)){ dir =>
+ FileUtils.moveDirectory(dir, getRepositoryDir(form.newOwner, repository.name))
+ }
+ // Move wiki repository
+ defining(getWikiRepositoryDir(repository.owner, repository.name)){ dir =>
+ FileUtils.moveDirectory(dir, getWikiRepositoryDir(form.newOwner, repository.name))
+ }
+ }
+ redirect(s"/${form.newOwner}/${repository.name}")
+ })
+
+ /**
+ * Delete the repository.
+ */
+ post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
+ deleteRepository(repository.owner, repository.name)
+
+ FileUtils.deleteDirectory(getRepositoryDir(repository.owner, repository.name))
+ FileUtils.deleteDirectory(getWikiRepositoryDir(repository.owner, repository.name))
+ FileUtils.deleteDirectory(getTemporaryDir(repository.owner, repository.name))
+
+ redirect(s"/${repository.owner}")
+ })
+
+ /**
+ * Provides duplication check for web hook url.
+ */
+ private def webHook: Constraint = new Constraint(){
+ override def validate(name: String, value: String, messages: Messages): Option[String] =
+ getWebHookURLs(params("owner"), params("repository")).map(_.url).find(_ == value).map(_ => "URL had been registered already.")
+ }
+
+ /**
+ * Provides Constraint to validate the collaborator name.
+ */
+ private def collaborator: Constraint = new Constraint(){
+ override def validate(name: String, value: String, messages: Messages): Option[String] =
+ getAccountByUserName(value) match {
+ case None => Some("User does not exist.")
+ case Some(x) if(x.isGroupAccount)
+ => Some("User does not exist.")
+ case Some(x) if(x.userName == params("owner") || getCollaborators(params("owner"), params("repository")).contains(x.userName))
+ => Some("User can access this repository already.")
+ case _ => None
+ }
+ }
+
+ /**
+ * Duplicate check for the rename repository name.
+ */
+ private def renameRepositoryName: Constraint = new Constraint(){
+ override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] =
+ params.get("repository").filter(_ != value).flatMap { _ =>
+ params.get("owner").flatMap { userName =>
+ getRepositoryNamesOfUser(userName).find(_ == value).map(_ => "Repository already exists.")
+ }
+ }
+ }
+
+ /**
+ * Provides Constraint to validate the repository transfer user.
+ */
+ private def transferUser: Constraint = new Constraint(){
+ override def validate(name: String, value: String, messages: Messages): Option[String] =
+ getAccountByUserName(value) match {
+ case None => Some("User does not exist.")
+ case Some(x) => if(x.userName == params("owner")){
+ Some("This is current repository owner.")
+ } else {
+ params.get("repository").flatMap { repositoryName =>
+ getRepositoryNamesOfUser(x.userName).find(_ == repositoryName).map{ _ => "User already has same repository." }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/scala/model/Account.scala b/src/main/scala/model/Account.scala
index 287bc41..7880f3e 100644
--- a/src/main/scala/model/Account.scala
+++ b/src/main/scala/model/Account.scala
@@ -1,39 +1,39 @@
-package model
-
-trait AccountComponent { self: Profile =>
- import profile.simple._
- import self._
-
- lazy val Accounts = TableQuery[Accounts]
-
- class Accounts(tag: Tag) extends Table[Account](tag, "ACCOUNT") {
- val userName = column[String]("USER_NAME", O PrimaryKey)
- val fullName = column[String]("FULL_NAME")
- val mailAddress = column[String]("MAIL_ADDRESS")
- val password = column[String]("PASSWORD")
- val isAdmin = column[Boolean]("ADMINISTRATOR")
- val url = column[String]("URL")
- val registeredDate = column[java.util.Date]("REGISTERED_DATE")
- val updatedDate = column[java.util.Date]("UPDATED_DATE")
- val lastLoginDate = column[java.util.Date]("LAST_LOGIN_DATE")
- 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)
- }
-
- case class Account(
- userName: String,
- fullName: String,
- mailAddress: String,
- password: String,
- isAdmin: Boolean,
- url: Option[String],
- registeredDate: java.util.Date,
- updatedDate: java.util.Date,
- lastLoginDate: Option[java.util.Date],
- image: Option[String],
- isGroupAccount: Boolean,
- isRemoved: Boolean
- )
-}
+package model
+
+trait AccountComponent { self: Profile =>
+ import profile.simple._
+ import self._
+
+ lazy val Accounts = TableQuery[Accounts]
+
+ class Accounts(tag: Tag) extends Table[Account](tag, "ACCOUNT") {
+ val userName = column[String]("USER_NAME", O PrimaryKey)
+ val fullName = column[String]("FULL_NAME")
+ val mailAddress = column[String]("MAIL_ADDRESS")
+ val password = column[String]("PASSWORD")
+ val isAdmin = column[Boolean]("ADMINISTRATOR")
+ val url = column[String]("URL")
+ val registeredDate = column[java.util.Date]("REGISTERED_DATE")
+ val updatedDate = column[java.util.Date]("UPDATED_DATE")
+ val lastLoginDate = column[java.util.Date]("LAST_LOGIN_DATE")
+ 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)
+ }
+
+ case class Account(
+ userName: String,
+ fullName: String,
+ mailAddress: String,
+ password: String,
+ isAdmin: Boolean,
+ url: Option[String],
+ registeredDate: java.util.Date,
+ updatedDate: java.util.Date,
+ lastLoginDate: Option[java.util.Date],
+ image: Option[String],
+ isGroupAccount: Boolean,
+ isRemoved: Boolean
+ )
+}
diff --git a/src/main/scala/model/BasicTemplate.scala b/src/main/scala/model/BasicTemplate.scala
index d6800da..e0460a9 100644
--- a/src/main/scala/model/BasicTemplate.scala
+++ b/src/main/scala/model/BasicTemplate.scala
@@ -1,47 +1,47 @@
-package model
-
-protected[model] trait TemplateComponent { self: Profile =>
- import profile.simple._
-
- trait BasicTemplate { self: Table[_] =>
- val userName = column[String]("USER_NAME")
- val repositoryName = column[String]("REPOSITORY_NAME")
-
- def byRepository(owner: String, repository: String) =
- (userName is owner.bind) && (repositoryName is repository.bind)
-
- def byRepository(userName: Column[String], repositoryName: Column[String]) =
- (this.userName is userName) && (this.repositoryName is repositoryName)
- }
-
- trait IssueTemplate extends BasicTemplate { self: Table[_] =>
- val issueId = column[Int]("ISSUE_ID")
-
- def byIssue(owner: String, repository: String, issueId: Int) =
- byRepository(owner, repository) && (this.issueId is issueId.bind)
-
- def byIssue(userName: Column[String], repositoryName: Column[String], issueId: Column[Int]) =
- byRepository(userName, repositoryName) && (this.issueId is issueId)
- }
-
- trait LabelTemplate extends BasicTemplate { self: Table[_] =>
- val labelId = column[Int]("LABEL_ID")
-
- def byLabel(owner: String, repository: String, labelId: Int) =
- byRepository(owner, repository) && (this.labelId is labelId.bind)
-
- def byLabel(userName: Column[String], repositoryName: Column[String], labelId: Column[Int]) =
- byRepository(userName, repositoryName) && (this.labelId is labelId)
- }
-
- trait MilestoneTemplate extends BasicTemplate { self: Table[_] =>
- val milestoneId = column[Int]("MILESTONE_ID")
-
- def byMilestone(owner: String, repository: String, milestoneId: Int) =
- byRepository(owner, repository) && (this.milestoneId is milestoneId.bind)
-
- def byMilestone(userName: Column[String], repositoryName: Column[String], milestoneId: Column[Int]) =
- byRepository(userName, repositoryName) && (this.milestoneId is milestoneId)
- }
-
-}
+package model
+
+protected[model] trait TemplateComponent { self: Profile =>
+ import profile.simple._
+
+ trait BasicTemplate { self: Table[_] =>
+ val userName = column[String]("USER_NAME")
+ val repositoryName = column[String]("REPOSITORY_NAME")
+
+ def byRepository(owner: String, repository: String) =
+ (userName is owner.bind) && (repositoryName is repository.bind)
+
+ def byRepository(userName: Column[String], repositoryName: Column[String]) =
+ (this.userName is userName) && (this.repositoryName is repositoryName)
+ }
+
+ trait IssueTemplate extends BasicTemplate { self: Table[_] =>
+ val issueId = column[Int]("ISSUE_ID")
+
+ def byIssue(owner: String, repository: String, issueId: Int) =
+ byRepository(owner, repository) && (this.issueId is issueId.bind)
+
+ def byIssue(userName: Column[String], repositoryName: Column[String], issueId: Column[Int]) =
+ byRepository(userName, repositoryName) && (this.issueId is issueId)
+ }
+
+ trait LabelTemplate extends BasicTemplate { self: Table[_] =>
+ val labelId = column[Int]("LABEL_ID")
+
+ def byLabel(owner: String, repository: String, labelId: Int) =
+ byRepository(owner, repository) && (this.labelId is labelId.bind)
+
+ def byLabel(userName: Column[String], repositoryName: Column[String], labelId: Column[Int]) =
+ byRepository(userName, repositoryName) && (this.labelId is labelId)
+ }
+
+ trait MilestoneTemplate extends BasicTemplate { self: Table[_] =>
+ val milestoneId = column[Int]("MILESTONE_ID")
+
+ def byMilestone(owner: String, repository: String, milestoneId: Int) =
+ byRepository(owner, repository) && (this.milestoneId is milestoneId.bind)
+
+ def byMilestone(userName: Column[String], repositoryName: Column[String], milestoneId: Column[Int]) =
+ byRepository(userName, repositoryName) && (this.milestoneId is milestoneId)
+ }
+
+}
diff --git a/src/main/scala/model/Issue.scala b/src/main/scala/model/Issue.scala
index bc594e3..d18c098 100644
--- a/src/main/scala/model/Issue.scala
+++ b/src/main/scala/model/Issue.scala
@@ -1,48 +1,48 @@
-package model
-
-trait IssueComponent extends TemplateComponent { self: Profile =>
- import profile.simple._
- import self._
-
- lazy val IssueId = TableQuery[IssueId]
- lazy val IssueOutline = TableQuery[IssueOutline]
- lazy val Issues = TableQuery[Issues]
-
- class IssueId(tag: Tag) extends Table[(String, String, Int)](tag, "ISSUE_ID") with IssueTemplate {
- def * = (userName, repositoryName, issueId)
- def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
- }
-
- class IssueOutline(tag: Tag) extends Table[(String, String, Int, Int)](tag, "ISSUE_OUTLINE_VIEW") with IssueTemplate {
- val commentCount = column[Int]("COMMENT_COUNT")
- def * = (userName, repositoryName, issueId, commentCount)
- }
-
- class Issues(tag: Tag) extends Table[Issue](tag, "ISSUE") with IssueTemplate with MilestoneTemplate {
- val openedUserName = column[String]("OPENED_USER_NAME")
- val assignedUserName = column[String]("ASSIGNED_USER_NAME")
- val title = column[String]("TITLE")
- val content = column[String]("CONTENT")
- val closed = column[Boolean]("CLOSED")
- val registeredDate = column[java.util.Date]("REGISTERED_DATE")
- val updatedDate = column[java.util.Date]("UPDATED_DATE")
- val pullRequest = column[Boolean]("PULL_REQUEST")
- def * = (userName, repositoryName, issueId, openedUserName, milestoneId.?, assignedUserName.?, title, content.?, closed, registeredDate, updatedDate, pullRequest) <> (Issue.tupled, Issue.unapply)
-
- def byPrimaryKey(owner: String, repository: String, issueId: Int) = byIssue(owner, repository, issueId)
- }
-
- case class Issue(
- userName: String,
- repositoryName: String,
- issueId: Int,
- openedUserName: String,
- milestoneId: Option[Int],
- assignedUserName: Option[String],
- title: String,
- content: Option[String],
- closed: Boolean,
- registeredDate: java.util.Date,
- updatedDate: java.util.Date,
- isPullRequest: Boolean)
-}
+package model
+
+trait IssueComponent extends TemplateComponent { self: Profile =>
+ import profile.simple._
+ import self._
+
+ lazy val IssueId = TableQuery[IssueId]
+ lazy val IssueOutline = TableQuery[IssueOutline]
+ lazy val Issues = TableQuery[Issues]
+
+ class IssueId(tag: Tag) extends Table[(String, String, Int)](tag, "ISSUE_ID") with IssueTemplate {
+ def * = (userName, repositoryName, issueId)
+ def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
+ }
+
+ class IssueOutline(tag: Tag) extends Table[(String, String, Int, Int)](tag, "ISSUE_OUTLINE_VIEW") with IssueTemplate {
+ val commentCount = column[Int]("COMMENT_COUNT")
+ def * = (userName, repositoryName, issueId, commentCount)
+ }
+
+ class Issues(tag: Tag) extends Table[Issue](tag, "ISSUE") with IssueTemplate with MilestoneTemplate {
+ val openedUserName = column[String]("OPENED_USER_NAME")
+ val assignedUserName = column[String]("ASSIGNED_USER_NAME")
+ val title = column[String]("TITLE")
+ val content = column[String]("CONTENT")
+ val closed = column[Boolean]("CLOSED")
+ val registeredDate = column[java.util.Date]("REGISTERED_DATE")
+ val updatedDate = column[java.util.Date]("UPDATED_DATE")
+ val pullRequest = column[Boolean]("PULL_REQUEST")
+ def * = (userName, repositoryName, issueId, openedUserName, milestoneId.?, assignedUserName.?, title, content.?, closed, registeredDate, updatedDate, pullRequest) <> (Issue.tupled, Issue.unapply)
+
+ def byPrimaryKey(owner: String, repository: String, issueId: Int) = byIssue(owner, repository, issueId)
+ }
+
+ case class Issue(
+ userName: String,
+ repositoryName: String,
+ issueId: Int,
+ openedUserName: String,
+ milestoneId: Option[Int],
+ assignedUserName: Option[String],
+ title: String,
+ content: Option[String],
+ closed: Boolean,
+ registeredDate: java.util.Date,
+ updatedDate: java.util.Date,
+ isPullRequest: Boolean)
+}
diff --git a/src/main/scala/model/IssueComment.scala b/src/main/scala/model/IssueComment.scala
index ea02285..8d42702 100644
--- a/src/main/scala/model/IssueComment.scala
+++ b/src/main/scala/model/IssueComment.scala
@@ -1,34 +1,34 @@
-package model
-
-trait IssueCommentComponent extends TemplateComponent { self: Profile =>
- import profile.simple._
- import self._
-
- lazy val IssueComments = new TableQuery(tag => new IssueComments(tag)){
- def autoInc = this returning this.map(_.commentId)
- }
-
- class IssueComments(tag: Tag) extends Table[IssueComment](tag, "ISSUE_COMMENT") with IssueTemplate {
- val commentId = column[Int]("COMMENT_ID", O AutoInc)
- val action = column[String]("ACTION")
- val commentedUserName = column[String]("COMMENTED_USER_NAME")
- val content = column[String]("CONTENT")
- val registeredDate = column[java.util.Date]("REGISTERED_DATE")
- val updatedDate = column[java.util.Date]("UPDATED_DATE")
- def * = (userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate) <> (IssueComment.tupled, IssueComment.unapply)
-
- def byPrimaryKey(commentId: Int) = this.commentId is commentId.bind
- }
-
- case class IssueComment(
- userName: String,
- repositoryName: String,
- issueId: Int,
- commentId: Int = 0,
- action: String,
- commentedUserName: String,
- content: String,
- registeredDate: java.util.Date,
- updatedDate: java.util.Date
- )
-}
+package model
+
+trait IssueCommentComponent extends TemplateComponent { self: Profile =>
+ import profile.simple._
+ import self._
+
+ lazy val IssueComments = new TableQuery(tag => new IssueComments(tag)){
+ def autoInc = this returning this.map(_.commentId)
+ }
+
+ class IssueComments(tag: Tag) extends Table[IssueComment](tag, "ISSUE_COMMENT") with IssueTemplate {
+ val commentId = column[Int]("COMMENT_ID", O AutoInc)
+ val action = column[String]("ACTION")
+ val commentedUserName = column[String]("COMMENTED_USER_NAME")
+ val content = column[String]("CONTENT")
+ val registeredDate = column[java.util.Date]("REGISTERED_DATE")
+ val updatedDate = column[java.util.Date]("UPDATED_DATE")
+ def * = (userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate) <> (IssueComment.tupled, IssueComment.unapply)
+
+ def byPrimaryKey(commentId: Int) = this.commentId is commentId.bind
+ }
+
+ case class IssueComment(
+ userName: String,
+ repositoryName: String,
+ issueId: Int,
+ commentId: Int = 0,
+ action: String,
+ commentedUserName: String,
+ content: String,
+ registeredDate: java.util.Date,
+ updatedDate: java.util.Date
+ )
+}
diff --git a/src/main/scala/model/package.scala b/src/main/scala/model/package.scala
index 36a3504..6c70c66 100644
--- a/src/main/scala/model/package.scala
+++ b/src/main/scala/model/package.scala
@@ -1,22 +1,22 @@
-package object model extends {
- // TODO
- val profile = slick.driver.H2Driver
-
-} with AccountComponent
- with ActivityComponent
- with CollaboratorComponent
- with GroupMemberComponent
- with IssueComponent
- with IssueCommentComponent
- with IssueLabelComponent
- with LabelComponent
- with MilestoneComponent
- with PullRequestComponent
- with RepositoryComponent
- with SshKeyComponent
- with WebHookComponent with Profile {
- /**
- * Returns system date.
- */
- def currentDate = new java.util.Date()
-}
+package object model extends {
+ // TODO
+ val profile = slick.driver.H2Driver
+
+} with AccountComponent
+ with ActivityComponent
+ with CollaboratorComponent
+ with GroupMemberComponent
+ with IssueComponent
+ with IssueCommentComponent
+ with IssueLabelComponent
+ with LabelComponent
+ with MilestoneComponent
+ with PullRequestComponent
+ with RepositoryComponent
+ with SshKeyComponent
+ with WebHookComponent with Profile {
+ /**
+ * Returns system date.
+ */
+ def currentDate = new java.util.Date()
+}
diff --git a/src/main/scala/service/IssuesService.scala b/src/main/scala/service/IssuesService.scala
index 0e6f7d5..6f421be 100644
--- a/src/main/scala/service/IssuesService.scala
+++ b/src/main/scala/service/IssuesService.scala
@@ -1,385 +1,385 @@
-package service
-
-import scala.slick.jdbc.{StaticQuery => Q}
-import Q.interpolation
-
-import model._
-import profile.simple._
-import util.Implicits._
-import util.StringUtil._
-
-trait IssuesService {
- import IssuesService._
-
- def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) =
- if (issueId forall (_.isDigit))
- Issues filter (_.byPrimaryKey(owner, repository, issueId.toInt)) firstOption
- else None
-
- def getComments(owner: String, repository: String, issueId: Int)(implicit s: Session) =
- IssueComments filter (_.byIssue(owner, repository, issueId)) list
-
- def getComment(owner: String, repository: String, commentId: String)(implicit s: Session) =
- if (commentId forall (_.isDigit))
- IssueComments filter { t =>
- t.byPrimaryKey(commentId.toInt) && t.byRepository(owner, repository)
- } firstOption
- else None
-
- def getIssueLabels(owner: String, repository: String, issueId: Int)(implicit s: Session) =
- IssueLabels
- .innerJoin(Labels).on { (t1, t2) =>
- t1.byLabel(t2.userName, t2.repositoryName, t2.labelId)
- }
- .filter ( _._1.byIssue(owner, repository, issueId) )
- .map ( _._2 )
- .list
-
- def getIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit s: Session) =
- IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) firstOption
-
- /**
- * Returns the count of the search result against issues.
- *
- * @param condition the search condition
- * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
- * @param onlyPullRequest if true then counts only pull request, false then counts both of issue and pull request.
- * @param repos Tuple of the repository owner and the repository name
- * @return the count of the search result
- */
- def countIssue(condition: IssueSearchCondition, filterUser: Map[String, String], onlyPullRequest: Boolean,
- repos: (String, String)*)(implicit s: Session): Int =
- // TODO check SQL
- Query(searchIssueQuery(repos, condition, filterUser, onlyPullRequest).length).first
- /**
- * Returns the Map which contains issue count for each labels.
- *
- * @param owner the repository owner
- * @param repository the repository name
- * @param condition the search condition
- * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
- * @return the Map which contains issue count for each labels (key is label name, value is issue count)
- */
- def countIssueGroupByLabels(owner: String, repository: String, condition: IssueSearchCondition,
- filterUser: Map[String, String])(implicit s: Session): Map[String, Int] = {
-
- searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), filterUser, false)
- .innerJoin(IssueLabels).on { (t1, t2) =>
- t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
- }
- .innerJoin(Labels).on { case ((t1, t2), t3) =>
- t2.byLabel(t3.userName, t3.repositoryName, t3.labelId)
- }
- .groupBy { case ((t1, t2), t3) =>
- t3.labelName
- }
- .map { case (labelName, t) =>
- labelName -> t.length
- }
- .toMap
- }
- /**
- * Returns list which contains issue count for each repository.
- * If the issue does not exist, its repository is not included in the result.
- *
- * @param condition the search condition
- * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
- * @param onlyPullRequest if true then returns only pull request, false then returns both of issue and pull request.
- * @param repos Tuple of the repository owner and the repository name
- * @return list which contains issue count for each repository
- */
- def countIssueGroupByRepository(
- condition: IssueSearchCondition, filterUser: Map[String, String], onlyPullRequest: Boolean,
- repos: (String, String)*)(implicit s: Session): List[(String, String, Int)] = {
- searchIssueQuery(repos, condition.copy(repo = None), filterUser, onlyPullRequest)
- .groupBy { t =>
- t.userName -> t.repositoryName
- }
- .map { case (repo, t) =>
- (repo._1, repo._2, t.length)
- }
- .sortBy(_._3 desc)
- .list
- }
-
- /**
- * Returns the search result against issues.
- *
- * @param condition the search condition
- * @param filterUser the filter user name (key is "all", "assigned", "created_by" or "not_created_by", value is the user name)
- * @param onlyPullRequest if true then returns only pull request, false then returns both of issue and pull request.
- * @param offset the offset for pagination
- * @param limit the limit for pagination
- * @param repos Tuple of the repository owner and the repository name
- * @return the search result (list of tuples which contain issue, labels and comment count)
- */
- def searchIssue(condition: IssueSearchCondition, filterUser: Map[String, String], onlyPullRequest: Boolean,
- offset: Int, limit: Int, repos: (String, String)*)
- (implicit s: Session): List[(Issue, List[Label], Int)] = {
-
- // get issues and comment count and labels
- searchIssueQuery(repos, condition, filterUser, onlyPullRequest)
- .innerJoin(IssueOutline).on { (t1, t2) => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) }
- .sortBy { case (t1, t2) =>
- (condition.sort match {
- case "created" => t1.registeredDate
- case "comments" => t2.commentCount
- case "updated" => t1.updatedDate
- }) match {
- case sort => condition.direction match {
- case "asc" => sort asc
- case "desc" => sort desc
- }
- }
- }
- .drop(offset).take(limit)
- .leftJoin (IssueLabels) .on { case ((t1, t2), t3) => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) }
- .leftJoin (Labels) .on { case (((t1, t2), t3), t4) => t3.byLabel(t4.userName, t4.repositoryName, t4.labelId) }
- .map { case (((t1, t2), t3), t4) =>
- (t1, t2.commentCount, t4.labelId.?, t4.labelName.?, t4.color.?)
- }
- .list
- .splitWith { (c1, c2) =>
- c1._1.userName == c2._1.userName &&
- c1._1.repositoryName == c2._1.repositoryName &&
- c1._1.issueId == c2._1.issueId
- }
- .map { issues => issues.head match {
- case (issue, commentCount, _,_,_) =>
- (issue,
- issues.flatMap { t => t._3.map (
- Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get)
- )} toList,
- commentCount)
- }} toList
- }
-
- /**
- * Assembles query for conditional issue searching.
- */
- private def searchIssueQuery(repos: Seq[(String, String)], condition: IssueSearchCondition,
- filterUser: Map[String, String], onlyPullRequest: Boolean)(implicit s: Session) =
- Issues filter { t1 =>
- condition.repo
- .map { _.split('/') match { case array => Seq(array(0) -> array(1)) } }
- .getOrElse (repos)
- .map { case (owner, repository) => t1.byRepository(owner, repository) }
- .foldLeft[Column[Boolean]](false) ( _ || _ ) &&
- (t1.closed is (condition.state == "closed").bind) &&
- (t1.milestoneId is condition.milestoneId.get.get.bind, condition.milestoneId.flatten.isDefined) &&
- (t1.milestoneId isNull, condition.milestoneId == Some(None)) &&
- (t1.assignedUserName is filterUser("assigned").bind, filterUser.get("assigned").isDefined) &&
- (t1.openedUserName is filterUser("created_by").bind, filterUser.get("created_by").isDefined) &&
- (t1.openedUserName isNot filterUser("not_created_by").bind, filterUser.get("not_created_by").isDefined) &&
- (t1.pullRequest is true.bind, onlyPullRequest) &&
- (IssueLabels filter { t2 =>
- (t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) &&
- (t2.labelId in
- (Labels filter { t3 =>
- (t3.byRepository(t1.userName, t1.repositoryName)) &&
- (t3.labelName inSetBind condition.labels)
- } map(_.labelId)))
- } exists, condition.labels.nonEmpty)
- }
-
- def createIssue(owner: String, repository: String, loginUser: String, title: String, content: Option[String],
- assignedUserName: Option[String], milestoneId: Option[Int],
- isPullRequest: Boolean = false)(implicit s: Session) =
- // next id number
- sql"SELECT ISSUE_ID + 1 FROM ISSUE_ID WHERE USER_NAME = $owner AND REPOSITORY_NAME = $repository FOR UPDATE".as[Int]
- .firstOption.filter { id =>
- Issues insert Issue(
- owner,
- repository,
- id,
- loginUser,
- milestoneId,
- assignedUserName,
- title,
- content,
- false,
- currentDate,
- currentDate,
- isPullRequest)
-
- // increment issue id
- IssueId
- .filter (_.byPrimaryKey(owner, repository))
- .map (_.issueId)
- .update (id) > 0
- } get
-
- def registerIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit s: Session) =
- IssueLabels insert IssueLabel(owner, repository, issueId, labelId)
-
- def deleteIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit s: Session) =
- IssueLabels filter(_.byPrimaryKey(owner, repository, issueId, labelId)) delete
-
- def createComment(owner: String, repository: String, loginUser: String,
- issueId: Int, content: String, action: String)(implicit s: Session): Int =
- IssueComments.autoInc insert IssueComment(
- userName = owner,
- repositoryName = repository,
- issueId = issueId,
- action = action,
- commentedUserName = loginUser,
- content = content,
- registeredDate = currentDate,
- updatedDate = currentDate)
-
- def updateIssue(owner: String, repository: String, issueId: Int,
- title: String, content: Option[String])(implicit s: Session) =
- Issues
- .filter (_.byPrimaryKey(owner, repository, issueId))
- .map { t =>
- (t.title, t.content.?, t.updatedDate)
- }
- .update (title, content, currentDate)
-
- def updateAssignedUserName(owner: String, repository: String, issueId: Int,
- assignedUserName: Option[String])(implicit s: Session) =
- Issues.filter (_.byPrimaryKey(owner, repository, issueId)).map(_.assignedUserName?).update (assignedUserName)
-
- def updateMilestoneId(owner: String, repository: String, issueId: Int,
- milestoneId: Option[Int])(implicit s: Session) =
- Issues.filter (_.byPrimaryKey(owner, repository, issueId)).map(_.milestoneId?).update (milestoneId)
-
- def updateComment(commentId: Int, content: String)(implicit s: Session) =
- IssueComments
- .filter (_.byPrimaryKey(commentId))
- .map { t =>
- t.content -> t.updatedDate
- }
- .update (content, currentDate)
-
- def deleteComment(commentId: Int)(implicit s: Session) =
- IssueComments filter (_.byPrimaryKey(commentId)) delete
-
- def updateClosed(owner: String, repository: String, issueId: Int, closed: Boolean)(implicit s: Session) =
- Issues
- .filter (_.byPrimaryKey(owner, repository, issueId))
- .map { t =>
- t.closed -> t.updatedDate
- }
- .update (closed, currentDate)
-
- /**
- * Search issues by keyword.
- *
- * @param owner the repository owner
- * @param repository the repository name
- * @param query the keywords separated by whitespace.
- * @return issues with comment count and matched content of issue or comment
- */
- def searchIssuesByKeyword(owner: String, repository: String, query: String)
- (implicit s: Session): List[(Issue, Int, String)] = {
- import slick.driver.JdbcDriver.likeEncode
- val keywords = splitWords(query.toLowerCase)
-
- // Search Issue
- val issues = Issues
- .innerJoin(IssueOutline).on { case (t1, t2) =>
- t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
- }
- .filter { case (t1, t2) =>
- keywords.map { keyword =>
- (t1.title.toLowerCase like (s"%${likeEncode(keyword)}%", '^')) ||
- (t1.content.toLowerCase like (s"%${likeEncode(keyword)}%", '^'))
- } .reduceLeft(_ && _)
- }
- .map { case (t1, t2) =>
- (t1, 0, t1.content.?, t2.commentCount)
- }
-
- // Search IssueComment
- val comments = IssueComments
- .innerJoin(Issues).on { case (t1, t2) =>
- t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
- }
- .innerJoin(IssueOutline).on { case ((t1, t2), t3) =>
- t2.byIssue(t3.userName, t3.repositoryName, t3.issueId)
- }
- .filter { case ((t1, t2), t3) =>
- keywords.map { query =>
- t1.content.toLowerCase like (s"%${likeEncode(query)}%", '^')
- }.reduceLeft(_ && _)
- }
- .map { case ((t1, t2), t3) =>
- (t2, t1.commentId, t1.content.?, t3.commentCount)
- }
-
- issues.union(comments).sortBy { case (issue, commentId, _, _) =>
- issue.issueId -> commentId
- }.list.splitWith { case ((issue1, _, _, _), (issue2, _, _, _)) =>
- issue1.issueId == issue2.issueId
- }.map { _.head match {
- case (issue, _, content, commentCount) => (issue, commentCount, content.getOrElse(""))
- }
- }.toList
- }
-
- def closeIssuesFromMessage(message: String, userName: String, owner: String, repository: String)(implicit s: Session) = {
- extractCloseId(message).foreach { issueId =>
- for(issue <- getIssue(owner, repository, issueId) if !issue.closed){
- createComment(owner, repository, userName, issue.issueId, "Close", "close")
- updateClosed(owner, repository, issue.issueId, true)
- }
- }
- }
-}
-
-object IssuesService {
- import javax.servlet.http.HttpServletRequest
-
- val IssueLimit = 30
-
- case class IssueSearchCondition(
- labels: Set[String] = Set.empty,
- milestoneId: Option[Option[Int]] = None,
- repo: Option[String] = None,
- state: String = "open",
- sort: String = "created",
- direction: String = "desc"){
-
- def toURL: String =
- "?" + List(
- if(labels.isEmpty) None else Some("labels=" + urlEncode(labels.mkString(","))),
- milestoneId.map { id => "milestone=" + (id match {
- case Some(x) => x.toString
- case None => "none"
- })},
- repo.map("for=" + urlEncode(_)),
- Some("state=" + urlEncode(state)),
- Some("sort=" + urlEncode(sort)),
- Some("direction=" + urlEncode(direction))).flatten.mkString("&")
-
- }
-
- object IssueSearchCondition {
-
- private def param(request: HttpServletRequest, name: String, allow: Seq[String] = Nil): Option[String] = {
- val value = request.getParameter(name)
- if(value == null || value.isEmpty || (allow.nonEmpty && !allow.contains(value))) None else Some(value)
- }
-
- def apply(request: HttpServletRequest): IssueSearchCondition =
- IssueSearchCondition(
- param(request, "labels").map(_.split(",").toSet).getOrElse(Set.empty),
- param(request, "milestone").map{
- case "none" => None
- case x => x.toIntOpt
- },
- param(request, "for"),
- param(request, "state", Seq("open", "closed")).getOrElse("open"),
- param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"),
- param(request, "direction", Seq("asc", "desc")).getOrElse("desc"))
-
- def page(request: HttpServletRequest) = try {
- val i = param(request, "page").getOrElse("1").toInt
- if(i <= 0) 1 else i
- } catch {
- case e: NumberFormatException => 1
- }
- }
-
-}
+package service
+
+import scala.slick.jdbc.{StaticQuery => Q}
+import Q.interpolation
+
+import model._
+import profile.simple._
+import util.Implicits._
+import util.StringUtil._
+
+trait IssuesService {
+ import IssuesService._
+
+ def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) =
+ if (issueId forall (_.isDigit))
+ Issues filter (_.byPrimaryKey(owner, repository, issueId.toInt)) firstOption
+ else None
+
+ def getComments(owner: String, repository: String, issueId: Int)(implicit s: Session) =
+ IssueComments filter (_.byIssue(owner, repository, issueId)) list
+
+ def getComment(owner: String, repository: String, commentId: String)(implicit s: Session) =
+ if (commentId forall (_.isDigit))
+ IssueComments filter { t =>
+ t.byPrimaryKey(commentId.toInt) && t.byRepository(owner, repository)
+ } firstOption
+ else None
+
+ def getIssueLabels(owner: String, repository: String, issueId: Int)(implicit s: Session) =
+ IssueLabels
+ .innerJoin(Labels).on { (t1, t2) =>
+ t1.byLabel(t2.userName, t2.repositoryName, t2.labelId)
+ }
+ .filter ( _._1.byIssue(owner, repository, issueId) )
+ .map ( _._2 )
+ .list
+
+ def getIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit s: Session) =
+ IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) firstOption
+
+ /**
+ * Returns the count of the search result against issues.
+ *
+ * @param condition the search condition
+ * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
+ * @param onlyPullRequest if true then counts only pull request, false then counts both of issue and pull request.
+ * @param repos Tuple of the repository owner and the repository name
+ * @return the count of the search result
+ */
+ def countIssue(condition: IssueSearchCondition, filterUser: Map[String, String], onlyPullRequest: Boolean,
+ repos: (String, String)*)(implicit s: Session): Int =
+ // TODO check SQL
+ Query(searchIssueQuery(repos, condition, filterUser, onlyPullRequest).length).first
+ /**
+ * Returns the Map which contains issue count for each labels.
+ *
+ * @param owner the repository owner
+ * @param repository the repository name
+ * @param condition the search condition
+ * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
+ * @return the Map which contains issue count for each labels (key is label name, value is issue count)
+ */
+ def countIssueGroupByLabels(owner: String, repository: String, condition: IssueSearchCondition,
+ filterUser: Map[String, String])(implicit s: Session): Map[String, Int] = {
+
+ searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), filterUser, false)
+ .innerJoin(IssueLabels).on { (t1, t2) =>
+ t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
+ }
+ .innerJoin(Labels).on { case ((t1, t2), t3) =>
+ t2.byLabel(t3.userName, t3.repositoryName, t3.labelId)
+ }
+ .groupBy { case ((t1, t2), t3) =>
+ t3.labelName
+ }
+ .map { case (labelName, t) =>
+ labelName -> t.length
+ }
+ .toMap
+ }
+ /**
+ * Returns list which contains issue count for each repository.
+ * If the issue does not exist, its repository is not included in the result.
+ *
+ * @param condition the search condition
+ * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
+ * @param onlyPullRequest if true then returns only pull request, false then returns both of issue and pull request.
+ * @param repos Tuple of the repository owner and the repository name
+ * @return list which contains issue count for each repository
+ */
+ def countIssueGroupByRepository(
+ condition: IssueSearchCondition, filterUser: Map[String, String], onlyPullRequest: Boolean,
+ repos: (String, String)*)(implicit s: Session): List[(String, String, Int)] = {
+ searchIssueQuery(repos, condition.copy(repo = None), filterUser, onlyPullRequest)
+ .groupBy { t =>
+ t.userName -> t.repositoryName
+ }
+ .map { case (repo, t) =>
+ (repo._1, repo._2, t.length)
+ }
+ .sortBy(_._3 desc)
+ .list
+ }
+
+ /**
+ * Returns the search result against issues.
+ *
+ * @param condition the search condition
+ * @param filterUser the filter user name (key is "all", "assigned", "created_by" or "not_created_by", value is the user name)
+ * @param onlyPullRequest if true then returns only pull request, false then returns both of issue and pull request.
+ * @param offset the offset for pagination
+ * @param limit the limit for pagination
+ * @param repos Tuple of the repository owner and the repository name
+ * @return the search result (list of tuples which contain issue, labels and comment count)
+ */
+ def searchIssue(condition: IssueSearchCondition, filterUser: Map[String, String], onlyPullRequest: Boolean,
+ offset: Int, limit: Int, repos: (String, String)*)
+ (implicit s: Session): List[(Issue, List[Label], Int)] = {
+
+ // get issues and comment count and labels
+ searchIssueQuery(repos, condition, filterUser, onlyPullRequest)
+ .innerJoin(IssueOutline).on { (t1, t2) => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) }
+ .sortBy { case (t1, t2) =>
+ (condition.sort match {
+ case "created" => t1.registeredDate
+ case "comments" => t2.commentCount
+ case "updated" => t1.updatedDate
+ }) match {
+ case sort => condition.direction match {
+ case "asc" => sort asc
+ case "desc" => sort desc
+ }
+ }
+ }
+ .drop(offset).take(limit)
+ .leftJoin (IssueLabels) .on { case ((t1, t2), t3) => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) }
+ .leftJoin (Labels) .on { case (((t1, t2), t3), t4) => t3.byLabel(t4.userName, t4.repositoryName, t4.labelId) }
+ .map { case (((t1, t2), t3), t4) =>
+ (t1, t2.commentCount, t4.labelId.?, t4.labelName.?, t4.color.?)
+ }
+ .list
+ .splitWith { (c1, c2) =>
+ c1._1.userName == c2._1.userName &&
+ c1._1.repositoryName == c2._1.repositoryName &&
+ c1._1.issueId == c2._1.issueId
+ }
+ .map { issues => issues.head match {
+ case (issue, commentCount, _,_,_) =>
+ (issue,
+ issues.flatMap { t => t._3.map (
+ Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get)
+ )} toList,
+ commentCount)
+ }} toList
+ }
+
+ /**
+ * Assembles query for conditional issue searching.
+ */
+ private def searchIssueQuery(repos: Seq[(String, String)], condition: IssueSearchCondition,
+ filterUser: Map[String, String], onlyPullRequest: Boolean)(implicit s: Session) =
+ Issues filter { t1 =>
+ condition.repo
+ .map { _.split('/') match { case array => Seq(array(0) -> array(1)) } }
+ .getOrElse (repos)
+ .map { case (owner, repository) => t1.byRepository(owner, repository) }
+ .foldLeft[Column[Boolean]](false) ( _ || _ ) &&
+ (t1.closed is (condition.state == "closed").bind) &&
+ (t1.milestoneId is condition.milestoneId.get.get.bind, condition.milestoneId.flatten.isDefined) &&
+ (t1.milestoneId isNull, condition.milestoneId == Some(None)) &&
+ (t1.assignedUserName is filterUser("assigned").bind, filterUser.get("assigned").isDefined) &&
+ (t1.openedUserName is filterUser("created_by").bind, filterUser.get("created_by").isDefined) &&
+ (t1.openedUserName isNot filterUser("not_created_by").bind, filterUser.get("not_created_by").isDefined) &&
+ (t1.pullRequest is true.bind, onlyPullRequest) &&
+ (IssueLabels filter { t2 =>
+ (t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) &&
+ (t2.labelId in
+ (Labels filter { t3 =>
+ (t3.byRepository(t1.userName, t1.repositoryName)) &&
+ (t3.labelName inSetBind condition.labels)
+ } map(_.labelId)))
+ } exists, condition.labels.nonEmpty)
+ }
+
+ def createIssue(owner: String, repository: String, loginUser: String, title: String, content: Option[String],
+ assignedUserName: Option[String], milestoneId: Option[Int],
+ isPullRequest: Boolean = false)(implicit s: Session) =
+ // next id number
+ sql"SELECT ISSUE_ID + 1 FROM ISSUE_ID WHERE USER_NAME = $owner AND REPOSITORY_NAME = $repository FOR UPDATE".as[Int]
+ .firstOption.filter { id =>
+ Issues insert Issue(
+ owner,
+ repository,
+ id,
+ loginUser,
+ milestoneId,
+ assignedUserName,
+ title,
+ content,
+ false,
+ currentDate,
+ currentDate,
+ isPullRequest)
+
+ // increment issue id
+ IssueId
+ .filter (_.byPrimaryKey(owner, repository))
+ .map (_.issueId)
+ .update (id) > 0
+ } get
+
+ def registerIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit s: Session) =
+ IssueLabels insert IssueLabel(owner, repository, issueId, labelId)
+
+ def deleteIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit s: Session) =
+ IssueLabels filter(_.byPrimaryKey(owner, repository, issueId, labelId)) delete
+
+ def createComment(owner: String, repository: String, loginUser: String,
+ issueId: Int, content: String, action: String)(implicit s: Session): Int =
+ IssueComments.autoInc insert IssueComment(
+ userName = owner,
+ repositoryName = repository,
+ issueId = issueId,
+ action = action,
+ commentedUserName = loginUser,
+ content = content,
+ registeredDate = currentDate,
+ updatedDate = currentDate)
+
+ def updateIssue(owner: String, repository: String, issueId: Int,
+ title: String, content: Option[String])(implicit s: Session) =
+ Issues
+ .filter (_.byPrimaryKey(owner, repository, issueId))
+ .map { t =>
+ (t.title, t.content.?, t.updatedDate)
+ }
+ .update (title, content, currentDate)
+
+ def updateAssignedUserName(owner: String, repository: String, issueId: Int,
+ assignedUserName: Option[String])(implicit s: Session) =
+ Issues.filter (_.byPrimaryKey(owner, repository, issueId)).map(_.assignedUserName?).update (assignedUserName)
+
+ def updateMilestoneId(owner: String, repository: String, issueId: Int,
+ milestoneId: Option[Int])(implicit s: Session) =
+ Issues.filter (_.byPrimaryKey(owner, repository, issueId)).map(_.milestoneId?).update (milestoneId)
+
+ def updateComment(commentId: Int, content: String)(implicit s: Session) =
+ IssueComments
+ .filter (_.byPrimaryKey(commentId))
+ .map { t =>
+ t.content -> t.updatedDate
+ }
+ .update (content, currentDate)
+
+ def deleteComment(commentId: Int)(implicit s: Session) =
+ IssueComments filter (_.byPrimaryKey(commentId)) delete
+
+ def updateClosed(owner: String, repository: String, issueId: Int, closed: Boolean)(implicit s: Session) =
+ Issues
+ .filter (_.byPrimaryKey(owner, repository, issueId))
+ .map { t =>
+ t.closed -> t.updatedDate
+ }
+ .update (closed, currentDate)
+
+ /**
+ * Search issues by keyword.
+ *
+ * @param owner the repository owner
+ * @param repository the repository name
+ * @param query the keywords separated by whitespace.
+ * @return issues with comment count and matched content of issue or comment
+ */
+ def searchIssuesByKeyword(owner: String, repository: String, query: String)
+ (implicit s: Session): List[(Issue, Int, String)] = {
+ import slick.driver.JdbcDriver.likeEncode
+ val keywords = splitWords(query.toLowerCase)
+
+ // Search Issue
+ val issues = Issues
+ .innerJoin(IssueOutline).on { case (t1, t2) =>
+ t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
+ }
+ .filter { case (t1, t2) =>
+ keywords.map { keyword =>
+ (t1.title.toLowerCase like (s"%${likeEncode(keyword)}%", '^')) ||
+ (t1.content.toLowerCase like (s"%${likeEncode(keyword)}%", '^'))
+ } .reduceLeft(_ && _)
+ }
+ .map { case (t1, t2) =>
+ (t1, 0, t1.content.?, t2.commentCount)
+ }
+
+ // Search IssueComment
+ val comments = IssueComments
+ .innerJoin(Issues).on { case (t1, t2) =>
+ t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
+ }
+ .innerJoin(IssueOutline).on { case ((t1, t2), t3) =>
+ t2.byIssue(t3.userName, t3.repositoryName, t3.issueId)
+ }
+ .filter { case ((t1, t2), t3) =>
+ keywords.map { query =>
+ t1.content.toLowerCase like (s"%${likeEncode(query)}%", '^')
+ }.reduceLeft(_ && _)
+ }
+ .map { case ((t1, t2), t3) =>
+ (t2, t1.commentId, t1.content.?, t3.commentCount)
+ }
+
+ issues.union(comments).sortBy { case (issue, commentId, _, _) =>
+ issue.issueId -> commentId
+ }.list.splitWith { case ((issue1, _, _, _), (issue2, _, _, _)) =>
+ issue1.issueId == issue2.issueId
+ }.map { _.head match {
+ case (issue, _, content, commentCount) => (issue, commentCount, content.getOrElse(""))
+ }
+ }.toList
+ }
+
+ def closeIssuesFromMessage(message: String, userName: String, owner: String, repository: String)(implicit s: Session) = {
+ extractCloseId(message).foreach { issueId =>
+ for(issue <- getIssue(owner, repository, issueId) if !issue.closed){
+ createComment(owner, repository, userName, issue.issueId, "Close", "close")
+ updateClosed(owner, repository, issue.issueId, true)
+ }
+ }
+ }
+}
+
+object IssuesService {
+ import javax.servlet.http.HttpServletRequest
+
+ val IssueLimit = 30
+
+ case class IssueSearchCondition(
+ labels: Set[String] = Set.empty,
+ milestoneId: Option[Option[Int]] = None,
+ repo: Option[String] = None,
+ state: String = "open",
+ sort: String = "created",
+ direction: String = "desc"){
+
+ def toURL: String =
+ "?" + List(
+ if(labels.isEmpty) None else Some("labels=" + urlEncode(labels.mkString(","))),
+ milestoneId.map { id => "milestone=" + (id match {
+ case Some(x) => x.toString
+ case None => "none"
+ })},
+ repo.map("for=" + urlEncode(_)),
+ Some("state=" + urlEncode(state)),
+ Some("sort=" + urlEncode(sort)),
+ Some("direction=" + urlEncode(direction))).flatten.mkString("&")
+
+ }
+
+ object IssueSearchCondition {
+
+ private def param(request: HttpServletRequest, name: String, allow: Seq[String] = Nil): Option[String] = {
+ val value = request.getParameter(name)
+ if(value == null || value.isEmpty || (allow.nonEmpty && !allow.contains(value))) None else Some(value)
+ }
+
+ def apply(request: HttpServletRequest): IssueSearchCondition =
+ IssueSearchCondition(
+ param(request, "labels").map(_.split(",").toSet).getOrElse(Set.empty),
+ param(request, "milestone").map{
+ case "none" => None
+ case x => x.toIntOpt
+ },
+ param(request, "for"),
+ param(request, "state", Seq("open", "closed")).getOrElse("open"),
+ param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"),
+ param(request, "direction", Seq("asc", "desc")).getOrElse("desc"))
+
+ def page(request: HttpServletRequest) = try {
+ val i = param(request, "page").getOrElse("1").toInt
+ if(i <= 0) 1 else i
+ } catch {
+ case e: NumberFormatException => 1
+ }
+ }
+
+}
diff --git a/src/main/scala/servlet/TransactionFilter.scala b/src/main/scala/servlet/TransactionFilter.scala
index 5e034d4..6773a59 100644
--- a/src/main/scala/servlet/TransactionFilter.scala
+++ b/src/main/scala/servlet/TransactionFilter.scala
@@ -1,44 +1,44 @@
-package servlet
-
-import javax.servlet._
-import org.slf4j.LoggerFactory
-import javax.servlet.http.HttpServletRequest
-import util.Keys
-
-/**
- * Controls the transaction with the open session in view pattern.
- */
-class TransactionFilter extends Filter {
-
- private val logger = LoggerFactory.getLogger(classOf[TransactionFilter])
-
- def init(config: FilterConfig) = {}
-
- def destroy(): Unit = {}
-
- def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
- if(req.asInstanceOf[HttpServletRequest].getRequestURI().startsWith("/assets/")){
- // assets don't need transaction
- chain.doFilter(req, res)
- } else {
- Database(req.getServletContext) withTransaction { session =>
- logger.debug("begin transaction")
- req.setAttribute(Keys.Request.DBSession, session)
- chain.doFilter(req, res)
- logger.debug("end transaction")
- }
- }
- }
-
-}
-
-object Database {
- def apply(context: ServletContext): slick.jdbc.JdbcBackend.Database =
- slick.jdbc.JdbcBackend.Database.forURL(context.getInitParameter("db.url"),
- context.getInitParameter("db.user"),
- context.getInitParameter("db.password"))
-
- def getSession(req: ServletRequest): slick.jdbc.JdbcBackend#Session =
- req.getAttribute(Keys.Request.DBSession).asInstanceOf[slick.jdbc.JdbcBackend#Session]
-
-}
+package servlet
+
+import javax.servlet._
+import org.slf4j.LoggerFactory
+import javax.servlet.http.HttpServletRequest
+import util.Keys
+
+/**
+ * Controls the transaction with the open session in view pattern.
+ */
+class TransactionFilter extends Filter {
+
+ private val logger = LoggerFactory.getLogger(classOf[TransactionFilter])
+
+ def init(config: FilterConfig) = {}
+
+ def destroy(): Unit = {}
+
+ def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
+ if(req.asInstanceOf[HttpServletRequest].getRequestURI().startsWith("/assets/")){
+ // assets don't need transaction
+ chain.doFilter(req, res)
+ } else {
+ Database(req.getServletContext) withTransaction { session =>
+ logger.debug("begin transaction")
+ req.setAttribute(Keys.Request.DBSession, session)
+ chain.doFilter(req, res)
+ logger.debug("end transaction")
+ }
+ }
+ }
+
+}
+
+object Database {
+ def apply(context: ServletContext): slick.jdbc.JdbcBackend.Database =
+ slick.jdbc.JdbcBackend.Database.forURL(context.getInitParameter("db.url"),
+ context.getInitParameter("db.user"),
+ context.getInitParameter("db.password"))
+
+ def getSession(req: ServletRequest): slick.jdbc.JdbcBackend#Session =
+ req.getAttribute(Keys.Request.DBSession).asInstanceOf[slick.jdbc.JdbcBackend#Session]
+
+}
diff --git a/src/main/scala/util/Notifier.scala b/src/main/scala/util/Notifier.scala
index 8758ef9..e1bd50b 100644
--- a/src/main/scala/util/Notifier.scala
+++ b/src/main/scala/util/Notifier.scala
@@ -1,117 +1,117 @@
-package util
-
-import scala.concurrent._
-import ExecutionContext.Implicits.global
-import org.apache.commons.mail.{DefaultAuthenticator, HtmlEmail}
-import org.slf4j.LoggerFactory
-
-import app.Context
-import service.{AccountService, RepositoryService, IssuesService, SystemSettingsService}
-import servlet.Database
-import SystemSettingsService.Smtp
-import _root_.util.ControlUtil.defining
-import model.profile.simple.Session
-
-trait Notifier extends RepositoryService with AccountService with IssuesService {
- def toNotify(r: RepositoryService.RepositoryInfo, issueId: Int, content: String)
- (msg: String => String)(implicit context: Context): Unit
-
- protected def recipients(issue: model.Issue)(notify: String => Unit)(implicit session: Session, context: Context) =
- (
- // individual repository's owner
- issue.userName ::
- // collaborators
- getCollaborators(issue.userName, issue.repositoryName) :::
- // participants
- issue.openedUserName ::
- getComments(issue.userName, issue.repositoryName, issue.issueId).map(_.commentedUserName)
- )
- .distinct
- .withFilter ( _ != context.loginAccount.get.userName ) // the operation in person is excluded
- .foreach ( getAccountByUserName(_) filterNot (_.isGroupAccount) foreach (x => notify(x.mailAddress)) )
-
-}
-
-object Notifier {
- // TODO We want to be able to switch to mock.
- def apply(): Notifier = new SystemSettingsService {}.loadSystemSettings match {
- case settings if settings.notification => new Mailer(settings.smtp.get)
- case _ => new MockMailer
- }
-
- def msgIssue(url: String) = (content: String) => s"""
- |${content}
- |--
- |View it on GitBucket
- """.stripMargin
-
- def msgPullRequest(url: String) = (content: String) => s"""
- |${content}