mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-10 15:35:59 +01:00
Merge branch 'master' into fork-and-pullreq
Conflicts: src/main/resources/update/1_3.sql src/main/resources/update/1_4.sql src/main/scala/app/CreateRepositoryController.scala src/main/scala/service/WikiService.scala src/main/twirl/account/repositories.scala.html
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
package app
|
||||
|
||||
import service._
|
||||
import util.{FileUtil, FileUploadUtil, OneselfAuthenticator}
|
||||
import util.{FileUtil, OneselfAuthenticator}
|
||||
import util.StringUtil._
|
||||
import util.Directory._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.scalatra.FlashMapSupport
|
||||
|
||||
class AccountController extends AccountControllerBase
|
||||
@@ -43,12 +42,23 @@ trait AccountControllerBase extends AccountManagementControllerBase with FlashMa
|
||||
*/
|
||||
get("/:userName") {
|
||||
val userName = params("userName")
|
||||
getAccountByUserName(userName).map { x =>
|
||||
getAccountByUserName(userName).map { account =>
|
||||
params.getOrElse("tab", "repositories") match {
|
||||
// Public Activity
|
||||
case "activity" => account.html.activity(x, getActivitiesByUser(userName, true))
|
||||
case "activity" =>
|
||||
_root_.account.html.activity(account,
|
||||
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getActivitiesByUser(userName, true))
|
||||
|
||||
// Members
|
||||
case "members" if(account.isGroupAccount) =>
|
||||
_root_.account.html.members(account, getGroupMembers(account.userName))
|
||||
|
||||
// Repositories
|
||||
case _ => account.html.repositories(x, getVisibleRepositories(userName, baseUrl, context.loginAccount.map(_.userName)))
|
||||
case _ =>
|
||||
_root_.account.html.repositories(account,
|
||||
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getVisibleRepositories(userName, baseUrl, context.loginAccount.map(_.userName)))
|
||||
}
|
||||
} getOrElse NotFound
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package app
|
||||
|
||||
import _root_.util.Directory._
|
||||
import _root_.util.{FileUploadUtil, FileUtil, Validations}
|
||||
import _root_.util.{FileUtil, Validations}
|
||||
import org.scalatra._
|
||||
import org.scalatra.json._
|
||||
import org.json4s._
|
||||
@@ -10,7 +10,9 @@ import org.apache.commons.io.FileUtils
|
||||
import model.Account
|
||||
import scala.Some
|
||||
import service.AccountService
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.{HttpSession, HttpServletRequest}
|
||||
import java.text.SimpleDateFormat
|
||||
import javax.servlet.{FilterChain, ServletResponse, ServletRequest}
|
||||
|
||||
/**
|
||||
* Provides generic features for controller implementations.
|
||||
@@ -20,6 +22,21 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
|
||||
implicit val jsonFormats = DefaultFormats
|
||||
|
||||
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
|
||||
val httpRequest = request.asInstanceOf[HttpServletRequest]
|
||||
val path = httpRequest.getRequestURI.substring(request.getServletContext.getContextPath.length)
|
||||
|
||||
if(path.startsWith("/console/")){
|
||||
Option(httpRequest.getSession.getAttribute("LOGIN_ACCOUNT").asInstanceOf[Account]).collect {
|
||||
case account if(account.isAdmin) => chain.doFilter(request, response)
|
||||
}
|
||||
} else if(path.startsWith("/git/")){
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
super.doFilter(request, response, chain)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context object for the request.
|
||||
*/
|
||||
@@ -116,7 +133,8 @@ case class Context(path: String, loginAccount: Option[Account], currentUrl: Stri
|
||||
/**
|
||||
* Base trait for controllers which manages account information.
|
||||
*/
|
||||
trait AccountManagementControllerBase extends ControllerBase { self: AccountService =>
|
||||
trait AccountManagementControllerBase extends ControllerBase with FileUploadControllerBase {
|
||||
self: AccountService =>
|
||||
|
||||
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit = {
|
||||
if(clearImage){
|
||||
@@ -126,9 +144,9 @@ trait AccountManagementControllerBase extends ControllerBase { self: AccountServ
|
||||
}
|
||||
} else {
|
||||
fileId.map { fileId =>
|
||||
val filename = "avatar." + FileUtil.getExtension(FileUploadUtil.getUploadedFilename(fileId).get)
|
||||
val filename = "avatar." + FileUtil.getExtension(getUploadedFilename(fileId).get)
|
||||
FileUtils.moveFile(
|
||||
FileUploadUtil.getTemporaryFile(fileId),
|
||||
getTemporaryFile(fileId),
|
||||
new java.io.File(getUserUploadDir(userName), filename)
|
||||
)
|
||||
updateAvatarImage(userName, Some(filename))
|
||||
@@ -148,4 +166,34 @@ trait AccountManagementControllerBase extends ControllerBase { self: AccountServ
|
||||
.map { _ => "Mail address is already registered." }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Base trait for controllers which needs file uploading feature.
|
||||
*/
|
||||
trait FileUploadControllerBase {
|
||||
|
||||
def generateFileId: String =
|
||||
new SimpleDateFormat("yyyyMMddHHmmSSsss").format(new java.util.Date(System.currentTimeMillis))
|
||||
|
||||
def TemporaryDir(implicit session: HttpSession): java.io.File =
|
||||
new java.io.File(GitBucketHome, s"tmp/_upload/${session.getId}")
|
||||
|
||||
def getTemporaryFile(fileId: String)(implicit session: HttpSession): java.io.File =
|
||||
new java.io.File(TemporaryDir, fileId)
|
||||
|
||||
// def removeTemporaryFile(fileId: String)(implicit session: HttpSession): Unit =
|
||||
// getTemporaryFile(fileId).delete()
|
||||
|
||||
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])
|
||||
if(filename.isDefined){
|
||||
session.removeAttribute("upload_" + fileId)
|
||||
}
|
||||
filename
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import util.{JGitUtil, UsersAuthenticator, ReferrerAuthenticator}
|
||||
import service._
|
||||
import java.io.File
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib._
|
||||
import org.apache.commons.io._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
import org.eclipse.jgit.lib.PersonIdent
|
||||
|
||||
class CreateRepositoryController extends CreateRepositoryControllerBase
|
||||
with RepositoryService with AccountService with WikiService with LabelsService with ActivityService
|
||||
@@ -17,14 +17,15 @@ class CreateRepositoryController extends CreateRepositoryControllerBase
|
||||
* Creates new repository.
|
||||
*/
|
||||
trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
self: RepositoryService with WikiService with LabelsService with ActivityService
|
||||
self: RepositoryService with AccountService with WikiService with LabelsService with ActivityService
|
||||
with UsersAuthenticator with ReferrerAuthenticator =>
|
||||
|
||||
case class RepositoryCreationForm(name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean)
|
||||
case class RepositoryCreationForm(owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean)
|
||||
|
||||
case class ForkRepositoryForm(owner: String, name: String)
|
||||
|
||||
val newForm = mapping(
|
||||
"owner" -> trim(label("Owner" , text(required, maxlength(40), identifier, existsAccount))),
|
||||
"name" -> trim(label("Repository name", text(required, maxlength(40), identifier, unique))),
|
||||
"description" -> trim(label("Description" , optional(text()))),
|
||||
"isPrivate" -> trim(label("Repository Type", boolean())),
|
||||
@@ -40,28 +41,36 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
* Show the new repository form.
|
||||
*/
|
||||
get("/new")(usersOnly {
|
||||
html.newrepo()
|
||||
html.newrepo(getGroupsByUserName(context.loginAccount.get.userName))
|
||||
})
|
||||
|
||||
/**
|
||||
* Create new repository.
|
||||
*/
|
||||
post("/new", newForm)(usersOnly { form =>
|
||||
val ownerAccount = getAccountByUserName(form.owner).get
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginUserName = loginAccount.userName
|
||||
|
||||
// Insert to the database at first
|
||||
createRepository(form.name, loginUserName, form.description, form.isPrivate)
|
||||
createRepository(form.name, form.owner, form.description, form.isPrivate)
|
||||
|
||||
// Add collaborators for group repository
|
||||
if(ownerAccount.isGroupAccount){
|
||||
getGroupMembers(form.owner).foreach { userName =>
|
||||
addCollaborator(form.owner, form.name, userName)
|
||||
}
|
||||
}
|
||||
|
||||
// Insert default labels
|
||||
insertDefaultLabels(loginUserName, form.name)
|
||||
|
||||
// Create the actual repository
|
||||
val gitdir = getRepositoryDir(loginUserName, form.name)
|
||||
val gitdir = getRepositoryDir(form.owner, form.name)
|
||||
JGitUtil.initRepository(gitdir)
|
||||
|
||||
if(form.createReadme){
|
||||
val tmpdir = getInitRepositoryDir(loginUserName, form.name)
|
||||
val tmpdir = getInitRepositoryDir(form.owner, form.name)
|
||||
try {
|
||||
// Clone the repository
|
||||
Git.cloneRepository.setURI(gitdir.toURI.toString).setDirectory(tmpdir).call
|
||||
@@ -91,13 +100,13 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
}
|
||||
|
||||
// Create Wiki repository
|
||||
createWikiRepository(loginAccount, form.name)
|
||||
createWikiRepository(loginAccount, form.owner, form.name)
|
||||
|
||||
// Record activity
|
||||
recordCreateRepositoryActivity(loginUserName, form.name, loginUserName)
|
||||
recordCreateRepositoryActivity(form.owner, form.name, loginUserName)
|
||||
|
||||
// redirect to the repository
|
||||
redirect(s"/${loginUserName}/${form.name}")
|
||||
redirect(s"/${form.owner}/${form.name}")
|
||||
})
|
||||
|
||||
post("/:owner/:repository/_fork")(referrersOnly { repository =>
|
||||
@@ -153,12 +162,19 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
createLabel(userName, repositoryName, "wontfix", "ffffff")
|
||||
}
|
||||
|
||||
private def existsAccount: Constraint = new Constraint(){
|
||||
def validate(name: String, value: String): Option[String] =
|
||||
if(getAccountByUserName(value).isEmpty) Some("User or group does not exist.") else None
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate check for the repository name.
|
||||
*/
|
||||
private def unique: Constraint = new Constraint(){
|
||||
def validate(name: String, value: String): Option[String] =
|
||||
getRepositoryNamesOfUser(context.loginAccount.get.userName).find(_ == value).map(_ => "Repository already exists.")
|
||||
params.get("owner").flatMap { userName =>
|
||||
getRepositoryNamesOfUser(userName).find(_ == value).map(_ => "Repository already exists.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package app
|
||||
|
||||
import util.{FileUtil, FileUploadUtil}
|
||||
import util.{FileUtil}
|
||||
import org.scalatra._
|
||||
import org.scalatra.servlet.{MultipartConfig, FileUploadSupport}
|
||||
import org.apache.commons.io.FileUtils
|
||||
@@ -9,17 +9,18 @@ import org.apache.commons.io.FileUtils
|
||||
* Provides Ajax based file upload functionality.
|
||||
*
|
||||
* This servlet saves uploaded file as temporary file and returns the unique id.
|
||||
* You can get uploaded file using [[util.FileUploadUtil#getTemporaryFile()]] with this id.
|
||||
* You can get uploaded file using [[app.FileUploadControllerBase#getTemporaryFile()]] with this id.
|
||||
*/
|
||||
// TODO Remove temporary files at session timeout by session listener.
|
||||
class FileUploadController extends ScalatraServlet with FileUploadSupport with FlashMapSupport {
|
||||
class FileUploadController extends ScalatraServlet
|
||||
with FileUploadSupport with FlashMapSupport with FileUploadControllerBase {
|
||||
|
||||
configureMultipartHandling(MultipartConfig(maxFileSize = Some(3 * 1024 * 1024)))
|
||||
|
||||
post("/image"){
|
||||
fileParams.get("file") match {
|
||||
case Some(file) if(FileUtil.isImage(file.name)) => {
|
||||
val fileId = FileUploadUtil.generateFileId
|
||||
FileUtils.writeByteArrayToFile(FileUploadUtil.getTemporaryFile(fileId), file.get)
|
||||
val fileId = generateFileId
|
||||
FileUtils.writeByteArrayToFile(getTemporaryFile(fileId), file.get)
|
||||
session += "upload_" + fileId -> file.name
|
||||
Ok(fileId)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package app
|
||||
|
||||
import util._
|
||||
import service._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
|
||||
class IndexController extends IndexControllerBase
|
||||
with RepositoryService with AccountService with SystemSettingsService with ActivityService
|
||||
with RepositoryService with SystemSettingsService with ActivityService with AccountService
|
||||
with UsersAuthenticator
|
||||
|
||||
trait IndexControllerBase extends ControllerBase {
|
||||
self: RepositoryService with SystemSettingsService with ActivityService with AccountService
|
||||
with UsersAuthenticator =>
|
||||
|
||||
trait IndexControllerBase extends ControllerBase { self: RepositoryService
|
||||
with SystemSettingsService with ActivityService =>
|
||||
|
||||
get("/"){
|
||||
val loginAccount = context.loginAccount
|
||||
|
||||
@@ -18,4 +22,14 @@ trait IndexControllerBase extends ControllerBase { self: RepositoryService
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* JSON API for collaborator completion.
|
||||
*/
|
||||
// TODO Move to other controller?
|
||||
get("/_user/proposals")(usersOnly {
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(Map("options" -> getAllUsers.filter(!_.isGroupAccount).map(_.userName).toArray))
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
getComments(owner, name, issueId.toInt),
|
||||
getIssueLabels(owner, name, issueId.toInt),
|
||||
(getCollaborators(owner, name) :+ owner).sorted,
|
||||
getMilestones(owner, name),
|
||||
getMilestonesWithIssueCount(owner, name),
|
||||
getLabels(owner, name),
|
||||
hasWritePermission(owner, name, context.loginAccount),
|
||||
repository)
|
||||
@@ -112,7 +112,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
// record activity
|
||||
recordCreateIssueActivity(owner, name, userName, issueId, form.title)
|
||||
|
||||
redirect("/%s/%s/issues/%d".format(owner, name, issueId))
|
||||
redirect(s"/${owner}/${name}/issues/${issueId}")
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (form, repository) =>
|
||||
@@ -122,17 +122,21 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
getIssue(owner, name, params("id")).map { issue =>
|
||||
if(isEditable(owner, name, issue.openedUserName)){
|
||||
updateIssue(owner, name, issue.issueId, form.title, form.content)
|
||||
redirect("/%s/%s/issues/_data/%d".format(owner, name, issue.issueId))
|
||||
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
||||
} else Unauthorized
|
||||
} getOrElse NotFound
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||
handleComment(form.issueId, Some(form.content), repository)
|
||||
handleComment(form.issueId, Some(form.content), repository)() map { id =>
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues/${form.issueId}#comment-${id}")
|
||||
} getOrElse NotFound
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) =>
|
||||
handleComment(form.issueId, form.content, repository)
|
||||
handleComment(form.issueId, form.content, repository)() map { id =>
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues/${form.issueId}#comment-${id}")
|
||||
} getOrElse NotFound
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issue_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) =>
|
||||
@@ -142,7 +146,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
getComment(owner, name, params("id")).map { comment =>
|
||||
if(isEditable(owner, name, comment.commentedUserName)){
|
||||
updateComment(comment.commentId, form.content)
|
||||
redirect("/%s/%s/issue_comments/_data/%d".format(owner, name, comment.commentId))
|
||||
redirect(s"/${owner}/${name}/issue_comments/_data/${comment.commentId}")
|
||||
} else Unauthorized
|
||||
} getOrElse NotFound
|
||||
})
|
||||
@@ -197,79 +201,81 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/:id/assign")(collaboratorsOnly { repository =>
|
||||
updateAssignedUserName(repository.owner, repository.name, params("id").toInt,
|
||||
params.get("assignedUserName") filter (_.trim != ""))
|
||||
updateAssignedUserName(repository.owner, repository.name, params("id").toInt, assignedUserName("assignedUserName"))
|
||||
Ok("updated")
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/:id/milestone")(collaboratorsOnly { repository =>
|
||||
updateMilestoneId(repository.owner, repository.name, params("id").toInt,
|
||||
params.get("milestoneId") collect { case x if x.trim != "" => x.toInt })
|
||||
Ok("updated")
|
||||
updateMilestoneId(repository.owner, repository.name, params("id").toInt, milestoneId("milestoneId"))
|
||||
milestoneId("milestoneId").map { milestoneId =>
|
||||
getMilestonesWithIssueCount(repository.owner, repository.name)
|
||||
.find(_._1.milestoneId == milestoneId).map { case (_, openCount, closeCount) =>
|
||||
issues.milestones.html.progress(openCount + closeCount, closeCount, false)
|
||||
} getOrElse NotFound
|
||||
} getOrElse Ok()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/state")(collaboratorsOnly { repository =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
val userName = context.loginAccount.get.userName
|
||||
val action = params.get("value")
|
||||
|
||||
params.get("value") collect {
|
||||
case s if s == "close" => (s.capitalize, Some(s), true)
|
||||
case s if s == "reopen" => (s.capitalize, Some(s), false)
|
||||
} map { case (content, action, closed) =>
|
||||
params("checked").split(',') foreach { issueId =>
|
||||
createComment(owner, name, userName, issueId.toInt, content, action)
|
||||
updateClosed(owner, name, issueId.toInt, closed)
|
||||
}
|
||||
redirect("/%s/%s/issues".format(owner, name))
|
||||
} getOrElse NotFound
|
||||
executeBatch(repository) {
|
||||
handleComment(_, None, repository)( _ => action)
|
||||
}
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/label")(collaboratorsOnly { repository =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
val labelId = params("value").toInt
|
||||
|
||||
params.get("value").map(_.toInt) map { labelId =>
|
||||
params("checked").split(',') foreach { issueId =>
|
||||
getIssueLabel(owner, name, issueId.toInt, labelId) getOrElse {
|
||||
registerIssueLabel(owner, name, issueId.toInt, labelId)
|
||||
}
|
||||
executeBatch(repository) { issueId =>
|
||||
getIssueLabel(repository.owner, repository.name, issueId, labelId) getOrElse {
|
||||
registerIssueLabel(repository.owner, repository.name, issueId, labelId)
|
||||
}
|
||||
redirect("/%s/%s/issues".format(owner, name))
|
||||
} getOrElse NotFound
|
||||
}
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/assign")(collaboratorsOnly { repository =>
|
||||
params("checked").split(',') foreach { issueId =>
|
||||
updateAssignedUserName(repository.owner, repository.name, issueId.toInt,
|
||||
params.get("value") filter (_.trim != ""))
|
||||
val value = assignedUserName("value")
|
||||
|
||||
executeBatch(repository) {
|
||||
updateAssignedUserName(repository.owner, repository.name, _, value)
|
||||
}
|
||||
redirect("/%s/%s/issues".format(repository.owner, repository.name))
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/milestone")(collaboratorsOnly { repository =>
|
||||
params("checked").split(',') foreach { issueId =>
|
||||
updateMilestoneId(repository.owner, repository.name, issueId.toInt,
|
||||
params.get("value") collect { case x if x.trim != "" => x.toInt })
|
||||
val value = milestoneId("value")
|
||||
|
||||
executeBatch(repository) {
|
||||
updateMilestoneId(repository.owner, repository.name, _, value)
|
||||
}
|
||||
redirect("/%s/%s/issues".format(repository.owner, repository.name))
|
||||
})
|
||||
|
||||
val assignedUserName = (key: String) => params.get(key) filter (_.trim != "")
|
||||
val milestoneId = (key: String) => params.get(key) collect { case x if x.trim != "" => x.toInt }
|
||||
|
||||
private def isEditable(owner: String, repository: String, author: String)(implicit context: app.Context): Boolean =
|
||||
hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
|
||||
|
||||
private def handleComment(issueId: Int, content: Option[String], repository: RepositoryService.RepositoryInfo) = {
|
||||
private def executeBatch(repository: RepositoryService.RepositoryInfo)(execute: Int => Unit) = {
|
||||
params("checked").split(',') map(_.toInt) foreach execute
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues")
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [[https://github.com/takezoe/gitbucket/wiki/CommentAction]]
|
||||
*/
|
||||
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
|
||||
val userName = context.loginAccount.get.userName
|
||||
|
||||
getIssue(owner, name, issueId.toString) map { issue =>
|
||||
val (action, recordActivity) =
|
||||
params.get("action")
|
||||
.filter(_ => isEditable(owner, name, issue.openedUserName))
|
||||
getAction(issue)
|
||||
.collect {
|
||||
case s if s == "close" => true -> (Some(s) -> Some(recordCloseIssueActivity _))
|
||||
case s if s == "reopen" => false -> (Some(s) -> Some(recordReopenIssueActivity _))
|
||||
case "close" => true -> (Some("close") -> Some(recordCloseIssueActivity _))
|
||||
case "reopen" => false -> (Some("reopen") -> Some(recordReopenIssueActivity _))
|
||||
}
|
||||
.map { case (closed, t) =>
|
||||
updateClosed(owner, name, issueId, closed)
|
||||
@@ -277,21 +283,26 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
}
|
||||
.getOrElse(None -> None)
|
||||
|
||||
val commentId = createComment(owner, name, userName, issueId, content.getOrElse(action.get.capitalize), action)
|
||||
val commentId = content
|
||||
.map ( _ -> action.map( _ + "_comment" ).getOrElse("comment") )
|
||||
.getOrElse ( action.get.capitalize -> action.get )
|
||||
match {
|
||||
case (content, action) => createComment(owner, name, userName, issueId, content, action)
|
||||
}
|
||||
|
||||
// record activity
|
||||
content foreach ( recordCommentIssueActivity(owner, name, userName, issueId, _) )
|
||||
recordActivity foreach ( _ (owner, name, userName, issueId, issue.title) )
|
||||
|
||||
redirect("/%s/%s/issues/%d#comment-%d".format(owner, name, issueId, commentId))
|
||||
} getOrElse NotFound
|
||||
commentId
|
||||
}
|
||||
}
|
||||
|
||||
private def searchIssues(filter: String, repository: RepositoryService.RepositoryInfo) = {
|
||||
val owner = repository.owner
|
||||
val repoName = repository.name
|
||||
val userName = if(filter != "all") Some(params("userName")) else None
|
||||
val sessionKey = "%s/%s/issues".format(owner, repoName)
|
||||
val sessionKey = s"${owner}/${repoName}/issues"
|
||||
|
||||
val page = try {
|
||||
val i = params.getOrElse("page", "1").toInt
|
||||
@@ -311,7 +322,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
searchIssue(owner, repoName, condition, filter, userName, (page - 1) * IssueLimit, IssueLimit),
|
||||
page,
|
||||
(getCollaborators(owner, repoName) :+ owner).sorted,
|
||||
getMilestones(owner, repoName).filter(_.closedDate.isEmpty),
|
||||
getMilestones(owner, repoName),
|
||||
getLabels(owner, repoName),
|
||||
countIssue(owner, repoName, condition.copy(state = "open"), filter, userName),
|
||||
countIssue(owner, repoName, condition.copy(state = "closed"), filter, userName),
|
||||
|
||||
@@ -3,7 +3,7 @@ package app
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
|
||||
import service._
|
||||
import util.{CollaboratorsAuthenticator, ReferrerAuthenticator, UsersAuthenticator}
|
||||
import util.{CollaboratorsAuthenticator, ReferrerAuthenticator}
|
||||
|
||||
class MilestonesController extends MilestonesControllerBase
|
||||
with MilestonesService with RepositoryService with AccountService
|
||||
|
||||
@@ -54,22 +54,19 @@ trait RepositorySettingsControllerBase extends ControllerBase with FlashMapSuppo
|
||||
* Display the Collaborators page.
|
||||
*/
|
||||
get("/:owner/:repository/settings/collaborators")(ownerOnly { repository =>
|
||||
settings.html.collaborators(getCollaborators(repository.owner, repository.name), repository)
|
||||
})
|
||||
|
||||
/**
|
||||
* JSON API for collaborator completion.
|
||||
*/
|
||||
get("/:owner/:repository/settings/collaborators/proposals")(usersOnly {
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(Map("options" -> getAllUsers.map(_.userName).toArray))
|
||||
settings.html.collaborators(
|
||||
getCollaborators(repository.owner, repository.name),
|
||||
getAccountByUserName(repository.owner).get.isGroupAccount,
|
||||
repository)
|
||||
})
|
||||
|
||||
/**
|
||||
* Add the collaborator.
|
||||
*/
|
||||
post("/:owner/:repository/settings/collaborators/add", collaboratorForm)(ownerOnly { (form, repository) =>
|
||||
addCollaborator(repository.owner, repository.name, form.userName)
|
||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
||||
addCollaborator(repository.owner, repository.name, form.userName)
|
||||
}
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
||||
})
|
||||
|
||||
@@ -77,7 +74,9 @@ trait RepositorySettingsControllerBase extends ControllerBase with FlashMapSuppo
|
||||
* Add the collaborator.
|
||||
*/
|
||||
get("/:owner/:repository/settings/collaborators/remove")(ownerOnly { repository =>
|
||||
removeCollaborator(repository.owner, repository.name, params("name"))
|
||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
||||
removeCollaborator(repository.owner, repository.name, params("name"))
|
||||
}
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
||||
})
|
||||
|
||||
|
||||
51
src/main/scala/app/SearchController.scala
Normal file
51
src/main/scala/app/SearchController.scala
Normal file
@@ -0,0 +1,51 @@
|
||||
package app
|
||||
|
||||
import util._
|
||||
import service._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
|
||||
class SearchController extends SearchControllerBase
|
||||
with RepositoryService with AccountService with SystemSettingsService with ActivityService
|
||||
with RepositorySearchService with IssuesService
|
||||
with ReferrerAuthenticator
|
||||
|
||||
trait SearchControllerBase extends ControllerBase { self: RepositoryService
|
||||
with SystemSettingsService with ActivityService with RepositorySearchService
|
||||
with ReferrerAuthenticator =>
|
||||
|
||||
val searchForm = mapping(
|
||||
"query" -> trim(text(required)),
|
||||
"owner" -> trim(text(required)),
|
||||
"repository" -> trim(text(required))
|
||||
)(SearchForm.apply)
|
||||
|
||||
case class SearchForm(query: String, owner: String, repository: String)
|
||||
|
||||
post("/search", searchForm){ form =>
|
||||
redirect(s"${form.owner}/${form.repository}/search?q=${StringUtil.urlEncode(form.query)}")
|
||||
}
|
||||
|
||||
get("/:owner/:repository/search")(referrersOnly { repository =>
|
||||
val query = params("q").trim
|
||||
val target = params.getOrElse("type", "code")
|
||||
val page = try {
|
||||
val i = params.getOrElse("page", "1").toInt
|
||||
if(i <= 0) 1 else i
|
||||
} catch {
|
||||
case e: NumberFormatException => 1
|
||||
}
|
||||
|
||||
target.toLowerCase match {
|
||||
case "issue" => search.html.issues(
|
||||
searchIssues(repository.owner, repository.name, query),
|
||||
countFiles(repository.owner, repository.name, query),
|
||||
query, page, repository)
|
||||
|
||||
case _ => search.html.code(
|
||||
searchFiles(repository.owner, repository.name, query),
|
||||
countIssues(repository.owner, repository.name, query),
|
||||
query, page, repository)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
@@ -24,20 +24,19 @@ trait SignInControllerBase extends ControllerBase { self: SystemSettingsService
|
||||
}
|
||||
|
||||
post("/signin", form){ form =>
|
||||
val account = getAccountByUserName(form.userName)
|
||||
if(account.isEmpty || account.get.password != sha1(form.password)){
|
||||
redirect("/signin")
|
||||
} else {
|
||||
session.setAttribute("LOGIN_ACCOUNT", account.get)
|
||||
updateLastLoginDate(account.get.userName)
|
||||
getAccountByUserName(form.userName).collect {
|
||||
case account if(!account.isGroupAccount && account.password == sha1(form.password)) => {
|
||||
session.setAttribute("LOGIN_ACCOUNT", account)
|
||||
updateLastLoginDate(account.userName)
|
||||
|
||||
session.get("REDIRECT").map { redirectUrl =>
|
||||
session.removeAttribute("REDIRECT")
|
||||
redirect(redirectUrl.asInstanceOf[String])
|
||||
}.getOrElse {
|
||||
redirect("/")
|
||||
session.get("REDIRECT").map { redirectUrl =>
|
||||
session.removeAttribute("REDIRECT")
|
||||
redirect(redirectUrl.asInstanceOf[String])
|
||||
}.getOrElse {
|
||||
redirect("/")
|
||||
}
|
||||
}
|
||||
}
|
||||
} getOrElse redirect("/signin")
|
||||
}
|
||||
|
||||
get("/signout"){
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
package app
|
||||
|
||||
import service._
|
||||
import util.{FileUploadUtil, FileUtil, AdminAuthenticator}
|
||||
import util.AdminAuthenticator
|
||||
import util.StringUtil._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
import org.apache.commons.io.FileUtils
|
||||
import util.Directory._
|
||||
import scala.Some
|
||||
|
||||
class UserManagementController extends UserManagementControllerBase with AccountService with AdminAuthenticator
|
||||
class UserManagementController extends UserManagementControllerBase
|
||||
with AccountService with RepositoryService with AdminAuthenticator
|
||||
|
||||
trait UserManagementControllerBase extends AccountManagementControllerBase {
|
||||
self: AccountService with AdminAuthenticator =>
|
||||
self: AccountService with RepositoryService with AdminAuthenticator =>
|
||||
|
||||
case class UserNewForm(userName: String, password: String, mailAddress: String, isAdmin: Boolean,
|
||||
case class NewUserForm(userName: String, password: String, mailAddress: String, isAdmin: Boolean,
|
||||
url: Option[String], fileId: Option[String])
|
||||
|
||||
case class UserEditForm(userName: String, password: Option[String], mailAddress: String, isAdmin: Boolean,
|
||||
case class EditUserForm(userName: String, password: Option[String], mailAddress: String, isAdmin: Boolean,
|
||||
url: Option[String], fileId: Option[String], clearImage: Boolean)
|
||||
|
||||
val newForm = mapping(
|
||||
case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String],
|
||||
memberNames: Option[String])
|
||||
|
||||
case class EditGroupForm(groupName: String, url: Option[String], fileId: Option[String],
|
||||
memberNames: Option[String], clearImage: Boolean)
|
||||
|
||||
val newUserForm = mapping(
|
||||
"userName" -> trim(label("Username" , text(required, maxlength(100), identifier, uniqueUserName))),
|
||||
"password" -> trim(label("Password" , text(required, maxlength(20)))),
|
||||
"mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress()))),
|
||||
"isAdmin" -> trim(label("User Type" , boolean())),
|
||||
"url" -> trim(label("URL" , optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" , optional(text())))
|
||||
)(UserNewForm.apply)
|
||||
)(NewUserForm.apply)
|
||||
|
||||
val editForm = mapping(
|
||||
val editUserForm = mapping(
|
||||
"userName" -> trim(label("Username" , text(required, maxlength(100), identifier))),
|
||||
"password" -> trim(label("Password" , optional(text(maxlength(20))))),
|
||||
"mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||
@@ -36,28 +40,47 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
||||
"url" -> trim(label("URL" , optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" , optional(text()))),
|
||||
"clearImage" -> trim(label("Clear image" , boolean()))
|
||||
)(UserEditForm.apply)
|
||||
|
||||
)(EditUserForm.apply)
|
||||
|
||||
val newGroupForm = mapping(
|
||||
"groupName" -> trim(label("Group name" , text(required, maxlength(100), identifier))),
|
||||
"url" -> trim(label("URL" , optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" , optional(text()))),
|
||||
"memberNames" -> trim(label("Member Names" , optional(text())))
|
||||
)(NewGroupForm.apply)
|
||||
|
||||
val editGroupForm = mapping(
|
||||
"groupName" -> trim(label("Group name" , text(required, maxlength(100), identifier))),
|
||||
"url" -> trim(label("URL" , optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" , optional(text()))),
|
||||
"memberNames" -> trim(label("Member Names" , optional(text()))),
|
||||
"clearImage" -> trim(label("Clear image" , boolean()))
|
||||
)(EditGroupForm.apply)
|
||||
|
||||
get("/admin/users")(adminOnly {
|
||||
admin.users.html.list(getAllUsers())
|
||||
val users = getAllUsers()
|
||||
val members = users.collect { case account if(account.isGroupAccount) =>
|
||||
account.userName -> getGroupMembers(account.userName)
|
||||
}.toMap
|
||||
admin.users.html.list(users, members)
|
||||
})
|
||||
|
||||
get("/admin/users/_new")(adminOnly {
|
||||
admin.users.html.edit(None)
|
||||
get("/admin/users/_newuser")(adminOnly {
|
||||
admin.users.html.user(None)
|
||||
})
|
||||
|
||||
post("/admin/users/_new", newForm)(adminOnly { form =>
|
||||
post("/admin/users/_newuser", newUserForm)(adminOnly { form =>
|
||||
createAccount(form.userName, sha1(form.password), form.mailAddress, form.isAdmin, form.url)
|
||||
updateImage(form.userName, form.fileId, false)
|
||||
redirect("/admin/users")
|
||||
})
|
||||
|
||||
get("/admin/users/:userName/_edit")(adminOnly {
|
||||
get("/admin/users/:userName/_edituser")(adminOnly {
|
||||
val userName = params("userName")
|
||||
admin.users.html.edit(getAccountByUserName(userName))
|
||||
admin.users.html.user(getAccountByUserName(userName))
|
||||
})
|
||||
|
||||
post("/admin/users/:name/_edit", editForm)(adminOnly { form =>
|
||||
post("/admin/users/:name/_edituser", editUserForm)(adminOnly { form =>
|
||||
val userName = params("userName")
|
||||
getAccountByUserName(userName).map { account =>
|
||||
updateAccount(getAccountByUserName(userName).get.copy(
|
||||
@@ -71,5 +94,46 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
} getOrElse NotFound
|
||||
})
|
||||
|
||||
|
||||
get("/admin/users/_newgroup")(adminOnly {
|
||||
admin.users.html.group(None, Nil)
|
||||
})
|
||||
|
||||
post("/admin/users/_newgroup", newGroupForm)(adminOnly { form =>
|
||||
createGroup(form.groupName, form.url)
|
||||
updateGroupMembers(form.groupName, form.memberNames.map(_.split(",").toList).getOrElse(Nil))
|
||||
updateImage(form.groupName, form.fileId, false)
|
||||
redirect("/admin/users")
|
||||
})
|
||||
|
||||
get("/admin/users/:groupName/_editgroup")(adminOnly {
|
||||
val groupName = params("groupName")
|
||||
admin.users.html.group(getAccountByUserName(groupName), getGroupMembers(groupName))
|
||||
})
|
||||
|
||||
post("/admin/users/:groupName/_editgroup", editGroupForm)(adminOnly { form =>
|
||||
val groupName = params("groupName")
|
||||
getAccountByUserName(groupName).map { account =>
|
||||
updateGroup(groupName, form.url)
|
||||
|
||||
val memberNames = form.memberNames.map(_.split(",").toList).getOrElse(Nil)
|
||||
updateGroupMembers(form.groupName, memberNames)
|
||||
|
||||
getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||
removeCollaborators(form.groupName, repositoryName)
|
||||
memberNames.foreach { userName =>
|
||||
addCollaborator(form.groupName, repositoryName, userName)
|
||||
}
|
||||
}
|
||||
|
||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||
redirect("/admin/users")
|
||||
|
||||
} getOrElse NotFound
|
||||
})
|
||||
|
||||
post("/admin/users/_usercheck")(adminOnly {
|
||||
getAccountByUserName(params("userName")).isDefined
|
||||
})
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user