diff --git a/src/main/scala/app/WikiController.scala b/src/main/scala/app/WikiController.scala index 2d7a588..3bffdb3 100644 --- a/src/main/scala/app/WikiController.scala +++ b/src/main/scala/app/WikiController.scala @@ -1,13 +1,14 @@ package app import service._ -import util.{CollaboratorsAuthenticator, ReferrerAuthenticator, JGitUtil, StringUtil} +import util._ import util.Directory._ import util.ControlUtil._ import jp.sf.amateras.scalatra.forms._ import org.eclipse.jgit.api.Git -import org.eclipse.jgit.api.errors.PatchApplyException import org.scalatra.FlashMapSupport +import service.WikiService.WikiPageInfo +import scala.Some class WikiController extends WikiControllerBase with WikiService with RepositoryService with AccountService with ActivityService @@ -17,20 +18,22 @@ self: WikiService with RepositoryService with ActivityService with CollaboratorsAuthenticator with ReferrerAuthenticator => - case class WikiPageEditForm(pageName: String, content: String, message: Option[String], currentPageName: String) + 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))), - "message" -> trim(label("Message" , optional(text()))), - "currentPageName" -> trim(label("Current page name" , text())) + "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))), - "message" -> trim(label("Message" , optional(text()))), - "currentPageName" -> trim(label("Current page name" , text(required))) + "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 => @@ -60,45 +63,43 @@ get("/:owner/:repository/wiki/:page/_compare/:commitId")(referrersOnly { repository => val pageName = StringUtil.urlDecode(params("page")) + val Array(from, to) = params("commitId").split("\\.\\.\\.") - defining(params("commitId").split("\\.\\.\\.")){ case Array(from, to) => - using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => - wiki.html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true), repository, - hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info")) - } + using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => + wiki.html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true), repository, + hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info")) } }) get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository => - defining(params("commitId").split("\\.\\.\\.")){ case Array(from, to) => - using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => - wiki.html.compare(None, from, to, JGitUtil.getDiffs(git, from, to, true), repository, - hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info")) - } + val Array(from, to) = params("commitId").split("\\.\\.\\.") + + using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => + wiki.html.compare(None, from, to, JGitUtil.getDiffs(git, from, to, true), repository, + hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info")) } }) get("/:owner/:repository/wiki/:page/_revert/:commitId")(collaboratorsOnly { repository => val pageName = StringUtil.urlDecode(params("page")) + val Array(from, to) = params("commitId").split("\\.\\.\\.") - defining(params("commitId").split("\\.\\.\\.")){ case Array(from, to) => - if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))){ - redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}") - } else { - flash += "info" -> "This patch was not able to be reversed." - redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}") - } + if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))){ + redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}") + } else { + flash += "info" -> "This patch was not able to be reversed." + redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}") } }) get("/:owner/:repository/wiki/_revert/:commitId")(collaboratorsOnly { repository => - defining(params("commitId").split("\\.\\.\\.")){ case Array(from, to) => - if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)){ - redirect(s"/${repository.owner}/${repository.name}/wiki/}") - } else { - flash += "info" -> "This patch was not able to be reversed." - redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}") - } + val Array(from, to) = params("commitId").split("\\.\\.\\.") + + if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)){ + redirect(s"/${repository.owner}/${repository.name}/wiki/}") + } else { + flash += "info" -> "This patch was not able to be reversed." + redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}") } }) @@ -110,7 +111,7 @@ post("/:owner/:repository/wiki/_edit", editForm)(collaboratorsOnly { (form, repository) => defining(context.loginAccount.get){ loginAccount => saveWikiPage(repository.owner, repository.name, form.currentPageName, form.pageName, - form.content, loginAccount, form.message.getOrElse("")).map { commitId => + form.content, loginAccount, form.message.getOrElse(""), Some(form.id)).map { commitId => updateLastActivityDate(repository.owner, repository.name) recordEditWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId) } @@ -125,7 +126,7 @@ post("/:owner/:repository/wiki/_new", newForm)(collaboratorsOnly { (form, repository) => defining(context.loginAccount.get){ loginAccount => saveWikiPage(repository.owner, repository.name, form.currentPageName, form.pageName, - form.content, loginAccount, form.message.getOrElse("")) + form.content, loginAccount, form.message.getOrElse(""), None) updateLastActivityDate(repository.owner, repository.name) recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName) @@ -160,9 +161,16 @@ }) get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository => - getFileContent(repository.owner, repository.name, multiParams("splat").head).map { content => - contentType = "application/octet-stream" - content + val path = multiParams("splat").head + + getFileContent(repository.owner, repository.name, path).map { bytes => + val mimeType = FileUtil.getMimeType(path) + contentType = if(mimeType == "application/octet-stream" && FileUtil.isText(bytes)){ + "text/plain" + } else { + mimeType + } + bytes } getOrElse NotFound }) @@ -182,5 +190,22 @@ } } + private def conflictForNew: Constraint = new Constraint(){ + override def validate(name: String, value: String): Option[String] = { + optionIf(targetWikiPage.nonEmpty){ + Some("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): Option[String] = { + optionIf(targetWikiPage.map(_.id != params("id")).getOrElse(true)){ + Some("Someone has edited the wiki since you started. Please reload this page and re-apply your changes.") + } + } + } + + private def targetWikiPage = getWikiPage(params("owner"), params("repository"), params("pageName")) } \ No newline at end of file diff --git a/src/main/scala/service/WikiService.scala b/src/main/scala/service/WikiService.scala index 3d09bc3..8802841 100644 --- a/src/main/scala/service/WikiService.scala +++ b/src/main/scala/service/WikiService.scala @@ -19,8 +19,9 @@ * @param content the page content * @param committer the last committer * @param time the last modified time + * @param id the latest commit id */ - case class WikiPageInfo(name: String, content: String, committer: String, time: Date) + case class WikiPageInfo(name: String, content: String, committer: String, time: Date, id: String) /** * The model for wiki page history. @@ -43,7 +44,7 @@ if(!dir.exists){ try { JGitUtil.initRepository(dir) - saveWikiPage(owner, repository, "Home", "Home", s"Welcome to the ${repository} wiki!!", loginAccount, "Initial Commit") + saveWikiPage(owner, repository, "Home", "Home", s"Welcome to the ${repository} wiki!!", loginAccount, "Initial Commit", None) } finally { // once delete cloned repository because initial cloned repository does not have 'branch.master.merge' FileUtils.deleteDirectory(Directory.getWikiWorkDir(owner, repository)) @@ -59,7 +60,7 @@ using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git => optionIf(!JGitUtil.isEmpty(git)){ JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file => - WikiPageInfo(file.name, new String(git.getRepository.open(file.id).getBytes, "UTF-8"), file.committer, file.time) + WikiPageInfo(file.name, new String(git.getRepository.open(file.id).getBytes, "UTF-8"), file.committer, file.time, file.commitId) } } } @@ -148,7 +149,7 @@ * Save the wiki page. */ def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String, - content: String, committer: model.Account, message: String): Option[String] = { + content: String, committer: model.Account, message: String, currentId: Option[String]): Option[String] = { LockUtil.lock(s"${owner}/${repository}/wiki"){ defining(Directory.getWikiWorkDir(owner, repository)){ workDir => @@ -158,6 +159,7 @@ // write as file using(Git.open(workDir)){ git => defining(new File(workDir, newPageName + ".md")){ file => + // new page val created = !file.exists // created or updated diff --git a/src/main/twirl/wiki/edit.scala.html b/src/main/twirl/wiki/edit.scala.html index a9b8d62..6177974 100644 --- a/src/main/twirl/wiki/edit.scala.html +++ b/src/main/twirl/wiki/edit.scala.html @@ -26,6 +26,7 @@ @helper.html.preview(repository, page.map(_.content).getOrElse(""), true, false, "width: 900px; height: 400px;", "") + }