gitbucket.core.controller.WikiController.scala Maven / Gradle / Ivy
The newest version!
package gitbucket.core.controller
import gitbucket.core.model.WebHook
import gitbucket.core.model.activity.{CreateWikiPageInfo, DeleteWikiInfo, EditWikiPageInfo}
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.service.WebHookService.WebHookGollumPayload
import gitbucket.core.wiki.html
import gitbucket.core.service._
import gitbucket.core.util._
import gitbucket.core.util.StringUtil._
import gitbucket.core.util.SyntaxSugars._
import gitbucket.core.util.Implicits._
import gitbucket.core.util.Directory._
import org.scalatra.forms._
import org.eclipse.jgit.api.Git
import org.scalatra.i18n.Messages
import scala.util.Using
class WikiController
extends WikiControllerBase
with WikiService
with RepositoryService
with AccountService
with ActivityService
with WebHookService
with ReadableUsersAuthenticator
with ReferrerAuthenticator
with RequestCache
trait WikiControllerBase extends ControllerBase {
self: WikiService
with RepositoryService
with AccountService
with ActivityService
with WebHookService
with ReadableUsersAuthenticator
with ReferrerAuthenticator =>
case class WikiPageEditForm(
pageName: String,
content: String,
message: Option[String],
currentPageName: String,
id: String
)
val newForm = mapping(
"pageName" -> trim(label("Page name", text(required, maxlength(40), pageName, unique))),
"content" -> trim(label("Content", text(required, conflictForNew))),
"message" -> trim(label("Message", optional(text()))),
"currentPageName" -> trim(label("Current page name", text())),
"id" -> trim(label("Latest commit id", text()))
)(WikiPageEditForm.apply)
val editForm = mapping(
"pageName" -> trim(label("Page name", text(required, maxlength(40), pageName))),
"content" -> trim(label("Content", text(required, conflictForEdit))),
"message" -> trim(label("Message", optional(text()))),
"currentPageName" -> trim(label("Current page name", text(required))),
"id" -> trim(label("Latest commit id", text(required)))
)(WikiPageEditForm.apply)
get("/:owner/:repository/wiki")(referrersOnly { repository =>
val branch = getWikiBranch(repository.owner, repository.name)
getWikiPage(repository.owner, repository.name, "Home", branch).map { page =>
html.page(
"Home",
page,
getWikiPageList(repository.owner, repository.name, branch),
repository,
isEditable(repository),
getWikiPage(repository.owner, repository.name, "_Sidebar", branch),
getWikiPage(repository.owner, repository.name, "_Footer", branch)
)
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/Home/_edit")
})
get("/:owner/:repository/wiki/:page")(referrersOnly { repository =>
val pageName = StringUtil.urlDecode(params("page"))
val branch = getWikiBranch(repository.owner, repository.name)
getWikiPage(repository.owner, repository.name, pageName, branch).map { page =>
html.page(
pageName,
page,
getWikiPageList(repository.owner, repository.name, branch),
repository,
isEditable(repository),
getWikiPage(repository.owner, repository.name, "_Sidebar", branch),
getWikiPage(repository.owner, repository.name, "_Footer", branch)
)
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_edit")
})
get("/:owner/:repository/wiki/:page/_history")(referrersOnly { repository =>
val pageName = StringUtil.urlDecode(params("page"))
val branch = getWikiBranch(repository.owner, repository.name)
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
JGitUtil.getCommitLog(git, branch, path = pageName + ".md") match {
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
case Left(_) => NotFound()
}
}
})
private def getWikiBranch(owner: String, repository: String): String = {
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
git.getRepository.getBranch
}
}
get("/:owner/:repository/wiki/:page/_compare/:commitId")(referrersOnly { repository =>
val pageName = StringUtil.urlDecode(params("page"))
val Array(from, to) = params("commitId").split("\\.\\.\\.")
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
html.compare(
Some(pageName),
from,
to,
JGitUtil
.getDiffs(
git = git,
from = Some(from),
to = to,
fetchContent = true,
makePatch = false,
maxFiles = context.settings.repositoryViewer.maxDiffFiles,
maxLines = context.settings.repositoryViewer.maxDiffLines
)
.filter(_.newPath == pageName + ".md"),
repository,
isEditable(repository),
flash.get("info")
)
}
})
get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository =>
val Array(from, to) = params("commitId").split("\\.\\.\\.")
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
html.compare(
None,
from,
to,
JGitUtil.getDiffs(
git = git,
from = Some(from),
to = to,
fetchContent = true,
makePatch = false,
maxFiles = context.settings.repositoryViewer.maxDiffFiles,
maxLines = context.settings.repositoryViewer.maxDiffLines
),
repository,
isEditable(repository),
flash.get("info")
)
}
})
get("/:owner/:repository/wiki/:page/_revert/:commitId")(readableUsersOnly { repository =>
context.withLoginAccount { loginAccount =>
if (isEditable(repository)) {
val pageName = StringUtil.urlDecode(params("page"))
val Array(from, to) = params("commitId").split("\\.\\.\\.")
val branch = getWikiBranch(repository.owner, repository.name)
if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, Some(pageName), branch)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
} else {
flash.update("info", "This patch was not able to be reversed.")
redirect(
s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
)
}
} else Unauthorized()
}
})
get("/:owner/:repository/wiki/_revert/:commitId")(readableUsersOnly { repository =>
context.withLoginAccount { loginAccount =>
if (isEditable(repository)) {
val Array(from, to) = params("commitId").split("\\.\\.\\.")
val branch = getWikiBranch(repository.owner, repository.name)
if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, None, branch)) {
redirect(s"/${repository.owner}/${repository.name}/wiki")
} else {
flash.update("info", "This patch was not able to be reversed.")
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
}
} else Unauthorized()
}
})
get("/:owner/:repository/wiki/:page/_edit")(readableUsersOnly { repository =>
if (isEditable(repository)) {
val pageName = StringUtil.urlDecode(params("page"))
val branch = getWikiBranch(repository.owner, repository.name)
html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName, branch), repository)
} else Unauthorized()
})
post("/:owner/:repository/wiki/_edit", editForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { loginAccount =>
if (isEditable(repository)) {
saveWikiPage(
repository.owner,
repository.name,
form.currentPageName,
form.pageName,
appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
loginAccount,
form.message.getOrElse(""),
Some(form.id)
).foreach { commitId =>
updateLastActivityDate(repository.owner, repository.name)
val wikiEditInfo =
EditWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId)
recordActivity(wikiEditInfo)
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
getAccountByUserName(repository.owner).map { repositoryUser =>
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
}
}
}
if (notReservedPageName(form.pageName)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
} else {
redirect(s"/${repository.owner}/${repository.name}/wiki")
}
} else Unauthorized()
}
})
get("/:owner/:repository/wiki/_new")(readableUsersOnly { repository =>
if (isEditable(repository)) {
html.edit("", None, repository)
} else Unauthorized()
})
post("/:owner/:repository/wiki/_new", newForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { loginAccount =>
if (isEditable(repository)) {
saveWikiPage(
repository.owner,
repository.name,
form.currentPageName,
form.pageName,
form.content,
loginAccount,
form.message.getOrElse(""),
None
).foreach { commitId =>
updateLastActivityDate(repository.owner, repository.name)
val createWikiPageInfo =
CreateWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName)
recordActivity(createWikiPageInfo)
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
getAccountByUserName(repository.owner).map { repositoryUser =>
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
}
}
}
if (notReservedPageName(form.pageName)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
} else {
redirect(s"/${repository.owner}/${repository.name}/wiki")
}
} else Unauthorized()
}
})
get("/:owner/:repository/wiki/:page/_delete")(readableUsersOnly { repository =>
context.withLoginAccount { loginAccount =>
if (isEditable(repository)) {
val pageName = StringUtil.urlDecode(params("page"))
deleteWikiPage(
repository.owner,
repository.name,
pageName,
loginAccount.fullName,
loginAccount.mailAddress,
s"Destroyed ${pageName}"
)
val deleteWikiInfo = DeleteWikiInfo(
repository.owner,
repository.name,
loginAccount.userName,
pageName
)
recordActivity(deleteWikiInfo)
updateLastActivityDate(repository.owner, repository.name)
redirect(s"/${repository.owner}/${repository.name}/wiki")
} else Unauthorized()
}
})
get("/:owner/:repository/wiki/_pages")(referrersOnly { repository =>
val branch = getWikiBranch(repository.owner, repository.name)
html.pages(getWikiPageList(repository.owner, repository.name, branch), repository, isEditable(repository))
})
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
JGitUtil.getCommitLog(git, "master") match {
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
case Left(_) => NotFound()
}
}
})
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
val path = multiParams("splat").head
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master"))
getPathObjectId(git, path, revCommit).map { objectId =>
responseRawFile(git, objectId, path, repository)
} getOrElse NotFound()
}
})
private def unique: Constraint = new Constraint() {
override def validate(
name: String,
value: String,
params: Map[String, Seq[String]],
messages: Messages
): Option[String] = {
val owner = params.value("owner")
val repository = params.value("repository")
val branch = getWikiBranch(owner, repository)
getWikiPageList(owner, repository, branch)
.find(_ == value)
.map(_ => "Page already exists.")
}
}
private def pageName: Constraint = new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] =
if (value.exists("\\/:*?\"<>|".contains(_))) {
Some(s"${name} contains invalid character.")
} else if (notReservedPageName(value) && (value.startsWith("_") || value.startsWith("-"))) {
Some(s"${name} starts with invalid character.")
} else {
None
}
}
private def notReservedPageName(value: String): Boolean = !(Array[String]("_Sidebar", "_Footer") contains value)
private def conflictForNew: Constraint = new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] = {
targetWikiPage.map { _ =>
"Someone has created the wiki since you started. Please reload this page and re-apply your changes."
}
}
}
private def conflictForEdit: Constraint = new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] = {
targetWikiPage.filter(_.id != params("id")).map { _ =>
"Someone has edited the wiki since you started. Please reload this page and re-apply your changes."
}
}
}
private def targetWikiPage: Option[WikiService.WikiPageInfo] = {
val owner = params("owner")
val repository = params("repository")
val pageName = params("pageName")
val branch = getWikiBranch(owner, repository)
getWikiPage(owner, repository, pageName, branch)
}
private def isEditable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
repository.repository.options.wikiOption match {
case "ALL" => !repository.repository.isPrivate && context.loginAccount.isDefined
case "PUBLIC" => hasGuestRole(repository.owner, repository.name, context.loginAccount)
case "PRIVATE" => hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
case "DISABLE" => false
}
}
}