Use ControlUtil.

This commit is contained in:
takezoe
2013-09-22 19:28:14 +09:00
parent 602b6c635a
commit fd8b5780f3
7 changed files with 299 additions and 323 deletions

View File

@@ -1,6 +1,8 @@
package app
import _root_.util.Directory._
import _root_.util.Implicits._
import _root_.util.ControlUtil._
import _root_.util.{FileUtil, Validations}
import org.scalatra._
import org.scalatra.json._
@@ -57,56 +59,51 @@ abstract class ControllerBase extends ScalatraFilter
*/
implicit def context: Context = Context(servletContext.getContextPath, LoginAccount, currentURL, request)
private def currentURL: String = {
val queryString = request.getQueryString
private def currentURL: String = defining(request.getQueryString){ queryString =>
request.getRequestURI + (if(queryString != null) "?" + queryString else "")
}
private def LoginAccount: Option[Account] = {
private def LoginAccount: Option[Account] =
session.get("LOGIN_ACCOUNT") match {
case Some(x: Account) => Some(x)
case _ => None
}
}
def ajaxGet(path : String)(action : => Any) : Route = {
def ajaxGet(path : String)(action : => Any) : Route =
super.get(path){
request.setAttribute("AJAX", "true")
action
}
}
override def ajaxGet[T](path : String, form : MappingValueType[T])(action : T => Any) : Route = {
override def ajaxGet[T](path : String, form : MappingValueType[T])(action : T => Any) : Route =
super.ajaxGet(path, form){ form =>
request.setAttribute("AJAX", "true")
action(form)
}
}
def ajaxPost(path : String)(action : => Any) : Route = {
def ajaxPost(path : String)(action : => Any) : Route =
super.post(path){
request.setAttribute("AJAX", "true")
action
}
}
override def ajaxPost[T](path : String, form : MappingValueType[T])(action : T => Any) : Route = {
override def ajaxPost[T](path : String, form : MappingValueType[T])(action : T => Any) : Route =
super.ajaxPost(path, form){ form =>
request.setAttribute("AJAX", "true")
action(form)
}
}
protected def NotFound() = {
if(request.getAttribute("AJAX") == null){
org.scalatra.NotFound(html.error("Not Found"))
} else {
protected def NotFound() =
if(request.hasAttribute("AJAX")){
org.scalatra.NotFound()
}
} else {
org.scalatra.NotFound(html.error("Not Found"))
}
protected def Unauthorized()(implicit context: app.Context) = {
if(request.getAttribute("AJAX") == null){
protected def Unauthorized()(implicit context: app.Context) =
if(request.hasAttribute("AJAX")){
org.scalatra.Unauthorized()
} else {
if(context.loginAccount.isDefined){
org.scalatra.Unauthorized(redirect("/"))
} else {
@@ -116,13 +113,9 @@ abstract class ControllerBase extends ScalatraFilter
org.scalatra.Unauthorized(redirect("/signin?redirect=" + currentURL))
}
}
} else {
org.scalatra.Unauthorized()
}
}
protected def baseUrl = {
val url = request.getRequestURL.toString
protected def baseUrl = defining(request.getRequestURL.toString){ url =>
url.substring(0, url.length - (request.getRequestURI.length - request.getContextPath.length))
}
@@ -133,13 +126,11 @@ abstract class ControllerBase extends ScalatraFilter
*/
case class Context(path: String, loginAccount: Option[Account], currentUrl: String, request: HttpServletRequest){
def redirectUrl = {
if(request.getParameter("redirect") != null){
def redirectUrl = if(request.getParameter("redirect") != null){
request.getParameter("redirect")
} else {
currentUrl
}
}
/**
* Get object from cache.
@@ -147,13 +138,12 @@ case class Context(path: String, loginAccount: Option[Account], currentUrl: Stri
* If object has not been cached with the specified key then retrieves by given action.
* Cached object are available during a request.
*/
def cache[A](key: String)(action: => A): A = {
def cache[A](key: String)(action: => A): A =
Option(request.getAttribute("cache." + key).asInstanceOf[A]).getOrElse {
val newObject = action
request.setAttribute("cache." + key, newObject)
newObject
}
}
}
@@ -163,7 +153,7 @@ case class Context(path: String, loginAccount: Option[Account], currentUrl: Stri
trait AccountManagementControllerBase extends ControllerBase with FileUploadControllerBase {
self: AccountService =>
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit = {
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit =
if(clearImage){
getAccountByUserName(userName).flatMap(_.image).map { image =>
new java.io.File(getUserUploadDir(userName), image).delete()
@@ -179,7 +169,6 @@ trait AccountManagementControllerBase extends ControllerBase with FileUploadCont
updateAvatarImage(userName, Some(filename))
}
}
}
protected def uniqueUserName: Constraint = new Constraint(){
override def validate(name: String, value: String): Option[String] =
@@ -212,11 +201,10 @@ trait FileUploadControllerBase {
// def removeTemporaryFile(fileId: String)(implicit session: HttpSession): Unit =
// getTemporaryFile(fileId).delete()
def removeTemporaryFiles()(implicit session: HttpSession): Unit =
FileUtils.deleteDirectory(TemporaryDir)
def removeTemporaryFiles()(implicit session: HttpSession): Unit = FileUtils.deleteDirectory(TemporaryDir)
def getUploadedFilename(fileId: String)(implicit session: HttpSession): Option[String] = {
val filename = Option(session.getAttribute("upload_" + fileId).asInstanceOf[String])
def getUploadedFilename(fileId: String)(implicit session: HttpSession): Option[String] =
defining(Option(session.getAttribute("upload_" + fileId).asInstanceOf[String])){ filename =>
if(filename.isDefined){
session.removeAttribute("upload_" + fileId)
}

View File

@@ -2,6 +2,7 @@ package app
import service._
import util.UsersAuthenticator
import util.Implicits._
class DashboardController extends DashboardControllerBase
with IssuesService with PullRequestService with RepositoryService with AccountService
@@ -43,11 +44,10 @@ trait DashboardControllerBase extends ControllerBase {
// condition
val sessionKey = "dashboard/issues"
val condition = if(request.getQueryString == null)
session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
else IssueSearchCondition(request)
session.put(sessionKey, condition)
val condition = session.putAndGet(sessionKey,
if(request.hasQueryString) IssueSearchCondition(request)
else session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
)
val userName = context.loginAccount.get.userName
val repositories = getUserRepositories(userName, baseUrl).map(repo => repo.owner -> repo.name)
@@ -76,14 +76,10 @@ trait DashboardControllerBase extends ControllerBase {
// condition
val sessionKey = "dashboard/pulls"
val condition = {
if(request.getQueryString == null)
session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
else
IssueSearchCondition(request)
}.copy(repo = repository)
session.put(sessionKey, condition)
val condition = session.putAndGet(sessionKey, {
if(request.hasQueryString) IssueSearchCondition(request)
else session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
}.copy(repo = repository))
val userName = context.loginAccount.get.userName
val repositories = getUserRepositories(userName, baseUrl).map(repo => repo.owner -> repo.name)

View File

@@ -5,6 +5,8 @@ import jp.sf.amateras.scalatra.forms._
import service._
import IssuesService._
import util.{CollaboratorsAuthenticator, ReferrerAuthenticator, ReadableUsersAuthenticator, Notifier}
import util.Implicits._
import util.ControlUtil._
import org.scalatra.Ok
class IssuesController extends IssuesControllerBase
@@ -57,10 +59,7 @@ trait IssuesControllerBase extends ControllerBase {
})
get("/:owner/:repository/issues/:id")(referrersOnly { repository =>
val owner = repository.owner
val name = repository.name
val issueId = params("id")
defining(repository.owner, repository.name, params("id")){ case (owner, name, issueId) =>
getIssue(owner, name, issueId) map {
issues.html.issue(
_,
@@ -72,23 +71,22 @@ trait IssuesControllerBase extends ControllerBase {
hasWritePermission(owner, name, context.loginAccount),
repository)
} getOrElse NotFound
}
})
get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
val owner = repository.owner
val name = repository.name
defining(repository.owner, repository.name){ case (owner, name) =>
issues.html.create(
(getCollaborators(owner, name) :+ owner).sorted,
getMilestones(owner, name),
getLabels(owner, name),
hasWritePermission(owner, name, context.loginAccount),
repository)
}
})
post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) =>
val owner = repository.owner
val name = repository.name
defining(repository.owner, repository.name){ case (owner, name) =>
val writable = hasWritePermission(owner, name, context.loginAccount)
val userName = context.loginAccount.get.userName
@@ -118,18 +116,18 @@ trait IssuesControllerBase extends ControllerBase {
}
redirect(s"/${owner}/${name}/issues/${issueId}")
}
})
ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (form, repository) =>
val owner = repository.owner
val name = repository.name
defining(repository.owner, repository.name){ case (owner, name) =>
getIssue(owner, name, params("id")).map { issue =>
if(isEditable(owner, name, issue.openedUserName)){
updateIssue(owner, name, issue.issueId, form.title, form.content)
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
} else Unauthorized
} getOrElse NotFound
}
})
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
@@ -147,15 +145,14 @@ trait IssuesControllerBase extends ControllerBase {
})
ajaxPost("/:owner/:repository/issue_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) =>
val owner = repository.owner
val name = repository.name
defining(repository.owner, repository.name){ case (owner, name) =>
getComment(owner, name, params("id")).map { comment =>
if(isEditable(owner, name, comment.commentedUserName)){
updateComment(comment.commentId, form.content)
redirect(s"/${owner}/${name}/issue_comments/_data/${comment.commentId}")
} else Unauthorized
} getOrElse NotFound
}
})
ajaxGet("/:owner/:repository/issues/_data/:id")(readableUsersOnly { repository =>
@@ -194,17 +191,17 @@ trait IssuesControllerBase extends ControllerBase {
})
ajaxPost("/:owner/:repository/issues/:id/label/new")(collaboratorsOnly { repository =>
val issueId = params("id").toInt
defining(params("id").toInt){ issueId =>
registerIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt)
issues.html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
}
})
ajaxPost("/:owner/:repository/issues/:id/label/delete")(collaboratorsOnly { repository =>
val issueId = params("id").toInt
defining(params("id").toInt){ issueId =>
deleteIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt)
issues.html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
}
})
ajaxPost("/:owner/:repository/issues/:id/assign")(collaboratorsOnly { repository =>
@@ -223,37 +220,37 @@ trait IssuesControllerBase extends ControllerBase {
})
post("/:owner/:repository/issues/batchedit/state")(collaboratorsOnly { repository =>
val action = params.get("value")
defining(params.get("value")){ action =>
executeBatch(repository) {
handleComment(_, None, repository)( _ => action)
}
}
})
post("/:owner/:repository/issues/batchedit/label")(collaboratorsOnly { repository =>
val labelId = params("value").toInt
defining(params("value").toInt){ labelId =>
executeBatch(repository) { issueId =>
getIssueLabel(repository.owner, repository.name, issueId, labelId) getOrElse {
registerIssueLabel(repository.owner, repository.name, issueId, labelId)
}
}
}
})
post("/:owner/:repository/issues/batchedit/assign")(collaboratorsOnly { repository =>
val value = assignedUserName("value")
defining(assignedUserName("value")){ value =>
executeBatch(repository) {
updateAssignedUserName(repository.owner, repository.name, _, value)
}
}
})
post("/:owner/:repository/issues/batchedit/milestone")(collaboratorsOnly { repository =>
val value = milestoneId("value")
defining(milestoneId("value")){ value =>
executeBatch(repository) {
updateMilestoneId(repository.owner, repository.name, _, value)
}
}
})
val assignedUserName = (key: String) => params.get(key) filter (_.trim != "")
@@ -273,8 +270,8 @@ trait IssuesControllerBase extends ControllerBase {
private def handleComment(issueId: Int, content: Option[String], repository: RepositoryService.RepositoryInfo)
(getAction: model.Issue => Option[String] =
p1 => params.get("action").filter(_ => isEditable(p1.userName, p1.repositoryName, p1.openedUserName))) = {
val owner = repository.owner
val name = repository.name
defining(repository.owner, repository.name){ case (owner, name) =>
val userName = context.loginAccount.get.userName
getIssue(owner, name, issueId.toString) map { issue =>
@@ -325,20 +322,19 @@ trait IssuesControllerBase extends ControllerBase {
issue -> commentId
}
}
}
private def searchIssues(filter: String, repository: RepositoryService.RepositoryInfo) = {
val owner = repository.owner
val repoName = repository.name
defining(repository.owner, repository.name){ case (owner, repoName) =>
val filterUser = Map(filter -> params.getOrElse("userName", ""))
val page = IssueSearchCondition.page(request)
val sessionKey = s"${owner}/${repoName}/issues"
// retrieve search condition
val condition = if(request.getQueryString == null){
session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
} else IssueSearchCondition(request)
session.put(sessionKey, condition)
val condition = session.putAndGet(sessionKey,
if(request.hasQueryString) IssueSearchCondition(request)
else session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
)
issues.html.list(
searchIssue(condition, filterUser, false, (page - 1) * IssueLimit, IssueLimit, owner -> repoName),
@@ -357,5 +353,6 @@ trait IssuesControllerBase extends ControllerBase {
repository,
hasWritePermission(owner, repoName, context.loginAccount))
}
}
}

View File

@@ -63,14 +63,9 @@ trait PullRequestsControllerBase extends ControllerBase {
})
get("/:owner/:repository/pull/:id")(referrersOnly { repository =>
val owner = repository.owner
val name = repository.name
val issueId = params("id").toInt
defining(repository.owner, repository.name, params("id").toInt){ case (owner, name, issueId) =>
getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
using(Git.open(getRepositoryDir(owner, name))){ git =>
val requestCommitId = git.getRepository.resolve(pullreq.requestBranch)
val (commits, diffs) =
getRequestCompareInfo(owner, name, pullreq.commitIdFrom, owner, name, pullreq.commitIdTo)
@@ -90,34 +85,33 @@ trait PullRequestsControllerBase extends ControllerBase {
repository,
s"${baseUrl}${context.path}/git/${pullreq.requestUserName}/${pullreq.requestRepositoryName}.git")
}
} getOrElse NotFound
}
})
post("/:owner/:repository/pull/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) =>
LockUtil.lock(s"${repository.owner}/${repository.name}/merge"){
val issueId = params("id").toInt
getPullRequest(repository.owner, repository.name, issueId).map { case (issue, pullreq) =>
val remote = getRepositoryDir(repository.owner, repository.name)
val tmpdir = new java.io.File(getTemporaryDir(repository.owner, repository.name), s"merge-${issueId}")
defining(repository.owner, repository.name, params("id").toInt){ case (owner, name, issueId) =>
LockUtil.lock(s"${owner}/${name}/merge"){
getPullRequest(owner, name, issueId).map { case (issue, pullreq) =>
val remote = getRepositoryDir(owner, name)
val tmpdir = new java.io.File(getTemporaryDir(owner, name), s"merge-${issueId}")
val git = Git.cloneRepository.setDirectory(tmpdir).setURI(remote.toURI.toString).setBranch(pullreq.branch).call
try {
// mark issue as merged and close.
val loginAccount = context.loginAccount.get
createComment(repository.owner, repository.name, loginAccount.userName, issueId, form.message, "merge")
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Close", "close")
updateClosed(repository.owner, repository.name, issueId, true)
createComment(owner, name, loginAccount.userName, issueId, form.message, "merge")
createComment(owner, name, loginAccount.userName, issueId, "Close", "close")
updateClosed(owner, name, issueId, true)
// record activity
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, form.message)
recordMergeActivity(owner, name, loginAccount.userName, issueId, form.message)
// fetch pull request to temporary working repository
val pullRequestBranchName = s"gitbucket-pullrequest-${issueId}"
git.fetch
.setRemote(getRepositoryDir(repository.owner, repository.name).toURI.toString)
.setRemote(getRepositoryDir(owner, name).toURI.toString)
.setRefSpecs(new RefSpec(s"refs/pull/${issueId}/head:refs/heads/${pullRequestBranchName}")).call
// merge pull request
@@ -145,21 +139,21 @@ trait PullRequestsControllerBase extends ControllerBase {
// push
git.push.call
val (commits, _) = getRequestCompareInfo(repository.owner, repository.name, pullreq.commitIdFrom,
val (commits, _) = getRequestCompareInfo(owner, name, pullreq.commitIdFrom,
pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo)
commits.flatten.foreach { commit =>
if(!existsCommitId(repository.owner, repository.name, commit.id)){
insertCommitId(repository.owner, repository.name, commit.id)
if(!existsCommitId(owner, name, commit.id)){
insertCommitId(owner, name, commit.id)
}
}
// notifications
Notifier().toNotify(repository, issueId, "merge"){
Notifier.msgStatus(s"${baseUrl}/${repository.owner}/${repository.name}/pull/${issueId}")
Notifier.msgStatus(s"${baseUrl}/${owner}/${name}/pull/${issueId}")
}
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
redirect(s"/${owner}/${name}/pull/${issueId}")
} finally {
git.getRepository.close
@@ -167,6 +161,7 @@ trait PullRequestsControllerBase extends ControllerBase {
}
} getOrElse NotFound
}
}
})
/**
@@ -377,11 +372,10 @@ trait PullRequestsControllerBase extends ControllerBase {
val sessionKey = s"${owner}/${repoName}/pulls"
// retrieve search condition
val condition = if(request.getQueryString == null){
session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
} else IssueSearchCondition(request)
session.put(sessionKey, condition)
val condition = session.putAndGet(sessionKey,
if(request.hasQueryString) IssueSearchCondition(request)
else session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
)
pulls.html.list(
searchIssue(condition, filterUser, true, (page - 1) * PullRequestLimit, PullRequestLimit, owner -> repoName),

View File

@@ -9,7 +9,6 @@ import org.scalatra.FlashMapSupport
import service.WebHookService.WebHookPayload
import util.JGitUtil.CommitInfo
import util.ControlUtil._
import util.Implicits._
import org.eclipse.jgit.api.Git
class RepositorySettingsController extends RepositorySettingsControllerBase
@@ -123,8 +122,7 @@ trait RepositorySettingsControllerBase extends ControllerBase with FlashMapSuppo
* Delete the web hook URL.
*/
get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
val url = params("url")
deleteWebHookURL(repository.owner, repository.name, url)
deleteWebHookURL(repository.owner, repository.name, params("url"))
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
})
@@ -139,24 +137,19 @@ trait RepositorySettingsControllerBase extends ControllerBase with FlashMapSuppo
.setMaxCount(3)
.call.iterator.asScala.map(new CommitInfo(_))
val payload = WebHookPayload(
callWebHook(repository.owner, repository.name,
WebHookPayload(
git,
"refs/heads/" + repository.repository.defaultBranch,
repository,
commits.toList,
getAccountByUserName(repository.owner).get)
getAccountByUserName(repository.owner).get))
callWebHook(repository.owner, repository.name, payload)
flash += "info" -> "Test payload deployed!"
}
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
})
// TODO Remove this action after web hook is completed.
post("/xxx/xxx/xxx/webhooktest"){
println(params("payload"))
}
/**
* Display the delete repository page.
*/
@@ -182,9 +175,7 @@ trait RepositorySettingsControllerBase extends ControllerBase with FlashMapSuppo
*/
private def webHook: Constraint = new Constraint(){
override def validate(name: String, value: String): Option[String] =
defining(request.paths){ paths =>
getWebHookURLs(paths(1), paths(2)).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.")
}
/**
@@ -192,14 +183,12 @@ trait RepositorySettingsControllerBase extends ControllerBase with FlashMapSuppo
*/
private def collaborator: Constraint = new Constraint(){
override def validate(name: String, value: String): Option[String] =
defining(request.paths){ paths =>
getAccountByUserName(value) match {
case None => Some("User does not exist.")
case Some(x) if(x.userName == paths(1) || getCollaborators(paths(1), paths(2)).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.")
case _ => None
}
}
}
}

View File

@@ -23,7 +23,6 @@ trait SignInControllerBase extends ControllerBase { self: SystemSettingsService
}
post("/signin", form){ form =>
val settings = loadSystemSettings()
authenticate(loadSystemSettings(), form.userName, form.password) match {
case Some(account) => signin(account)
case None => redirect("/signin")

View File

@@ -1,7 +1,7 @@
package util
import scala.util.matching.Regex
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.{HttpSession, HttpServletRequest}
/**
* Provides some usable implicit conversions.
@@ -44,7 +44,20 @@ object Implicits {
}
implicit class RichRequest(request: HttpServletRequest){
def paths: Array[String] = request.getRequestURI.substring(request.getContextPath.length).split("/")
def hasQueryString: Boolean = request.getQueryString != null
def hasAttribute(name: String): Boolean = request.getAttribute(name) != null
}
implicit class RichSession(session: HttpSession){
def putAndGet[T](key: String, value: T): T = {
session.setAttribute(key, value)
value
}
}
}