(refs #341)Fix compilation error of delete statements

This commit is contained in:
Naoki Takezoe
2014-07-06 15:42:45 +09:00
parent 606cd83f44
commit cce0b67871
13 changed files with 480 additions and 483 deletions

View File

@@ -1,6 +1,7 @@
package object model extends {
// TODO
val profile = slick.driver.H2Driver
val simple = profile.simple
} with AccountComponent
with ActivityComponent

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
import service.SystemSettingsService.SystemSettings
import util.StringUtil._
import util.LDAPUtil

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
trait ActivityService {

View File

@@ -4,7 +4,7 @@ import scala.slick.jdbc.{StaticQuery => Q}
import Q.interpolation
import model._
import profile.simple._
import simple._
import util.Implicits._
import util.StringUtil._

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
trait LabelsService {

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
trait MilestonesService {

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
trait PullRequestService { self: IssuesService =>
import PullRequestService._

View File

@@ -3,12 +3,12 @@ package service
import util.{FileUtil, StringUtil, JGitUtil}
import util.Directory._
import util.ControlUtil._
import model.Issue
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.treewalk.TreeWalk
import org.eclipse.jgit.lib.FileMode
import org.eclipse.jgit.api.Git
import model.profile.simple.Session
import model._
import simple._
trait RepositorySearchService { self: IssuesService =>
import RepositorySearchService._

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
import util.JGitUtil
trait RepositoryService { self: AccountService =>

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
trait SshKeyService {

View File

@@ -1,190 +1,190 @@
package service
import util.Directory._
import util.ControlUtil._
import SystemSettingsService._
import javax.servlet.http.HttpServletRequest
trait SystemSettingsService {
def baseUrl(implicit request: HttpServletRequest): String = loadSystemSettings().baseUrl(request)
def saveSystemSettings(settings: SystemSettings): Unit = {
defining(new java.util.Properties()){ props =>
settings.baseUrl.foreach(x => props.setProperty(BaseURL, x.replaceFirst("/\\Z", "")))
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
props.setProperty(Gravatar, settings.gravatar.toString)
props.setProperty(Notification, settings.notification.toString)
props.setProperty(Ssh, settings.ssh.toString)
settings.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
if(settings.notification) {
settings.smtp.foreach { smtp =>
props.setProperty(SmtpHost, smtp.host)
smtp.port.foreach(x => props.setProperty(SmtpPort, x.toString))
smtp.user.foreach(props.setProperty(SmtpUser, _))
smtp.password.foreach(props.setProperty(SmtpPassword, _))
smtp.ssl.foreach(x => props.setProperty(SmtpSsl, x.toString))
smtp.fromAddress.foreach(props.setProperty(SmtpFromAddress, _))
smtp.fromName.foreach(props.setProperty(SmtpFromName, _))
}
}
props.setProperty(LdapAuthentication, settings.ldapAuthentication.toString)
if(settings.ldapAuthentication){
settings.ldap.map { ldap =>
props.setProperty(LdapHost, ldap.host)
ldap.port.foreach(x => props.setProperty(LdapPort, x.toString))
ldap.bindDN.foreach(x => props.setProperty(LdapBindDN, x))
ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x))
props.setProperty(LdapBaseDN, ldap.baseDN)
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x))
props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute)
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
}
}
using(new java.io.FileOutputStream(GitBucketConf)){ out =>
props.store(out, null)
}
}
}
def loadSystemSettings(): SystemSettings = {
defining(new java.util.Properties()){ props =>
if(GitBucketConf.exists){
using(new java.io.FileInputStream(GitBucketConf)){ in =>
props.load(in)
}
}
SystemSettings(
getOptionValue[String](props, BaseURL, None).map(x => x.replaceFirst("/\\Z", "")),
getValue(props, AllowAccountRegistration, false),
getValue(props, Gravatar, true),
getValue(props, Notification, false),
getValue(props, Ssh, false),
getOptionValue(props, SshPort, Some(DefaultSshPort)),
if(getValue(props, Notification, false)){
Some(Smtp(
getValue(props, SmtpHost, ""),
getOptionValue(props, SmtpPort, Some(DefaultSmtpPort)),
getOptionValue(props, SmtpUser, None),
getOptionValue(props, SmtpPassword, None),
getOptionValue[Boolean](props, SmtpSsl, None),
getOptionValue(props, SmtpFromAddress, None),
getOptionValue(props, SmtpFromName, None)))
} else {
None
},
getValue(props, LdapAuthentication, false),
if(getValue(props, LdapAuthentication, false)){
Some(Ldap(
getValue(props, LdapHost, ""),
getOptionValue(props, LdapPort, Some(DefaultLdapPort)),
getOptionValue(props, LdapBindDN, None),
getOptionValue(props, LdapBindPassword, None),
getValue(props, LdapBaseDN, ""),
getValue(props, LdapUserNameAttribute, ""),
getOptionValue(props, LdapFullNameAttribute, None),
getValue(props, LdapMailAddressAttribute, ""),
getOptionValue[Boolean](props, LdapTls, None),
getOptionValue(props, LdapKeystore, None)))
} else {
None
}
)
}
}
}
object SystemSettingsService {
import scala.reflect.ClassTag
case class SystemSettings(
baseUrl: Option[String],
allowAccountRegistration: Boolean,
gravatar: Boolean,
notification: Boolean,
ssh: Boolean,
sshPort: Option[Int],
smtp: Option[Smtp],
ldapAuthentication: Boolean,
ldap: Option[Ldap]){
def baseUrl(request: HttpServletRequest): String = baseUrl.getOrElse {
defining(request.getRequestURL.toString){ url =>
url.substring(0, url.length - (request.getRequestURI.length - request.getContextPath.length))
}
}.stripSuffix("/")
}
case class Ldap(
host: String,
port: Option[Int],
bindDN: Option[String],
bindPassword: Option[String],
baseDN: String,
userNameAttribute: String,
fullNameAttribute: Option[String],
mailAttribute: String,
tls: Option[Boolean],
keystore: Option[String])
case class Smtp(
host: String,
port: Option[Int],
user: Option[String],
password: Option[String],
ssl: Option[Boolean],
fromAddress: Option[String],
fromName: Option[String])
val DefaultSshPort = 29418
val DefaultSmtpPort = 25
val DefaultLdapPort = 389
private val BaseURL = "base_url"
private val AllowAccountRegistration = "allow_account_registration"
private val Gravatar = "gravatar"
private val Notification = "notification"
private val Ssh = "ssh"
private val SshPort = "ssh.port"
private val SmtpHost = "smtp.host"
private val SmtpPort = "smtp.port"
private val SmtpUser = "smtp.user"
private val SmtpPassword = "smtp.password"
private val SmtpSsl = "smtp.ssl"
private val SmtpFromAddress = "smtp.from_address"
private val SmtpFromName = "smtp.from_name"
private val LdapAuthentication = "ldap_authentication"
private val LdapHost = "ldap.host"
private val LdapPort = "ldap.port"
private val LdapBindDN = "ldap.bindDN"
private val LdapBindPassword = "ldap.bind_password"
private val LdapBaseDN = "ldap.baseDN"
private val LdapUserNameAttribute = "ldap.username_attribute"
private val LdapFullNameAttribute = "ldap.fullname_attribute"
private val LdapMailAddressAttribute = "ldap.mail_attribute"
private val LdapTls = "ldap.tls"
private val LdapKeystore = "ldap.keystore"
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A =
defining(props.getProperty(key)){ value =>
if(value == null || value.isEmpty) default
else convertType(value).asInstanceOf[A]
}
private def getOptionValue[A: ClassTag](props: java.util.Properties, key: String, default: Option[A]): Option[A] =
defining(props.getProperty(key)){ value =>
if(value == null || value.isEmpty) default
else Some(convertType(value)).asInstanceOf[Option[A]]
}
private def convertType[A: ClassTag](value: String) =
defining(implicitly[ClassTag[A]].runtimeClass){ c =>
if(c == classOf[Boolean]) value.toBoolean
else if(c == classOf[Int]) value.toInt
else value
}
}
package service
import util.Directory._
import util.ControlUtil._
import SystemSettingsService._
import javax.servlet.http.HttpServletRequest
trait SystemSettingsService {
def baseUrl(implicit request: HttpServletRequest): String = loadSystemSettings().baseUrl(request)
def saveSystemSettings(settings: SystemSettings): Unit = {
defining(new java.util.Properties()){ props =>
settings.baseUrl.foreach(x => props.setProperty(BaseURL, x.replaceFirst("/\\Z", "")))
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
props.setProperty(Gravatar, settings.gravatar.toString)
props.setProperty(Notification, settings.notification.toString)
props.setProperty(Ssh, settings.ssh.toString)
settings.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
if(settings.notification) {
settings.smtp.foreach { smtp =>
props.setProperty(SmtpHost, smtp.host)
smtp.port.foreach(x => props.setProperty(SmtpPort, x.toString))
smtp.user.foreach(props.setProperty(SmtpUser, _))
smtp.password.foreach(props.setProperty(SmtpPassword, _))
smtp.ssl.foreach(x => props.setProperty(SmtpSsl, x.toString))
smtp.fromAddress.foreach(props.setProperty(SmtpFromAddress, _))
smtp.fromName.foreach(props.setProperty(SmtpFromName, _))
}
}
props.setProperty(LdapAuthentication, settings.ldapAuthentication.toString)
if(settings.ldapAuthentication){
settings.ldap.map { ldap =>
props.setProperty(LdapHost, ldap.host)
ldap.port.foreach(x => props.setProperty(LdapPort, x.toString))
ldap.bindDN.foreach(x => props.setProperty(LdapBindDN, x))
ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x))
props.setProperty(LdapBaseDN, ldap.baseDN)
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x))
props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute)
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
}
}
using(new java.io.FileOutputStream(GitBucketConf)){ out =>
props.store(out, null)
}
}
}
def loadSystemSettings(): SystemSettings = {
defining(new java.util.Properties()){ props =>
if(GitBucketConf.exists){
using(new java.io.FileInputStream(GitBucketConf)){ in =>
props.load(in)
}
}
SystemSettings(
getOptionValue[String](props, BaseURL, None).map(x => x.replaceFirst("/\\Z", "")),
getValue(props, AllowAccountRegistration, false),
getValue(props, Gravatar, true),
getValue(props, Notification, false),
getValue(props, Ssh, false),
getOptionValue(props, SshPort, Some(DefaultSshPort)),
if(getValue(props, Notification, false)){
Some(Smtp(
getValue(props, SmtpHost, ""),
getOptionValue(props, SmtpPort, Some(DefaultSmtpPort)),
getOptionValue(props, SmtpUser, None),
getOptionValue(props, SmtpPassword, None),
getOptionValue[Boolean](props, SmtpSsl, None),
getOptionValue(props, SmtpFromAddress, None),
getOptionValue(props, SmtpFromName, None)))
} else {
None
},
getValue(props, LdapAuthentication, false),
if(getValue(props, LdapAuthentication, false)){
Some(Ldap(
getValue(props, LdapHost, ""),
getOptionValue(props, LdapPort, Some(DefaultLdapPort)),
getOptionValue(props, LdapBindDN, None),
getOptionValue(props, LdapBindPassword, None),
getValue(props, LdapBaseDN, ""),
getValue(props, LdapUserNameAttribute, ""),
getOptionValue(props, LdapFullNameAttribute, None),
getValue(props, LdapMailAddressAttribute, ""),
getOptionValue[Boolean](props, LdapTls, None),
getOptionValue(props, LdapKeystore, None)))
} else {
None
}
)
}
}
}
object SystemSettingsService {
import scala.reflect.ClassTag
case class SystemSettings(
baseUrl: Option[String],
allowAccountRegistration: Boolean,
gravatar: Boolean,
notification: Boolean,
ssh: Boolean,
sshPort: Option[Int],
smtp: Option[Smtp],
ldapAuthentication: Boolean,
ldap: Option[Ldap]){
def baseUrl(request: HttpServletRequest): String = baseUrl.getOrElse {
defining(request.getRequestURL.toString){ url =>
url.substring(0, url.length - (request.getRequestURI.length - request.getContextPath.length))
}
}.stripSuffix("/")
}
case class Ldap(
host: String,
port: Option[Int],
bindDN: Option[String],
bindPassword: Option[String],
baseDN: String,
userNameAttribute: String,
fullNameAttribute: Option[String],
mailAttribute: String,
tls: Option[Boolean],
keystore: Option[String])
case class Smtp(
host: String,
port: Option[Int],
user: Option[String],
password: Option[String],
ssl: Option[Boolean],
fromAddress: Option[String],
fromName: Option[String])
val DefaultSshPort = 29418
val DefaultSmtpPort = 25
val DefaultLdapPort = 389
private val BaseURL = "base_url"
private val AllowAccountRegistration = "allow_account_registration"
private val Gravatar = "gravatar"
private val Notification = "notification"
private val Ssh = "ssh"
private val SshPort = "ssh.port"
private val SmtpHost = "smtp.host"
private val SmtpPort = "smtp.port"
private val SmtpUser = "smtp.user"
private val SmtpPassword = "smtp.password"
private val SmtpSsl = "smtp.ssl"
private val SmtpFromAddress = "smtp.from_address"
private val SmtpFromName = "smtp.from_name"
private val LdapAuthentication = "ldap_authentication"
private val LdapHost = "ldap.host"
private val LdapPort = "ldap.port"
private val LdapBindDN = "ldap.bindDN"
private val LdapBindPassword = "ldap.bind_password"
private val LdapBaseDN = "ldap.baseDN"
private val LdapUserNameAttribute = "ldap.username_attribute"
private val LdapFullNameAttribute = "ldap.fullname_attribute"
private val LdapMailAddressAttribute = "ldap.mail_attribute"
private val LdapTls = "ldap.tls"
private val LdapKeystore = "ldap.keystore"
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A =
defining(props.getProperty(key)){ value =>
if(value == null || value.isEmpty) default
else convertType(value).asInstanceOf[A]
}
private def getOptionValue[A: ClassTag](props: java.util.Properties, key: String, default: Option[A]): Option[A] =
defining(props.getProperty(key)){ value =>
if(value == null || value.isEmpty) default
else Some(convertType(value)).asInstanceOf[Option[A]]
}
private def convertType[A: ClassTag](value: String) =
defining(implicitly[ClassTag[A]].runtimeClass){ c =>
if(c == classOf[Boolean]) value.toBoolean
else if(c == classOf[Int]) value.toInt
else value
}
}

View File

@@ -1,7 +1,7 @@
package service
import model._
import profile.simple._
import simple._
import org.slf4j.LoggerFactory
import service.RepositoryService.RepositoryInfo
import util.JGitUtil

View File

@@ -1,282 +1,278 @@
package service
import java.util.Date
import org.eclipse.jgit.api.Git
import org.apache.commons.io.FileUtils
import util._
import _root_.util.ControlUtil._
import org.eclipse.jgit.treewalk.{TreeWalk, CanonicalTreeParser}
import org.eclipse.jgit.lib._
import org.eclipse.jgit.dircache.{DirCache, DirCacheEntry}
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter}
import java.io.ByteArrayInputStream
import org.eclipse.jgit.patch._
import org.eclipse.jgit.api.errors.PatchFormatException
import scala.collection.JavaConverters._
import scala.Some
import service.RepositoryService.RepositoryInfo
object WikiService {
/**
* The model for wiki page.
*
* @param name the page name
* @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, id: String)
/**
* The model for wiki page history.
*
* @param name the page name
* @param committer the committer the committer
* @param message the commit message
* @param date the commit date
*/
case class WikiPageHistoryInfo(name: String, committer: String, message: String, date: Date)
def httpUrl(repository: RepositoryInfo) = repository.httpUrl.replaceFirst("\\.git\\Z", ".wiki.git")
def sshUrl(repository: RepositoryInfo, settings: SystemSettingsService.SystemSettings, userName: String) =
repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), userName).replaceFirst("\\.git\\Z", ".wiki.git")
}
trait WikiService {
import WikiService._
def createWikiRepository(loginAccount: model.Account, owner: String, repository: String): Unit =
LockUtil.lock(s"${owner}/${repository}/wiki"){
defining(Directory.getWikiRepositoryDir(owner, repository)){ dir =>
if(!dir.exists){
JGitUtil.initRepository(dir)
saveWikiPage(owner, repository, "Home", "Home", s"Welcome to the ${repository} wiki!!", loginAccount, "Initial Commit", None)
}
}
}
/**
* Returns the wiki page.
*/
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
if(!JGitUtil.isEmpty(git)){
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
WikiPageInfo(file.name, StringUtil.convertFromByteArray(git.getRepository.open(file.id).getBytes),
file.committer, file.time, file.commitId)
}
} else None
}
}
/**
* Returns the content of the specified file.
*/
def getFileContent(owner: String, repository: String, path: String): Option[Array[Byte]] =
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
if(!JGitUtil.isEmpty(git)){
val index = path.lastIndexOf('/')
val parentPath = if(index < 0) "." else path.substring(0, index)
val fileName = if(index < 0) path else path.substring(index + 1)
JGitUtil.getFileList(git, "master", parentPath).find(_.name == fileName).map { file =>
git.getRepository.open(file.id).getBytes
}
} else None
}
/**
* Returns the list of wiki page names.
*/
def getWikiPageList(owner: String, repository: String): List[String] = {
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
JGitUtil.getFileList(git, "master", ".")
.filter(_.name.endsWith(".md"))
.map(_.name.stripSuffix(".md"))
.sortBy(x => x)
}
}
/**
* Reverts specified changes.
*/
def revertWikiPage(owner: String, repository: String, from: String, to: String,
committer: model.Account, pageName: Option[String]): Boolean = {
case class RevertInfo(operation: String, filePath: String, source: String)
try {
LockUtil.lock(s"${owner}/${repository}/wiki"){
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
val reader = git.getRepository.newObjectReader
val oldTreeIter = new CanonicalTreeParser
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
val newTreeIter = new CanonicalTreeParser
newTreeIter.reset(reader, git.getRepository.resolve(to + "^{tree}"))
val diffs = git.diff.setNewTree(oldTreeIter).setOldTree(newTreeIter).call.asScala.filter { diff =>
pageName match {
case Some(x) => diff.getNewPath == x + ".md"
case None => true
}
}
val patch = using(new java.io.ByteArrayOutputStream()){ out =>
val formatter = new DiffFormatter(out)
formatter.setRepository(git.getRepository)
formatter.format(diffs.asJava)
new String(out.toByteArray, "UTF-8")
}
val p = new Patch()
p.parse(new ByteArrayInputStream(patch.getBytes("UTF-8")))
if(!p.getErrors.isEmpty){
throw new PatchFormatException(p.getErrors())
}
val revertInfo = (p.getFiles.asScala.map { fh =>
fh.getChangeType match {
case DiffEntry.ChangeType.MODIFY => {
val source = getWikiPage(owner, repository, fh.getNewPath.stripSuffix(".md")).map(_.content).getOrElse("")
val applied = PatchUtil.apply(source, patch, fh)
if(applied != null){
Seq(RevertInfo("ADD", fh.getNewPath, applied))
} else Nil
}
case DiffEntry.ChangeType.ADD => {
val applied = PatchUtil.apply("", patch, fh)
if(applied != null){
Seq(RevertInfo("ADD", fh.getNewPath, applied))
} else Nil
}
case DiffEntry.ChangeType.DELETE => {
Seq(RevertInfo("DELETE", fh.getNewPath, ""))
}
case DiffEntry.ChangeType.RENAME => {
val applied = PatchUtil.apply("", patch, fh)
if(applied != null){
Seq(RevertInfo("DELETE", fh.getOldPath, ""), RevertInfo("ADD", fh.getNewPath, applied))
} else {
Seq(RevertInfo("DELETE", fh.getOldPath, ""))
}
}
case _ => Nil
}
}).flatten
if(revertInfo.nonEmpty){
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
JGitUtil.processTree(git, headId){ (path, tree) =>
if(revertInfo.find(x => x.filePath == path).isEmpty){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
}
revertInfo.filter(_.operation == "ADD").foreach { x =>
builder.add(JGitUtil.createDirCacheEntry(x.filePath, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, x.source.getBytes("UTF-8"))))
}
builder.finish()
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress,
pageName match {
case Some(x) => s"Revert ${from} ... ${to} on ${x}"
case None => s"Revert ${from} ... ${to}"
})
}
}
}
true
} catch {
case e: Exception => {
e.printStackTrace()
false
}
}
}
/**
* Save the wiki page.
*/
def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String,
content: String, committer: model.Account, message: String, currentId: Option[String]): Option[String] = {
LockUtil.lock(s"${owner}/${repository}/wiki"){
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
var created = true
var updated = false
var removed = false
if(headId != null){
JGitUtil.processTree(git, headId){ (path, tree) =>
if(path == currentPageName + ".md" && currentPageName != newPageName){
removed = true
} else if(path != newPageName + ".md"){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
} else {
created = false
updated = JGitUtil.getContentFromId(git, tree.getEntryObjectId, true).map(new String(_, "UTF-8") != content).getOrElse(false)
}
}
}
if(created || updated || removed){
builder.add(JGitUtil.createDirCacheEntry(newPageName + ".md", FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
builder.finish()
val newHeadId = JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress,
if(message.trim.length == 0) {
if(removed){
s"Rename ${currentPageName} to ${newPageName}"
} else if(created){
s"Created ${newPageName}"
} else {
s"Updated ${newPageName}"
}
} else {
message
})
Some(newHeadId.getName)
} else None
}
}
}
/**
* Delete the wiki page.
*/
def deleteWikiPage(owner: String, repository: String, pageName: String,
committer: String, mailAddress: String, message: String): Unit = {
LockUtil.lock(s"${owner}/${repository}/wiki"){
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
var removed = false
JGitUtil.processTree(git, headId){ (path, tree) =>
if(path != pageName + ".md"){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
} else {
removed = true
}
}
if(removed){
builder.finish()
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer, mailAddress, message)
}
}
}
}
}
package service
import java.util.Date
import org.eclipse.jgit.api.Git
import util._
import _root_.util.ControlUtil._
import org.eclipse.jgit.treewalk.CanonicalTreeParser
import org.eclipse.jgit.lib._
import org.eclipse.jgit.dircache.DirCache
import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter}
import java.io.ByteArrayInputStream
import org.eclipse.jgit.patch._
import org.eclipse.jgit.api.errors.PatchFormatException
import scala.collection.JavaConverters._
import service.RepositoryService.RepositoryInfo
object WikiService {
/**
* The model for wiki page.
*
* @param name the page name
* @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, id: String)
/**
* The model for wiki page history.
*
* @param name the page name
* @param committer the committer the committer
* @param message the commit message
* @param date the commit date
*/
case class WikiPageHistoryInfo(name: String, committer: String, message: String, date: Date)
def httpUrl(repository: RepositoryInfo) = repository.httpUrl.replaceFirst("\\.git\\Z", ".wiki.git")
def sshUrl(repository: RepositoryInfo, settings: SystemSettingsService.SystemSettings, userName: String) =
repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), userName).replaceFirst("\\.git\\Z", ".wiki.git")
}
trait WikiService {
import WikiService._
def createWikiRepository(loginAccount: model.Account, owner: String, repository: String): Unit =
LockUtil.lock(s"${owner}/${repository}/wiki"){
defining(Directory.getWikiRepositoryDir(owner, repository)){ dir =>
if(!dir.exists){
JGitUtil.initRepository(dir)
saveWikiPage(owner, repository, "Home", "Home", s"Welcome to the ${repository} wiki!!", loginAccount, "Initial Commit", None)
}
}
}
/**
* Returns the wiki page.
*/
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
if(!JGitUtil.isEmpty(git)){
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
WikiPageInfo(file.name, StringUtil.convertFromByteArray(git.getRepository.open(file.id).getBytes),
file.committer, file.time, file.commitId)
}
} else None
}
}
/**
* Returns the content of the specified file.
*/
def getFileContent(owner: String, repository: String, path: String): Option[Array[Byte]] =
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
if(!JGitUtil.isEmpty(git)){
val index = path.lastIndexOf('/')
val parentPath = if(index < 0) "." else path.substring(0, index)
val fileName = if(index < 0) path else path.substring(index + 1)
JGitUtil.getFileList(git, "master", parentPath).find(_.name == fileName).map { file =>
git.getRepository.open(file.id).getBytes
}
} else None
}
/**
* Returns the list of wiki page names.
*/
def getWikiPageList(owner: String, repository: String): List[String] = {
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
JGitUtil.getFileList(git, "master", ".")
.filter(_.name.endsWith(".md"))
.map(_.name.stripSuffix(".md"))
.sortBy(x => x)
}
}
/**
* Reverts specified changes.
*/
def revertWikiPage(owner: String, repository: String, from: String, to: String,
committer: model.Account, pageName: Option[String]): Boolean = {
case class RevertInfo(operation: String, filePath: String, source: String)
try {
LockUtil.lock(s"${owner}/${repository}/wiki"){
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
val reader = git.getRepository.newObjectReader
val oldTreeIter = new CanonicalTreeParser
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
val newTreeIter = new CanonicalTreeParser
newTreeIter.reset(reader, git.getRepository.resolve(to + "^{tree}"))
val diffs = git.diff.setNewTree(oldTreeIter).setOldTree(newTreeIter).call.asScala.filter { diff =>
pageName match {
case Some(x) => diff.getNewPath == x + ".md"
case None => true
}
}
val patch = using(new java.io.ByteArrayOutputStream()){ out =>
val formatter = new DiffFormatter(out)
formatter.setRepository(git.getRepository)
formatter.format(diffs.asJava)
new String(out.toByteArray, "UTF-8")
}
val p = new Patch()
p.parse(new ByteArrayInputStream(patch.getBytes("UTF-8")))
if(!p.getErrors.isEmpty){
throw new PatchFormatException(p.getErrors())
}
val revertInfo = (p.getFiles.asScala.map { fh =>
fh.getChangeType match {
case DiffEntry.ChangeType.MODIFY => {
val source = getWikiPage(owner, repository, fh.getNewPath.stripSuffix(".md")).map(_.content).getOrElse("")
val applied = PatchUtil.apply(source, patch, fh)
if(applied != null){
Seq(RevertInfo("ADD", fh.getNewPath, applied))
} else Nil
}
case DiffEntry.ChangeType.ADD => {
val applied = PatchUtil.apply("", patch, fh)
if(applied != null){
Seq(RevertInfo("ADD", fh.getNewPath, applied))
} else Nil
}
case DiffEntry.ChangeType.DELETE => {
Seq(RevertInfo("DELETE", fh.getNewPath, ""))
}
case DiffEntry.ChangeType.RENAME => {
val applied = PatchUtil.apply("", patch, fh)
if(applied != null){
Seq(RevertInfo("DELETE", fh.getOldPath, ""), RevertInfo("ADD", fh.getNewPath, applied))
} else {
Seq(RevertInfo("DELETE", fh.getOldPath, ""))
}
}
case _ => Nil
}
}).flatten
if(revertInfo.nonEmpty){
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
JGitUtil.processTree(git, headId){ (path, tree) =>
if(revertInfo.find(x => x.filePath == path).isEmpty){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
}
revertInfo.filter(_.operation == "ADD").foreach { x =>
builder.add(JGitUtil.createDirCacheEntry(x.filePath, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, x.source.getBytes("UTF-8"))))
}
builder.finish()
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress,
pageName match {
case Some(x) => s"Revert ${from} ... ${to} on ${x}"
case None => s"Revert ${from} ... ${to}"
})
}
}
}
true
} catch {
case e: Exception => {
e.printStackTrace()
false
}
}
}
/**
* Save the wiki page.
*/
def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String,
content: String, committer: model.Account, message: String, currentId: Option[String]): Option[String] = {
LockUtil.lock(s"${owner}/${repository}/wiki"){
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
var created = true
var updated = false
var removed = false
if(headId != null){
JGitUtil.processTree(git, headId){ (path, tree) =>
if(path == currentPageName + ".md" && currentPageName != newPageName){
removed = true
} else if(path != newPageName + ".md"){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
} else {
created = false
updated = JGitUtil.getContentFromId(git, tree.getEntryObjectId, true).map(new String(_, "UTF-8") != content).getOrElse(false)
}
}
}
if(created || updated || removed){
builder.add(JGitUtil.createDirCacheEntry(newPageName + ".md", FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
builder.finish()
val newHeadId = JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress,
if(message.trim.length == 0) {
if(removed){
s"Rename ${currentPageName} to ${newPageName}"
} else if(created){
s"Created ${newPageName}"
} else {
s"Updated ${newPageName}"
}
} else {
message
})
Some(newHeadId.getName)
} else None
}
}
}
/**
* Delete the wiki page.
*/
def deleteWikiPage(owner: String, repository: String, pageName: String,
committer: String, mailAddress: String, message: String): Unit = {
LockUtil.lock(s"${owner}/${repository}/wiki"){
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
var removed = false
JGitUtil.processTree(git, headId){ (path, tree) =>
if(path != pageName + ".md"){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
} else {
removed = true
}
}
if(removed){
builder.finish()
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer, mailAddress, message)
}
}
}
}
}