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