diff --git a/src/main/scala/model/package.scala b/src/main/scala/model/package.scala index 6c70c66..6ba4752 100644 --- a/src/main/scala/model/package.scala +++ b/src/main/scala/model/package.scala @@ -1,6 +1,7 @@ package object model extends { // TODO val profile = slick.driver.H2Driver + val simple = profile.simple } with AccountComponent with ActivityComponent diff --git a/src/main/scala/service/AccountService.scala b/src/main/scala/service/AccountService.scala index d165f63..b29891c 100644 --- a/src/main/scala/service/AccountService.scala +++ b/src/main/scala/service/AccountService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ import service.SystemSettingsService.SystemSettings import util.StringUtil._ import util.LDAPUtil diff --git a/src/main/scala/service/ActivityService.scala b/src/main/scala/service/ActivityService.scala index 99b2d67..0be6242 100644 --- a/src/main/scala/service/ActivityService.scala +++ b/src/main/scala/service/ActivityService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ trait ActivityService { diff --git a/src/main/scala/service/IssuesService.scala b/src/main/scala/service/IssuesService.scala index 6f421be..46ac85e 100644 --- a/src/main/scala/service/IssuesService.scala +++ b/src/main/scala/service/IssuesService.scala @@ -4,7 +4,7 @@ import Q.interpolation import model._ -import profile.simple._ +import simple._ import util.Implicits._ import util.StringUtil._ diff --git a/src/main/scala/service/LabelsService.scala b/src/main/scala/service/LabelsService.scala index 251e5fd..ff41173 100644 --- a/src/main/scala/service/LabelsService.scala +++ b/src/main/scala/service/LabelsService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ trait LabelsService { diff --git a/src/main/scala/service/MilestonesService.scala b/src/main/scala/service/MilestonesService.scala index 60073da..d236486 100644 --- a/src/main/scala/service/MilestonesService.scala +++ b/src/main/scala/service/MilestonesService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ trait MilestonesService { diff --git a/src/main/scala/service/PullRequestService.scala b/src/main/scala/service/PullRequestService.scala index 026d640..beb79b9 100644 --- a/src/main/scala/service/PullRequestService.scala +++ b/src/main/scala/service/PullRequestService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ trait PullRequestService { self: IssuesService => import PullRequestService._ diff --git a/src/main/scala/service/RepositorySearchService.scala b/src/main/scala/service/RepositorySearchService.scala index 5c9830f..46821cd 100644 --- a/src/main/scala/service/RepositorySearchService.scala +++ b/src/main/scala/service/RepositorySearchService.scala @@ -3,12 +3,12 @@ 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._ diff --git a/src/main/scala/service/RepositoryService.scala b/src/main/scala/service/RepositoryService.scala index 6d8206b..cdc72fe 100644 --- a/src/main/scala/service/RepositoryService.scala +++ b/src/main/scala/service/RepositoryService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ import util.JGitUtil trait RepositoryService { self: AccountService => diff --git a/src/main/scala/service/SshKeyService.scala b/src/main/scala/service/SshKeyService.scala index d38804a..23feffa 100644 --- a/src/main/scala/service/SshKeyService.scala +++ b/src/main/scala/service/SshKeyService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ trait SshKeyService { diff --git a/src/main/scala/service/SystemSettingsService.scala b/src/main/scala/service/SystemSettingsService.scala index fcceecb..42f3a75 100644 --- a/src/main/scala/service/SystemSettingsService.scala +++ b/src/main/scala/service/SystemSettingsService.scala @@ -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 + } + +} diff --git a/src/main/scala/service/WebHookService.scala b/src/main/scala/service/WebHookService.scala index 9061a07..eaa3e59 100644 --- a/src/main/scala/service/WebHookService.scala +++ b/src/main/scala/service/WebHookService.scala @@ -1,7 +1,7 @@ package service import model._ -import profile.simple._ +import simple._ import org.slf4j.LoggerFactory import service.RepositoryService.RepositoryInfo import util.JGitUtil diff --git a/src/main/scala/service/WikiService.scala b/src/main/scala/service/WikiService.scala index 7f5f908..f634e34 100644 --- a/src/main/scala/service/WikiService.scala +++ b/src/main/scala/service/WikiService.scala @@ -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) + } + } + } + } + +}