From 3fdb444961f6ed490279a1a740a7cac81fd5d35c Mon Sep 17 00:00:00 2001 From: Roy Li Date: Wed, 27 Jul 2016 11:23:29 -0700 Subject: [PATCH 001/413] Added new API to handle file upload to repository --- .../RepositoryViewerController.scala | 116 +++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 2563558d8..4c43b8b68 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -1,7 +1,8 @@ package gitbucket.core.controller -import javax.servlet.http.{HttpServletResponse, HttpServletRequest} +import javax.servlet.http.{HttpServletRequest, HttpServletResponse} +import gitbucket.core.api.UploadFiles import gitbucket.core.plugin.PluginRegistry import gitbucket.core.repo.html import gitbucket.core.helper @@ -16,7 +17,6 @@ import gitbucket.core.model.{Account, WebHook} import gitbucket.core.service.WebHookService._ import gitbucket.core.view import gitbucket.core.view.helpers - import io.github.gitbucket.scalatra.forms._ import org.apache.commons.io.FileUtils import org.eclipse.jgit.api.{ArchiveCommand, Git} @@ -526,6 +526,118 @@ trait RepositoryViewerControllerBase extends ControllerBase { } }) + /** + * Upload file to a branch. + */ + post("/:owner/:repository/files/upload")(collaboratorsOnly { repository => + defining(repository.owner, repository.name){ case (owner, name) => + (for { + data <- extractFromJsonBody[UploadFiles] if data.isValid + } yield { + Directory.getAttachedDir(owner, name) match { + case dir if (dir.exists && dir.isDirectory) => + val _commitFiles = data.fileIds.map { case (fileName, id) => + dir.listFiles.find(_.getName.startsWith(id + ".")).map { file => + val s = scala.io.Source.fromFile(file) // Codec ???? + val byteArray = s.map(_.toByte).toArray + + CommitFile(id, fileName, byteArray) + } + }.toList + + val finalCommitFiles = _commitFiles.flatten + if(finalCommitFiles.size == data.fileIds.size) { + commitFiles( + repository, + files = finalCommitFiles, + branch = data.branch, + path = data.path, + message = data.message) + } + else { + org.scalatra.NotAcceptable( + s"""{"message": + |"$repository doesn't contain all the files you specified in the body"}""".stripMargin) + } + + case _ => org.scalatra.NotFound(s"""{"message": "$repository doesn't contain any attached files"}""") + } + + }) getOrElse + org.scalatra.NotAcceptable("""{"message": "FileIds can't be an empty list"}""") + + } + }) + + /* +Roy Li modification + */ + + case class CommitFile(fileId: String, name: String, fileBytes: Array[Byte]) + + private def commitFiles(repository: RepositoryService.RepositoryInfo, + files: List[CommitFile], + branch: String, path: String, message: String) = { + + + LockUtil.lock(s"${repository.owner}/${repository.name}") { + using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => + + val loginAccount = context.loginAccount.get + val builder = DirCache.newInCore.builder() + val inserter = git.getRepository.newObjectInserter() + val headName = s"refs/heads/${branch}" + val headTip = git.getRepository.resolve(headName) + + if(headTip != null){ + JGitUtil.processTree(git, headTip) { (path, tree) => + builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId)) + } + } + + files.foreach{item => + val fileName = item.name + val bytes = item.fileBytes + builder.add(JGitUtil.createDirCacheEntry(fileName, + FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, bytes))) + builder.finish() + } + + val commitId = JGitUtil.createNewCommit(git, inserter, headTip, builder.getDirCache.writeTree(inserter), + headName, loginAccount.userName, loginAccount.mailAddress, message) + + inserter.flush() + inserter.close() + + // update refs + val refUpdate = git.getRepository.updateRef(headName) + refUpdate.setNewObjectId(commitId) + refUpdate.setForceUpdate(false) + refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)) + //refUpdate.setRefLogMessage("merged", true) + refUpdate.update() + + // update pull request + updatePullRequests(repository.owner, repository.name, branch) + + // record activity + recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch, + List(new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId)))) + + //call web hook + callPullRequestWebHookByRequestBranch("synchronize", repository, branch, context.baseUrl, loginAccount) + val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId)) + callWebHookOf(repository.owner, repository.name, WebHook.Push) { + getAccountByUserName(repository.owner).map{ ownerAccount => + WebHookPushPayload(git, loginAccount, headName, repository, List(commit), ownerAccount, + oldId = headTip, newId = commitId) + } + } + + } + } + } + private def splitPath(repository: RepositoryService.RepositoryInfo, path: String): (String, String) = { val id = repository.branchList.collectFirst { case branch if(path == branch || path.startsWith(branch + "/")) => branch From d6f8a458897b920382e419868dd8486deb193361 Mon Sep 17 00:00:00 2001 From: Roy Li Date: Wed, 27 Jul 2016 11:33:38 -0700 Subject: [PATCH 002/413] added helper case class for json body --- src/main/scala/gitbucket/core/api/UploadFiles.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/scala/gitbucket/core/api/UploadFiles.scala diff --git a/src/main/scala/gitbucket/core/api/UploadFiles.scala b/src/main/scala/gitbucket/core/api/UploadFiles.scala new file mode 100644 index 000000000..e682011b6 --- /dev/null +++ b/src/main/scala/gitbucket/core/api/UploadFiles.scala @@ -0,0 +1,12 @@ +package gitbucket.core.api + + +case class UploadFiles(branch: String, path: String, fileIds : Map[String,String], message: String) +{ + def isValid: Boolean = { + + fileIds.size > 0 + } + + +} From c760af7810410d9167098c6902938d942f71366e Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sat, 11 Mar 2017 11:19:57 +0900 Subject: [PATCH 003/413] Add adapter filter for plugin controllers --- build.sbt | 2 +- src/main/scala/ScalatraBootstrap.scala | 5 ++- .../core/servlet/PluginControllerFilter.scala | 34 +++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala diff --git a/build.sbt b/build.sbt index d219ff3a2..d3532de1e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ val Organization = "io.github.gitbucket" val Name = "gitbucket" -val GitBucketVersion = "4.10.0" +val GitBucketVersion = "4.11.0-SNAPSHOT" val ScalatraVersion = "2.5.0" val JettyVersion = "9.3.9.v20160517" diff --git a/src/main/scala/ScalatraBootstrap.scala b/src/main/scala/ScalatraBootstrap.scala index 949bf80d9..c767f591c 100644 --- a/src/main/scala/ScalatraBootstrap.scala +++ b/src/main/scala/ScalatraBootstrap.scala @@ -31,9 +31,8 @@ class ScalatraBootstrap extends LifeCycle with SystemSettingsService { // Register controllers context.mount(new AnonymousAccessController, "/*") - PluginRegistry().getControllers.foreach { case (controller, path) => - context.mount(controller, path) - } + context.addFilter("pluginControllerFilter", new PluginControllerFilter) + context.getFilterRegistration("pluginControllerFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*") context.mount(new IndexController, "/") context.mount(new ApiController, "/api/v3") diff --git a/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala b/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala new file mode 100644 index 000000000..95060f00c --- /dev/null +++ b/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala @@ -0,0 +1,34 @@ +package gitbucket.core.servlet + +import javax.servlet._ +import javax.servlet.http.HttpServletRequest + +import gitbucket.core.plugin.PluginRegistry + +class PluginControllerFilter extends Filter { + + override def init(filterConfig: FilterConfig): Unit = { + PluginRegistry().getControllers().foreach { case (controller, _) => + controller.init(filterConfig) + } + } + + override def destroy(): Unit = { + PluginRegistry().getControllers().foreach { case (controller, _) => + controller.destroy() + } + } + + override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = { + val controller = PluginRegistry().getControllers().find { case (_, path) => + val requestUri = request.asInstanceOf[HttpServletRequest].getRequestURI + path.endsWith("/*") && requestUri.startsWith(path.replaceFirst("/\\*$", "/")) + } + + controller.map { case (controller, _) => + controller.doFilter(request, response, chain) + }.getOrElse{ + chain.doFilter(request, response) + } + } +} From 7e7e45e794de76ac0994f8a000a81038afe96dd9 Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sat, 11 Mar 2017 12:13:59 +0900 Subject: [PATCH 004/413] Add the plugin reload button to the plugin list page --- .../controller/SystemSettingsController.scala | 7 ++++++- .../gitbucket/core/plugin/PluginRegistory.scala | 16 ++++++++++++---- .../core/servlet/PluginControllerFilter.scala | 9 ++++++--- .../gitbucket/core/admin/plugins.scala.html | 7 +++++-- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala index ac2fbff14..e47d0fc89 100644 --- a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala @@ -181,9 +181,14 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase { }) get("/admin/plugins")(adminOnly { - html.plugins(PluginRegistry().getPlugins()) + html.plugins(PluginRegistry().getPlugins(), flash.get("info")) }) + post("/admin/plugins/_reload")(adminOnly { + PluginRegistry.reload(request.getServletContext(), loadSystemSettings(), request2Session(request).conn) + flash += "info" -> "All plugins are reloaded." + redirect("/admin/plugins") + }) get("/admin/users")(adminOnly { val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false) diff --git a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala index 7ba1026e6..76c360233 100644 --- a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala +++ b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala @@ -155,17 +155,26 @@ object PluginRegistry { private val logger = LoggerFactory.getLogger(classOf[PluginRegistry]) - private val instance = new PluginRegistry() + private var instance = new PluginRegistry() /** * Returns the PluginRegistry singleton instance. */ def apply(): PluginRegistry = instance + /** + * Reload all plugins. + */ + def reload(context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = synchronized { + shutdown(context, settings) + instance = new PluginRegistry() + initialize(context, settings, conn) + } + /** * Initializes all installed plugins. */ - def initialize(context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = { + def initialize(context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = synchronized { val pluginDir = new File(PluginHome) val manager = new JDBCVersionManager(conn) @@ -207,7 +216,7 @@ object PluginRegistry { } } - def shutdown(context: ServletContext, settings: SystemSettings): Unit = { + def shutdown(context: ServletContext, settings: SystemSettings): Unit = synchronized { instance.getPlugins().foreach { pluginInfo => try { pluginInfo.pluginClass.shutdown(instance, context, settings) @@ -219,7 +228,6 @@ object PluginRegistry { } } - } case class Link(id: String, label: String, path: String, icon: Option[String] = None) diff --git a/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala b/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala index 95060f00c..442841b1b 100644 --- a/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala +++ b/src/main/scala/gitbucket/core/servlet/PluginControllerFilter.scala @@ -7,10 +7,10 @@ import gitbucket.core.plugin.PluginRegistry class PluginControllerFilter extends Filter { + private var filterConfig: FilterConfig = null + override def init(filterConfig: FilterConfig): Unit = { - PluginRegistry().getControllers().foreach { case (controller, _) => - controller.init(filterConfig) - } + this.filterConfig = filterConfig } override def destroy(): Unit = { @@ -26,6 +26,9 @@ class PluginControllerFilter extends Filter { } controller.map { case (controller, _) => + if(controller.config == null){ + controller.init(filterConfig) + } controller.doFilter(request, response, chain) }.getOrElse{ chain.doFilter(request, response) diff --git a/src/main/twirl/gitbucket/core/admin/plugins.scala.html b/src/main/twirl/gitbucket/core/admin/plugins.scala.html index 0186e4983..446bdfc29 100644 --- a/src/main/twirl/gitbucket/core/admin/plugins.scala.html +++ b/src/main/twirl/gitbucket/core/admin/plugins.scala.html @@ -1,8 +1,11 @@ -@(plugins: List[gitbucket.core.plugin.PluginInfo])(implicit context: gitbucket.core.controller.Context) +@(plugins: List[gitbucket.core.plugin.PluginInfo], info: Option[Any])(implicit context: gitbucket.core.controller.Context) @gitbucket.core.html.main("Plugins"){ @gitbucket.core.admin.html.menu("plugins") { + @gitbucket.core.helper.html.information(info)

Installed plugins

- +
+ +
@if(plugins.size > 0) {