diff --git a/.travis.yml b/.travis.yml index d39e7f9..dc863a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: scala sudo: true +jdk: + - oraclejdk8 script: - sbt test before_script: @@ -14,40 +16,3 @@ - $HOME/.coursier - $HOME/.embedmysql - $HOME/.embedpostgresql -matrix: - include: - - jdk: oraclejdk8 - addons: - apt: - packages: - - libaio1 - - dist: trusty - group: edge - sudo: required - jdk: oraclejdk9 - addons: - apt: - packages: - - libaio1 - before_install: - - cd ~ - - JDK9_URL=`curl http://jdk.java.net/9/ | grep "lin64JDK" | grep "tar.gz\"" | sed -e "s/\"/ /g" | awk '{print $5}'` - - wget -O jdk-9_linux-x64_bin.tar.gz $JDK9_URL - - tar -xzf jdk-9_linux-x64_bin.tar.gz - - cd - - script: - # https://github.com/sbt/sbt/pull/2951 - - git clone https://github.com/retronym/java9-rt-export - - cd java9-rt-export/ - - git checkout 1019a2873d057dd7214f4135e84283695728395d - - jdk_switcher use oraclejdk8 - - sbt package -# - jdk_switcher use oraclejdk9 - - export JAVA_HOME=~/jdk-9 - - PATH=$JAVA_HOME/bin:$PATH - - java -version - - mkdir -p $HOME/.sbt/0.13/java9-rt-ext; java -jar target/java9-rt-export-*.jar $HOME/.sbt/0.13/java9-rt-ext/rt.jar - - jar tf $HOME/.sbt/0.13/java9-rt-ext/rt.jar | grep java/lang/Object - - cd .. - - wget https://raw.githubusercontent.com/paulp/sbt-extras/9ade5fa54914ca8aded44105bf4b9a60966f3ccd/sbt && chmod +x ./sbt - - ./sbt -Dscala.ext.dirs=$HOME/.sbt/0.13/java9-rt-ext test diff --git a/README.md b/README.md index ea6a1a0..cb016bd 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,11 @@ Release Notes ------------- +### 4.16.0 - 2 Sep 2017 +- Support AdminLTE color skin +- Improve unexpected error handling +- Show commit status on the commits list + ### 4.15.0 - 5 Aug 2017 - Bundle GitBucket organization plugins - Notifications plugin diff --git a/build.sbt b/build.sbt index a2be14c..47a87c7 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ val Organization = "io.github.gitbucket" val Name = "gitbucket" -val GitBucketVersion = "4.15.0" +val GitBucketVersion = "4.16.0" val ScalatraVersion = "2.5.0" val JettyVersion = "9.3.19.v20170502" @@ -29,13 +29,13 @@ "io.github.gitbucket" %% "scalatra-forms" % "1.1.0", "commons-io" % "commons-io" % "2.5", "io.github.gitbucket" % "solidbase" % "1.0.2", - "io.github.gitbucket" % "markedj" % "1.0.14-SNAPSHOT", + "io.github.gitbucket" % "markedj" % "1.0.14", "org.apache.commons" % "commons-compress" % "1.13", "org.apache.commons" % "commons-email" % "1.4", "org.apache.httpcomponents" % "httpclient" % "4.5.3", "org.apache.sshd" % "apache-sshd" % "1.4.0" exclude("org.slf4j","slf4j-jdk14"), "org.apache.tika" % "tika-core" % "1.14", - "com.github.takezoe" %% "blocking-slick-32" % "0.0.9", + "com.github.takezoe" %% "blocking-slick-32" % "0.0.10", "joda-time" % "joda-time" % "2.9.9", "com.novell.ldap" % "jldap" % "2009-10-07", "com.h2database" % "h2" % "1.4.195", diff --git a/plugins.json b/plugins.json index 835a742..6d8d98a 100644 --- a/plugins.json +++ b/plugins.json @@ -5,9 +5,9 @@ "description": "Provides notifications feature on GitBucket.", "versions": [ { - "version": "1.0.0", - "range": ">=4.15.0", - "file": "gitbucket-notifications-plugin_2.12-1.0.0.jar" + "version": "1.1.0", + "range": ">=4.16.0", + "file": "gitbucket-notifications-plugin_2.12-1.1.0.jar" } ], "default": true diff --git a/src/main/scala/ScalatraBootstrap.scala b/src/main/scala/ScalatraBootstrap.scala index 7bc40ca..80e3a58 100644 --- a/src/main/scala/ScalatraBootstrap.scala +++ b/src/main/scala/ScalatraBootstrap.scala @@ -25,11 +25,9 @@ context.getFilterRegistration("gitAuthenticationFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/git/*") context.addFilter("apiAuthenticationFilter", new ApiAuthenticationFilter) context.getFilterRegistration("apiAuthenticationFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/api/v3/*") - context.addFilter("ghCompatRepositoryAccessFilter", new GHCompatRepositoryAccessFilter) - context.getFilterRegistration("ghCompatRepositoryAccessFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*") // Register controllers - context.mount(new AnonymousAccessController, "/*") + context.mount(new PreProcessController, "/*") context.addFilter("pluginControllerFilter", new PluginControllerFilter) context.getFilterRegistration("pluginControllerFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*") diff --git a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala index 9f933c2..6db2af3 100644 --- a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala +++ b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala @@ -40,5 +40,6 @@ new SqlMigration("update/gitbucket-core_4.14.sql") ), new Version("4.14.1"), - new Version("4.15.0") + new Version("4.15.0"), + new Version("4.16.0") ) diff --git a/src/main/scala/gitbucket/core/controller/AnonymousAccessController.scala b/src/main/scala/gitbucket/core/controller/AnonymousAccessController.scala deleted file mode 100644 index 180b164..0000000 --- a/src/main/scala/gitbucket/core/controller/AnonymousAccessController.scala +++ /dev/null @@ -1,14 +0,0 @@ -package gitbucket.core.controller - -class AnonymousAccessController extends AnonymousAccessControllerBase - -trait AnonymousAccessControllerBase extends ControllerBase { - get(!context.settings.allowAnonymousAccess, context.loginAccount.isEmpty) { - if(!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") && - !context.currentPath.startsWith("/register")) { - Unauthorized() - } else { - pass() - } - } -} diff --git a/src/main/scala/gitbucket/core/controller/ApiController.scala b/src/main/scala/gitbucket/core/controller/ApiController.scala index 04e60de..384fa9d 100644 --- a/src/main/scala/gitbucket/core/controller/ApiController.scala +++ b/src/main/scala/gitbucket/core/controller/ApiController.scala @@ -5,14 +5,13 @@ import gitbucket.core.service.IssuesService.IssueSearchCondition import gitbucket.core.service.PullRequestService._ import gitbucket.core.service._ -import gitbucket.core.util.SyntaxSugars._ import gitbucket.core.util.Directory._ -import gitbucket.core.util.JGitUtil._ -import gitbucket.core.util._ import gitbucket.core.util.Implicits._ +import gitbucket.core.util.JGitUtil._ +import gitbucket.core.util.SyntaxSugars._ +import gitbucket.core.util._ import gitbucket.core.view.helpers.{isRenderable, renderMarkup} import org.eclipse.jgit.api.Git -import org.eclipse.jgit.revwalk.RevWalk import org.scalatra.{Created, NoContent, UnprocessableEntity} import scala.collection.JavaConverters._ @@ -127,10 +126,10 @@ /** * https://developer.github.com/v3/repos/branches/#get-branch */ - get ("/api/v3/repos/:owner/:repo/branches/:branch")(referrersOnly { repository => + get ("/api/v3/repos/:owner/:repo/branches/*")(referrersOnly { repository => //import gitbucket.core.api._ (for{ - branch <- params.get("branch") if repository.branchList.contains(branch) + branch <- params.get("splat") if repository.branchList.contains(branch) br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch) } yield { val protection = getProtectedBranchInfo(repository.owner, repository.name, branch) @@ -289,10 +288,10 @@ /** * https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */ - patch("/api/v3/repos/:owner/:repo/branches/:branch")(ownerOnly { repository => + patch("/api/v3/repos/:owner/:repo/branches/*")(ownerOnly { repository => import gitbucket.core.api._ (for{ - branch <- params.get("branch") if repository.branchList.contains(branch) + branch <- params.get("splat") if repository.branchList.contains(branch) protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection) br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch) } yield { diff --git a/src/main/scala/gitbucket/core/controller/ControllerBase.scala b/src/main/scala/gitbucket/core/controller/ControllerBase.scala index 60e7893..41b45cd 100644 --- a/src/main/scala/gitbucket/core/controller/ControllerBase.scala +++ b/src/main/scala/gitbucket/core/controller/ControllerBase.scala @@ -26,6 +26,7 @@ import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.treewalk._ import org.apache.commons.io.IOUtils +import org.slf4j.LoggerFactory /** * Provides generic features for controller implementations. @@ -34,6 +35,8 @@ with ClientSideValidationFormSupport with JacksonJsonSupport with I18nSupport with FlashMapSupport with Validations with SystemSettingsService { + private val logger = LoggerFactory.getLogger(getClass) + implicit val jsonFormats = gitbucket.core.api.JsonFormat.jsonFormats before("/api/v3/*") { @@ -147,6 +150,20 @@ } } + error{ + case e => { + logger.error(s"Catch unhandled error in request: ${request}", e) + if(request.hasAttribute(Keys.Request.Ajax)){ + org.scalatra.InternalServerError() + } else if(request.hasAttribute(Keys.Request.APIv3)){ + contentType = formats("json") + org.scalatra.InternalServerError(ApiError("Internal Server Error")) + } else { + org.scalatra.InternalServerError(gitbucket.core.html.error("Internal Server Error", Some(e))) + } + } + } + override def url(path: String, params: Iterable[(String, Any)] = Iterable.empty, includeContextPath: Boolean = true, includeServletPath: Boolean = true, absolutize: Boolean = true, withSessionId: Boolean = true) diff --git a/src/main/scala/gitbucket/core/controller/PreProcessController.scala b/src/main/scala/gitbucket/core/controller/PreProcessController.scala new file mode 100644 index 0000000..1c5d263 --- /dev/null +++ b/src/main/scala/gitbucket/core/controller/PreProcessController.scala @@ -0,0 +1,40 @@ +package gitbucket.core.controller + +import org.scalatra.MovedPermanently + +class PreProcessController extends PreProcessControllerBase + +trait PreProcessControllerBase extends ControllerBase { + + /** + * Provides GitHub compatible URLs for Git client. + * + * + * + * @see https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols + */ + get("/*/*/info/refs") { + val query = Option(request.getQueryString).map("?" + _).getOrElse("") + halt(MovedPermanently(baseUrl + "/git" + request.getRequestURI + query)) + } + + /** + * Filter requests from anonymous users. + * + * If anonymous access is allowed, pass all requests. + * But if it's not allowed, demands authentication except some paths. + */ + get(!context.settings.allowAnonymousAccess, context.loginAccount.isEmpty) { + if(!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") && + !context.currentPath.startsWith("/register")) { + Unauthorized() + } else { + pass() + } + } + + +} diff --git a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala index 88328e1..839da3f 100644 --- a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala +++ b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala @@ -251,7 +251,7 @@ using(Git.open(getRepositoryDir(owner, name))) { git => // mark issue as merged and close. val loginAccount = context.loginAccount.get - createComment(owner, name, loginAccount.userName, issueId, form.message, "merge") + val commentId = createComment(owner, name, loginAccount.userName, issueId, form.message, "merge") createComment(owner, name, loginAccount.userName, issueId, "Close", "close") updateClosed(owner, name, issueId, true) @@ -282,7 +282,10 @@ callPullRequestWebHook("closed", repository, issueId, context.baseUrl, context.loginAccount.get) // call hooks - PluginRegistry().getPullRequestHooks.foreach(_.merged(issue, repository)) + PluginRegistry().getPullRequestHooks.foreach{ h => + h.addedComment(commentId, form.message, issue, repository) + h.merged(issue, repository) + } redirect(s"/${owner}/${name}/pull/${issueId}") } @@ -321,8 +324,8 @@ get("/:owner/:repository/compare/*...*")(referrersOnly { forkedRepository => val Seq(origin, forked) = multiParams("splat") - val (originOwner, originId) = parseCompareIdentifie(origin, forkedRepository.owner) - val (forkedOwner, forkedId) = parseCompareIdentifie(forked, forkedRepository.owner) + val (originOwner, originId) = parseCompareIdentifier(origin, forkedRepository.owner) + val (forkedOwner, forkedId) = parseCompareIdentifier(forked, forkedRepository.owner) (for( originRepositoryName <- if(originOwner == forkedOwner) { @@ -408,8 +411,8 @@ ajaxGet("/:owner/:repository/compare/*...*/mergecheck")(readableUsersOnly { forkedRepository => val Seq(origin, forked) = multiParams("splat") - val (originOwner, tmpOriginBranch) = parseCompareIdentifie(origin, forkedRepository.owner) - val (forkedOwner, tmpForkedBranch) = parseCompareIdentifie(forked, forkedRepository.owner) + val (originOwner, tmpOriginBranch) = parseCompareIdentifier(origin, forkedRepository.owner) + val (forkedOwner, tmpForkedBranch) = parseCompareIdentifier(forked, forkedRepository.owner) (for( originRepositoryName <- if(originOwner == forkedOwner){ @@ -502,7 +505,7 @@ * - "owner:branch" to ("owner", "branch") * - "branch" to ("defaultOwner", "branch") */ - private def parseCompareIdentifie(value: String, defaultOwner: String): (String, String) = + private def parseCompareIdentifier(value: String, defaultOwner: String): (String, String) = if(value.contains(':')){ val array = value.split(":") (array(0), array(1)) diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 8d68272..df4af27 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -13,7 +13,7 @@ import gitbucket.core.util.SyntaxSugars._ import gitbucket.core.util.Implicits._ import gitbucket.core.util.Directory._ -import gitbucket.core.model.{Account, WebHook} +import gitbucket.core.model.{Account, CommitState, CommitStatus, WebHook} import gitbucket.core.service.WebHookService._ import gitbucket.core.view import gitbucket.core.view.helpers @@ -174,13 +174,24 @@ val (branchName, path) = repository.splitPath(multiParams("splat").head) val page = params.get("page").flatMap(_.toIntOpt).getOrElse(1) + def getStatuses(sha: String): List[CommitStatus] = { + getCommitStatues(repository.owner, repository.name, sha) + } + + def getSummary(statuses: List[CommitStatus]): (CommitState, String) = { + val stateMap = statuses.groupBy(_.state) + val state = CommitState.combine(stateMap.keySet) + val summary = stateMap.map{ case (keyState, states) => states.size+" "+keyState.name }.mkString(", ") + state -> summary + } + using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => JGitUtil.getCommitLog(git, branchName, page, 30, path) match { case Right((logs, hasNext)) => html.commits(if(path.isEmpty) Nil else path.split("/").toList, branchName, repository, logs.splitWith{ (commit1, commit2) => view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime) - }, page, hasNext, hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) + }, page, hasNext, hasDeveloperRole(repository.owner, repository.name, context.loginAccount), getStatuses, getSummary) case Left(_) => NotFound() } } @@ -467,9 +478,13 @@ val comment = getCommitComment(repository.owner, repository.name, commentId.toString).get form.issueId match { case Some(issueId) => - recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, issueId, form.content) - callPullRequestReviewCommentWebHook("create", comment, repository, issueId, context.baseUrl, context.loginAccount.get) - case None => recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content) + getPullRequest(repository.owner, repository.name, issueId).foreach { case (issue, pullRequest) => + recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, issueId, form.content) + PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId, form.content, issue, repository)) + callPullRequestReviewCommentWebHook("create", comment, repository, issue, pullRequest, context.baseUrl, context.loginAccount.get) + } + case None => + recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content) } helper.html.commitcomment(comment, hasDeveloperRole(repository.owner, repository.name, context.loginAccount), repository) }) diff --git a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala index 7841721..080d499 100644 --- a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala @@ -63,7 +63,8 @@ "tls" -> trim(label("Enable TLS", optional(boolean()))), "ssl" -> trim(label("Enable SSL", optional(boolean()))), "keystore" -> trim(label("Keystore", optional(text()))) - )(Ldap.apply)) + )(Ldap.apply)), + "skinName" -> trim(label("AdminLTE skin name", text(required))) )(SystemSettings.apply).verifying { settings => Vector( if(settings.ssh && settings.baseUrl.isEmpty){ @@ -174,8 +175,9 @@ post("/admin/system/sendmail", sendMailForm)(adminOnly { form => try { new Mailer(form.smtp).send(form.testAddress, - "Test message from GitBucket", "This is a test message from GitBucket.", - context.loginAccount.get) + "Test message from GitBucket", context.loginAccount.get, + "This is a test message from GitBucket.", None + ) "Test mail has been sent to: " + form.testAddress diff --git a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala index 075dfd5..3d3d597 100644 --- a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala +++ b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala @@ -264,6 +264,7 @@ (extraJars ++ pluginJars).foreach { pluginJar => val installedJar = new File(installedDir, pluginJar.getName) + FileUtils.copyFile(pluginJar, installedJar) logger.info(s"Initialize ${pluginJar.getName}") @@ -400,12 +401,15 @@ events.foreach { event => logger.info(event.kind + ": " + event.context) } - - gitbucket.core.servlet.Database() withTransaction { session => - logger.info("Reloading plugins...") - PluginRegistry.reload(context, loadSystemSettings(), session.conn) - logger.info("Reloading finished.") - } + new Thread { + override def run(): Unit = { + gitbucket.core.servlet.Database() withTransaction { session => + logger.info("Reloading plugins...") + PluginRegistry.reload(context, loadSystemSettings(), session.conn) + logger.info("Reloading finished.") + } + } + }.start() } detectedWatchKey.reset() } diff --git a/src/main/scala/gitbucket/core/service/IssuesService.scala b/src/main/scala/gitbucket/core/service/IssuesService.scala index ed82f9f..8c183b8 100644 --- a/src/main/scala/gitbucket/core/service/IssuesService.scala +++ b/src/main/scala/gitbucket/core/service/IssuesService.scala @@ -455,9 +455,8 @@ def createIssueComment(owner: String, repository: String, commit: CommitInfo)(implicit s: Session): Unit = { extractIssueId(commit.fullMessage).foreach { issueId => if(getIssue(owner, repository, issueId).isDefined){ - getAccountByMailAddress(commit.committerEmailAddress).foreach { account => - createComment(owner, repository, account.userName, issueId.toInt, commit.fullMessage + " " + commit.id, "commit") - } + val userName = getAccountByMailAddress(commit.committerEmailAddress).map(_.userName).getOrElse(commit.committerName) + createComment(owner, repository, userName, issueId.toInt, commit.fullMessage + " " + commit.id, "commit") } } } diff --git a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala index 248d122..c73ed35 100644 --- a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala +++ b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala @@ -54,6 +54,7 @@ ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x)) } } + props.setProperty(SkinName, settings.skinName.toString) using(new java.io.FileOutputStream(GitBucketConf)){ out => props.store(out, null) } @@ -111,7 +112,8 @@ getOptionValue(props, LdapKeystore, None))) } else { None - } + }, + getValue(props, SkinName, "skin-blue") ) } } @@ -136,7 +138,8 @@ useSMTP: Boolean, smtp: Option[Smtp], ldapAuthentication: Boolean, - ldap: Option[Ldap]){ + ldap: Option[Ldap], + skinName: String){ def baseUrl(request: HttpServletRequest): String = baseUrl.fold(request.baseUrl)(_.stripSuffix("/")) def sshAddress:Option[SshAddress] = @@ -219,6 +222,7 @@ private val LdapTls = "ldap.tls" private val LdapSsl = "ldap.ssl" private val LdapKeystore = "ldap.keystore" + private val SkinName = "skinName" private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = { getSystemProperty(key).getOrElse(getEnvironmentVariable(key).getOrElse { diff --git a/src/main/scala/gitbucket/core/service/WebHookService.scala b/src/main/scala/gitbucket/core/service/WebHookService.scala index dc2b9b6..1558115 100644 --- a/src/main/scala/gitbucket/core/service/WebHookService.scala +++ b/src/main/scala/gitbucket/core/service/WebHookService.scala @@ -296,13 +296,13 @@ trait WebHookPullRequestReviewCommentService extends WebHookService { self: AccountService with RepositoryService with PullRequestService with IssuesService with CommitsService => - def callPullRequestReviewCommentWebHook(action: String, comment: CommitComment, repository: RepositoryService.RepositoryInfo, issueId: Int, baseUrl: String, sender: Account) + def callPullRequestReviewCommentWebHook(action: String, comment: CommitComment, repository: RepositoryService.RepositoryInfo, + issue: Issue, pullRequest: PullRequest, baseUrl: String, sender: Account) (implicit s: Session, c: JsonFormat.Context): Unit = { import WebHookService._ callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment){ + val users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender)) for{ - (issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId) - users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender)) baseOwner <- users.get(repository.owner) headOwner <- users.get(pullRequest.requestUserName) issueUser <- users.get(issue.openedUserName) diff --git a/src/main/scala/gitbucket/core/servlet/GHCompatRepositoryAccessFilter.scala b/src/main/scala/gitbucket/core/servlet/GHCompatRepositoryAccessFilter.scala deleted file mode 100644 index c712dd9..0000000 --- a/src/main/scala/gitbucket/core/servlet/GHCompatRepositoryAccessFilter.scala +++ /dev/null @@ -1,37 +0,0 @@ -package gitbucket.core.servlet - -import javax.servlet._ -import javax.servlet.http.{HttpServletRequest, HttpServletResponse} - -import gitbucket.core.service.SystemSettingsService - -/** - * A controller to provide GitHub compatible URL for Git clients. - */ -class GHCompatRepositoryAccessFilter extends Filter with SystemSettingsService { - - /** - * Pattern of GitHub compatible repository URL. - * /:user/:repo.git/ - */ - private val githubRepositoryPattern = """^/[^/]+/[^/]+\.git/.*""".r - - override def init(filterConfig: FilterConfig) = {} - - override def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain) = { - implicit val request = req.asInstanceOf[HttpServletRequest] - val agent = request.getHeader("USER-AGENT") - val response = res.asInstanceOf[HttpServletResponse] - val requestPath = request.getRequestURI.substring(request.getContextPath.length) - - requestPath match { - case githubRepositoryPattern() if agent != null && agent.toLowerCase.indexOf("git") >= 0 => - response.sendRedirect(baseUrl + "/git" + requestPath) - case _ => - chain.doFilter(req, res) - } - } - - override def destroy() = {} - -} diff --git a/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala b/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala index 06c6ba5..f8be024 100644 --- a/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala +++ b/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala @@ -24,7 +24,7 @@ val controller = PluginRegistry().getControllers().filter { case (_, path) => val requestUri = request.asInstanceOf[HttpServletRequest].getRequestURI val start = path.replaceFirst("/\\*$", "/") - path.endsWith("/*") && (requestUri + "/").startsWith(start) + (requestUri + "/").startsWith(start) } val filterChainWrapper = controller.foldLeft(chain){ case (chain, (controller, _)) => diff --git a/src/main/scala/gitbucket/core/ssh/GitCommand.scala b/src/main/scala/gitbucket/core/ssh/GitCommand.scala index 0629c14..9d7345d 100644 --- a/src/main/scala/gitbucket/core/ssh/GitCommand.scala +++ b/src/main/scala/gitbucket/core/ssh/GitCommand.scala @@ -19,7 +19,7 @@ import org.eclipse.jgit.errors.RepositoryNotFoundException object GitCommand { - val DefaultCommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-_.]+).git'\Z""".r + val DefaultCommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-\+_.]+).git'\Z""".r val SimpleCommandRegex = """\Agit-(upload|receive)-pack '/(.+\.git)'\Z""".r } diff --git a/src/main/scala/gitbucket/core/util/Notifier.scala b/src/main/scala/gitbucket/core/util/Notifier.scala index 0c7d861..61f3c68 100644 --- a/src/main/scala/gitbucket/core/util/Notifier.scala +++ b/src/main/scala/gitbucket/core/util/Notifier.scala @@ -19,8 +19,12 @@ * Please see the plugin for details. */ trait Notifier { + def toNotify(subject: String, textMsg: String) + (recipients: Account => Session => Seq[String])(implicit context: Context): Unit = { + toNotify(subject, textMsg, None)(recipients) + } - def toNotify(subject: String, msg: String) + def toNotify(subject: String, textMsg: String, htmlMsg: Option[String]) (recipients: Account => Session => Seq[String])(implicit context: Context): Unit } @@ -35,7 +39,7 @@ class Mailer(private val smtp: Smtp) extends Notifier { private val logger = LoggerFactory.getLogger(classOf[Mailer]) - def toNotify(subject: String, msg: String) + def toNotify(subject: String, textMsg: String, htmlMsg: Option[String] = None) (recipients: Account => Session => Seq[String])(implicit context: Context): Unit = { context.loginAccount.foreach { loginAccount => val database = Database() @@ -43,7 +47,7 @@ val f = Future { database withSession { session => recipients(loginAccount)(session) foreach { to => - send(to, subject, msg, loginAccount) + send(to, subject, loginAccount, textMsg, htmlMsg) } } "Notifications Successful." @@ -55,7 +59,7 @@ } } - def send(to: String, subject: String, msg: String, loginAccount: Account): Unit = { + def send(to: String, subject: String, loginAccount: Account, textMsg: String, htmlMsg: Option[String] = None): Unit = { val email = new HtmlEmail email.setHostName(smtp.host) email.setSmtpPort(smtp.port.get) @@ -80,13 +84,16 @@ } email.setCharset("UTF-8") email.setSubject(subject) - email.setHtmlMsg(msg) + email.setTextMsg(textMsg) + htmlMsg.foreach { msg => + email.setHtmlMsg(msg) + } email.addTo(to).send } } class MockMailer extends Notifier { - def toNotify(subject: String, msg: String) + def toNotify(subject: String, textMsg: String, htmlMsg: Option[String] = None) (recipients: Account => Session => Seq[String])(implicit context: Context): Unit = () } diff --git a/src/main/twirl/gitbucket/core/admin/system.scala.html b/src/main/twirl/gitbucket/core/admin/system.scala.html index 4234243..4496e65 100644 --- a/src/main/twirl/gitbucket/core/admin/system.scala.html +++ b/src/main/twirl/gitbucket/core/admin/system.scala.html @@ -1,5 +1,6 @@ @(info: Option[Any])(implicit context: gitbucket.core.controller.Context) @import gitbucket.core.util.DatabaseConfig +@import gitbucket.core.view.helpers @gitbucket.core.html.main("System settings"){ @gitbucket.core.admin.html.menu("system"){ @gitbucket.core.helper.html.information(info) @@ -344,6 +345,36 @@ *@ + + + +
+ +
+ +
+ +
+
diff --git a/src/main/twirl/gitbucket/core/error.scala.html b/src/main/twirl/gitbucket/core/error.scala.html index 3c987d8..cd01eda 100644 --- a/src/main/twirl/gitbucket/core/error.scala.html +++ b/src/main/twirl/gitbucket/core/error.scala.html @@ -1,8 +1,22 @@ -@(title: String)(implicit context: gitbucket.core.controller.Context) +@(title: String, e: Option[Throwable]=None)(implicit context: gitbucket.core.controller.Context) @gitbucket.core.html.main("Error"){

@title

+ @if(context.loginAccount.map{_.isAdmin}.getOrElse(false)){ + @e.map { ex => +

@ex.getMessage

+ + + @ex.getStackTrace.map{ st => + + } + +
@st
+ } + } else { +
Please contact your administrator.
+ }
-} \ No newline at end of file +} diff --git a/src/main/twirl/gitbucket/core/main.scala.html b/src/main/twirl/gitbucket/core/main.scala.html index 2324f6e..612491a 100644 --- a/src/main/twirl/gitbucket/core/main.scala.html +++ b/src/main/twirl/gitbucket/core/main.scala.html @@ -16,7 +16,7 @@ - + @@ -42,7 +42,7 @@ } - +
Show all checks +
+ @statuses.map{ status => +
+ @helpers.commitStateIcon(status.state) + @status.context + @status.description.map { desc => - @desc } + + @status.targetUrl.map { url => - Details } + +
+ } +
+ } + }
@@ -86,5 +112,19 @@ } + } } diff --git a/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala b/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala index 27d8c7d..ba24069 100644 --- a/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala +++ b/src/test/scala/gitbucket/core/view/AvatarImageProviderSpec.scala @@ -118,7 +118,9 @@ useSMTP = false, smtp = None, ldapAuthentication = false, - ldap = None) + ldap = None, + skinName = "skin-blue" + ) /** * Adapter to test AvatarImageProviderImpl.